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 import sumtype; 19 import my.path : Path; 20 21 import dextool.type : DextoolVersion; 22 23 import cpptooling.data : CppClass, FullyQualifiedNameType, TypeKindVariable; 24 import cpptooling.data.symbol : Container; 25 import cpptooling.type : CustomHeader; 26 27 // argument names used in the generated code 28 private immutable argValue = "x0"; 29 private immutable argStream = "os"; 30 31 auto generateGtestHdr(Path if_file, Path incl_guard, DextoolVersion ver, 32 CustomHeader custom_hdr, CppModule gtest) { 33 import std.path : baseName; 34 import dsrcgen.cpp : CppHModule; 35 import cpptooling.generator.includes : convToIncludeGuard, makeHeader; 36 37 auto o = CppHModule(convToIncludeGuard(incl_guard)); 38 o.header.append(makeHeader(incl_guard, ver, custom_hdr)); 39 o.content.include(if_file.baseName); 40 o.content.include("gtest/gtest.h"); 41 o.content.sep(2); 42 o.content.append(gtest); 43 44 return o; 45 } 46 47 /** Generate a compare operator for use with EXPECT_EQ in gtest. 48 * 49 * Optimized compares using == for primitive types except floats. 50 * Google tests internal helper for all others. 51 */ 52 void generateGtestPrettyEqual(T)(T members, FullyQualifiedNameType name, 53 string guard_prefix, ref Container container, CppModule m) { 54 import std.algorithm : map, among; 55 import std.ascii : isAlphaNum; 56 import std.conv : to; 57 import std.format : format; 58 import std..string : toUpper; 59 import logger = std.experimental.logger; 60 61 import cpptooling.data.kind : resolveTypeRef; 62 import cpptooling.data : TypeKind, USRType, TypeAttr, isIncompleteArray; 63 64 auto findType(USRType a) { 65 return container.find!TypeKind(a); 66 } 67 68 static void fieldCompare(string field, TypeKind canonical_t, CppModule code) { 69 canonical_t.info.match!((TypeKind.PrimitiveInfo t) { 70 // reuse google tests internal helper for floating points because it does an ULP*4 71 if (t.fmt.typeId.among("float", "double", "long double")) { 72 // long double do not work with the template thus reducing to a double 73 code.stmt(format( 74 `acc = acc && ::testing::internal::CmpHelperFloatingPointEQ<%s>("", "", lhs.%s, rhs.%s)`, 75 t.fmt.typeId == "long double" ? "double" : t.fmt.typeId, field, field)); 76 } else { 77 code.stmt(E("acc") = E("acc && " ~ format("lhs.%s == rhs.%s", field, field))); 78 } 79 }, (_) { 80 code.stmt(format(`acc = acc && ::testing::internal::CmpHelperEQ("", "", lhs.%s, rhs.%s)`, 81 field, field)); 82 }); 83 } 84 85 auto ifndef = m.IFNDEF(format("%s_NO_CMP_%s", guard_prefix.toUpper, 86 name.map!(a => a.isAlphaNum ? a : '_') 87 .map!(a => a.to!char))); 88 89 auto func = ifndef.func_body("inline bool", "operator==", 90 format("const %s& lhs, const %s& rhs", name, name)); 91 92 func.stmt("bool acc = true"); 93 94 foreach (mem; members) { 95 TypeKind kind = mem.type.kind; 96 auto canonical_t = resolveTypeRef(kind, &findType); 97 98 // a constant array compares element vise. For now can only handle one dimensional arrays 99 canonical_t.info.match!((TypeKind.ArrayInfo t) { 100 if (!isIncompleteArray(t.indexes) && t.indexes.length == 1) { 101 auto elem_t = findType(t.element).front; 102 auto canonical_elem_t = resolveTypeRef(elem_t, &findType); 103 auto loop = func.for_("unsigned int dextool_i = 0", 104 "dextool_i < " ~ t.indexes[0].to!string, "++dextool_i"); 105 fieldCompare(mem.name ~ "[dextool_i]", canonical_elem_t, loop); 106 with (loop.if_("!acc")) 107 return_("false"); 108 } else { 109 fieldCompare(mem.name, canonical_t, func); 110 } 111 }, (_) { fieldCompare(mem.name, canonical_t, func); }); 112 } 113 114 func.return_("acc"); 115 m.sep(2); 116 } 117 118 /** Generate Google Test pretty printers of a PODs public members. 119 * 120 * Params: 121 * src = POD to generate the pretty printer for. 122 * m = module to generate code in. 123 */ 124 void generateGtestPrettyPrintHdr(FullyQualifiedNameType name, CppModule m) { 125 import std.format : format; 126 127 m.func("void", "PrintTo", format("const %s& %s, ::std::ostream* %s", name, 128 argValue, argStream)); 129 m.sep(2); 130 } 131 132 /** Generate Google Test pretty printers of a PODs public members. 133 * 134 * This mean that the actual values are printed instead of the byte 135 * representation. 136 * 137 * Params: 138 * members = range of the members to pretty print 139 * name = fqn name of the type that have the members 140 * m = module to generate code in. 141 */ 142 void generateGtestPrettyPrintImpl(T)(T members, FullyQualifiedNameType name, CppModule m) 143 if (isInputRange!T) { 144 import std.algorithm; 145 import std.format : format; 146 147 auto func = m.func_body("void", "PrintTo", 148 format("const %s& %s, ::std::ostream* %s", name, argValue, argStream)); 149 150 string space = null; 151 foreach (mem; members) { 152 func.stmt(E("*os <<") ~ E(format(`"%s%s:"`, space, 153 mem.name)) ~ E("<<") ~ E("::testing::PrintToString")(E(argValue).E(mem.name))); 154 space = " "; 155 } 156 157 m.sep(2); 158 }