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 : '_').map!(a => a.to!char))); 85 86 auto func = ifndef.func_body("inline bool", "operator==", 87 format("const %s& lhs, const %s& rhs", name, name)); 88 89 func.stmt("bool acc = true"); 90 91 foreach (mem; members) { 92 TypeKind kind = mem.type.kind; 93 auto canonical_t = resolveTypeRef(kind, &findType); 94 95 // a constant array compares element vise. For now can only handle one dimensional arrays 96 if (canonical_t.info.kind == TypeKind.Info.Kind.array 97 && !isIncompleteArray(canonical_t.info.indexes) 98 && canonical_t.info.indexes.length == 1) { 99 auto elem_t = findType(canonical_t.info.element).front; 100 auto canonical_elem_t = resolveTypeRef(elem_t, &findType); 101 auto loop = func.for_("unsigned int dextool_i = 0", 102 "dextool_i < " ~ canonical_t.info.indexes[0].to!string, "++dextool_i"); 103 fieldCompare(mem.name ~ "[dextool_i]", canonical_elem_t, loop); 104 with (loop.if_("!acc")) 105 return_("false"); 106 } else { 107 fieldCompare(mem.name, canonical_t, func); 108 } 109 } 110 111 func.return_("acc"); 112 m.sep(2); 113 } 114 115 /** Generate Google Test pretty printers of a PODs public members. 116 * 117 * Params: 118 * src = POD to generate the pretty printer for. 119 * m = module to generate code in. 120 */ 121 void generateGtestPrettyPrintHdr(const FullyQualifiedNameType name, CppModule m) { 122 import std.format : format; 123 124 m.func("void", "PrintTo", format("const %s& %s, ::std::ostream* %s", name, 125 argValue, argStream)); 126 m.sep(2); 127 } 128 129 /** Generate Google Test pretty printers of a PODs public members. 130 * 131 * This mean that the actual values are printed instead of the byte 132 * representation. 133 * 134 * Params: 135 * members = range of the members to pretty print 136 * name = fqn name of the type that have the members 137 * m = module to generate code in. 138 */ 139 void generateGtestPrettyPrintImpl(T)(T members, const FullyQualifiedNameType name, CppModule m) 140 if (isInputRange!T) { 141 import std.algorithm; 142 import std.format : format; 143 144 auto func = m.func_body("void", "PrintTo", 145 format("const %s& %s, ::std::ostream* %s", name, argValue, argStream)); 146 147 string space = null; 148 foreach (mem; members) { 149 func.stmt(E("*os <<") ~ E(format(`"%s%s:"`, space, 150 mem.name)) ~ E("<<") ~ E("::testing::PrintToString")(E(argValue).E(mem.name))); 151 space = " "; 152 } 153 154 m.sep(2); 155 }