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 }