1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 Test cases in
11 See: test.component.generator
12 */
13 module cpptooling.generator.gtest;
14 
15 import std.range : isInputRange;
16 
17 import dsrcgen.cpp : CppModule, E;
18 
19 import dextool.type : FileName, DextoolVersion, CustomHeader;
20 
21 import cpptooling.data : CppClass, FullyQualifiedNameType, TypeKindVariable;
22 import cpptooling.data.symbol : Container;
23 
24 // argument names used in the generated code
25 private immutable argValue = "x0";
26 private immutable argStream = "os";
27 
28 auto generateGtestHdr(FileName if_file, FileName incl_guard, DextoolVersion ver,
29         CustomHeader custom_hdr, CppModule gtest) {
30     import std.path : baseName;
31     import dsrcgen.cpp : CppHModule;
32     import cpptooling.generator.includes : convToIncludeGuard, makeHeader;
33 
34     auto o = CppHModule(convToIncludeGuard(incl_guard));
35     o.header.append(makeHeader(incl_guard, ver, custom_hdr));
36     o.content.include(if_file.baseName);
37     o.content.include("gtest/gtest.h");
38     o.content.sep(2);
39     o.content.append(gtest);
40 
41     return o;
42 }
43 
44 /** Generate a compare operator for use with EXPECT_EQ in gtest.
45  *
46  * Optimized compares using == for primitive types except floats.
47  * Google tests internal helper for all others.
48  */
49 void generateGtestPrettyEqual(T)(T members, const FullyQualifiedNameType name,
50         string guard_prefix, ref const Container container, CppModule m) {
51     import std.algorithm : map, among;
52     import std.ascii : isAlphaNum;
53     import std.conv : to;
54     import std.format : format;
55     import std..string : toUpper;
56     import logger = std.experimental.logger;
57 
58     import cpptooling.data.kind : resolveTypeRef;
59     import cpptooling.data : TypeKind, USRType, TypeAttr;
60 
61     auto findType(USRType a) {
62         return container.find!TypeKind(a);
63     }
64 
65     auto ifndef = m.IFNDEF(format("%s_NO_CMP_%s", guard_prefix.toUpper,
66             name.map!(a => a.isAlphaNum ? a : '_').map!(a => a.to!char)));
67 
68     auto func = ifndef.func_body("inline bool", "operator==",
69             format("const %s& lhs, const %s& rhs", name, name));
70 
71     func.stmt("bool acc = true");
72 
73     foreach (mem; members) {
74         TypeKind kind = mem.type.kind;
75         auto canonical_t = resolveTypeRef(kind, &findType);
76 
77         if (canonical_t.info.kind == TypeKind.Info.Kind.primitive) {
78             auto info = cast(TypeKind.PrimitiveInfo) canonical_t.info;
79             if (info.fmt.typeId.among("float", "double", "long double")) {
80                 // reuse google tests internal helper for floating points because it does an ULP*4
81                 func.stmt(format(`acc = acc && ::testing::internal::CmpHelperEQ("", "", lhs.%s, rhs.%s)`,
82                         mem.name, mem.name));
83             } else {
84                 func.stmt(E("acc") = E("acc && " ~ format("lhs.%s == rhs.%s", mem.name, mem.name)));
85             }
86         } else {
87             func.stmt(format(`acc = acc && ::testing::internal::CmpHelperEQ("", "", lhs.%s, rhs.%s)`,
88                     mem.name, mem.name));
89         }
90     }
91 
92     func.return_("acc");
93     m.sep(2);
94 }
95 
96 /** Generate Google Test pretty printers of a PODs public members.
97  *
98  * Params:
99  *  src = POD to generate the pretty printer for.
100  *  m = module to generate code in.
101  */
102 void generateGtestPrettyPrintHdr(const FullyQualifiedNameType name, CppModule m) {
103     import std.format : format;
104 
105     m.func("void", "PrintTo", format("const %s& %s, ::std::ostream* %s", name,
106             argValue, argStream));
107     m.sep(2);
108 }
109 
110 /** Generate Google Test pretty printers of a PODs public members.
111  *
112  * This mean that the actual values are printed instead of the byte
113  * representation.
114  *
115  * Params:
116  *  members = range of the members to pretty print
117  *  name = fqn name of the type that have the members
118  *  m = module to generate code in.
119  */
120 void generateGtestPrettyPrintImpl(T)(T members, const FullyQualifiedNameType name, CppModule m)
121         if (isInputRange!T) {
122     import std.algorithm;
123     import std.format : format;
124 
125     auto func = m.func_body("void", "PrintTo",
126             format("const %s& %s, ::std::ostream* %s", name, argValue, argStream));
127 
128     string space = null;
129     foreach (mem; members) {
130         func.stmt(E("*os <<") ~ E(format(`"%s%s:"`, space,
131                 mem.name)) ~ E("<<") ~ E("::testing::PrintToString")(E(argValue).E(mem.name)));
132         space = " ";
133     }
134 
135     m.sep(2);
136 }