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, isIncompleteArray; 60 61 auto findType(USRType a) { 62 return container.find!TypeKind(a); 63 } 64 65 static void fieldCompare(string field, TypeKind canonical_t, CppModule code) { 66 if (canonical_t.info.kind == TypeKind.Info.Kind.primitive) { 67 auto info = cast(TypeKind.PrimitiveInfo) canonical_t.info; 68 // reuse google tests internal helper for floating points because it does an ULP*4 69 if (info.fmt.typeId.among("float", "double", "long double")) { 70 // long double do not work with the template thus reducing to a double 71 code.stmt(format( 72 `acc = acc && ::testing::internal::CmpHelperFloatingPointEQ<%s>("", "", lhs.%s, rhs.%s)`, 73 info.fmt.typeId == "long double" ? "double" : info.fmt.typeId, field, field)); 74 } else { 75 code.stmt(E("acc") = E("acc && " ~ format("lhs.%s == rhs.%s", field, field))); 76 } 77 } else { 78 code.stmt(format(`acc = acc && ::testing::internal::CmpHelperEQ("", "", lhs.%s, rhs.%s)`, 79 field, field)); 80 } 81 } 82 83 auto ifndef = m.IFNDEF(format("%s_NO_CMP_%s", guard_prefix.toUpper, 84 name.map!(a => a.isAlphaNum ? a : '_') 85 .map!(a => a.to!char))); 86 87 auto func = ifndef.func_body("inline bool", "operator==", 88 format("const %s& lhs, const %s& rhs", name, name)); 89 90 func.stmt("bool acc = true"); 91 92 foreach (mem; members) { 93 TypeKind kind = mem.type.kind; 94 auto canonical_t = resolveTypeRef(kind, &findType); 95 96 // a constant array compares element vise. For now can only handle one dimensional arrays 97 if (canonical_t.info.kind == TypeKind.Info.Kind.array 98 && !isIncompleteArray(canonical_t.info.indexes) 99 && canonical_t.info.indexes.length == 1) { 100 auto elem_t = findType(canonical_t.info.element).front; 101 auto canonical_elem_t = resolveTypeRef(elem_t, &findType); 102 auto loop = func.for_("unsigned int dextool_i = 0", 103 "dextool_i < " ~ canonical_t.info.indexes[0].to!string, "++dextool_i"); 104 fieldCompare(mem.name ~ "[dextool_i]", canonical_elem_t, loop); 105 with (loop.if_("!acc")) 106 return_("false"); 107 } else { 108 fieldCompare(mem.name, canonical_t, func); 109 } 110 } 111 112 func.return_("acc"); 113 m.sep(2); 114 } 115 116 /** Generate Google Test pretty printers of a PODs public members. 117 * 118 * Params: 119 * src = POD to generate the pretty printer for. 120 * m = module to generate code in. 121 */ 122 void generateGtestPrettyPrintHdr(const FullyQualifiedNameType name, CppModule m) { 123 import std.format : format; 124 125 m.func("void", "PrintTo", format("const %s& %s, ::std::ostream* %s", name, 126 argValue, argStream)); 127 m.sep(2); 128 } 129 130 /** Generate Google Test pretty printers of a PODs public members. 131 * 132 * This mean that the actual values are printed instead of the byte 133 * representation. 134 * 135 * Params: 136 * members = range of the members to pretty print 137 * name = fqn name of the type that have the members 138 * m = module to generate code in. 139 */ 140 void generateGtestPrettyPrintImpl(T)(T members, const FullyQualifiedNameType name, CppModule m) 141 if (isInputRange!T) { 142 import std.algorithm; 143 import std.format : format; 144 145 auto func = m.func_body("void", "PrintTo", 146 format("const %s& %s, ::std::ostream* %s", name, argValue, argStream)); 147 148 string space = null; 149 foreach (mem; members) { 150 func.stmt(E("*os <<") ~ E(format(`"%s%s:"`, space, 151 mem.name)) ~ E("<<") ~ E("::testing::PrintToString")(E(argValue).E(mem.name))); 152 space = " "; 153 } 154 155 m.sep(2); 156 }