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