1// Copyright 2022 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cc 16 17import ( 18 "fmt" 19 "strings" 20 "testing" 21 22 "android/soong/android" 23) 24 25func TestTidyFlagsWarningsAsErrors(t *testing.T) { 26 // The "tidy_flags" property should not contain -warnings-as-errors. 27 type testCase struct { 28 libName, bp string 29 errorMsg string // a negative test; must have error message 30 flags []string // must have substrings in tidyFlags 31 noFlags []string // must not have substrings in tidyFlags 32 } 33 34 testCases := []testCase{ 35 { 36 "libfoo1", 37 `cc_library_shared { // no warnings-as-errors, good tidy_flags 38 name: "libfoo1", 39 srcs: ["foo.c"], 40 tidy_flags: ["-header-filter=dir1/"], 41 }`, 42 "", 43 []string{"-header-filter=dir1/"}, 44 []string{"-warnings-as-errors"}, 45 }, 46 { 47 "libfoo2", 48 `cc_library_shared { // good use of tidy_checks_as_errors 49 name: "libfoo2", 50 srcs: ["foo.c"], 51 tidy_checks_as_errors: ["xyz-*", "abc"], 52 }`, 53 "", 54 []string{ 55 "-header-filter=^", // there is a default header filter 56 "-warnings-as-errors='xyz-*',abc,${config.TidyGlobalNoErrorChecks}", 57 }, 58 []string{}, 59 }, 60 } 61 if NoWarningsAsErrorsInTidyFlags { 62 testCases = append(testCases, testCase{ 63 "libfoo3", 64 `cc_library_shared { // bad use of -warnings-as-errors in tidy_flags 65 name: "libfoo3", 66 srcs: ["foo.c"], 67 tidy_flags: [ 68 "-header-filters=.*", 69 "-warnings-as-errors=xyz-*", 70 ], 71 }`, 72 `module "libfoo3" .*: tidy_flags: should not contain .*;` + 73 ` use tidy_checks_as_errors instead`, 74 []string{}, 75 []string{}, 76 }) 77 } 78 for _, test := range testCases { 79 if test.errorMsg != "" { 80 testCcError(t, test.errorMsg, test.bp) 81 continue 82 } 83 variant := "android_arm64_armv8-a_shared" 84 ctx := testCc(t, test.bp) 85 t.Run("caseTidyFlags", func(t *testing.T) { 86 flags := ctx.ModuleForTests(test.libName, variant).Rule("clangTidy").Args["tidyFlags"] 87 for _, flag := range test.flags { 88 if !strings.Contains(flags, flag) { 89 t.Errorf("tidyFlags %v for %s does not contain %s.", flags, test.libName, flag) 90 } 91 } 92 for _, flag := range test.noFlags { 93 if strings.Contains(flags, flag) { 94 t.Errorf("tidyFlags %v for %s should not contain %s.", flags, test.libName, flag) 95 } 96 } 97 }) 98 } 99} 100 101func TestTidyChecks(t *testing.T) { 102 // The "tidy_checks" property defines additional checks appended 103 // to global default. But there are some checks disabled after 104 // the local tidy_checks. 105 bp := ` 106 cc_library_shared { // has global checks + extraGlobalChecks 107 name: "libfoo_1", 108 srcs: ["foo.c"], 109 } 110 cc_library_shared { // has only local checks + extraGlobalChecks 111 name: "libfoo_2", 112 srcs: ["foo.c"], 113 tidy_checks: ["-*", "xyz-*"], 114 } 115 cc_library_shared { // has global checks + local checks + extraGlobalChecks 116 name: "libfoo_3", 117 srcs: ["foo.c"], 118 tidy_checks: ["-abc*", "xyz-*", "mycheck"], 119 } 120 cc_library_shared { // has only local checks after "-*" + extraGlobalChecks 121 name: "libfoo_4", 122 srcs: ["foo.c"], 123 tidy_checks: ["-abc*", "xyz-*", "mycheck", "-*", "xyz-*"], 124 }` 125 ctx := testCc(t, bp) 126 127 globalChecks := "-checks=${config.TidyDefaultGlobalChecks}," 128 firstXyzChecks := "-checks='-*','xyz-*'," 129 localXyzChecks := "'-*','xyz-*'" 130 localAbcChecks := "'-abc*','xyz-*',mycheck" 131 extraGlobalChecks := ",${config.TidyGlobalNoChecks}" 132 testCases := []struct { 133 libNumber int // 1,2,3,... 134 checks []string // must have substrings in -checks 135 noChecks []string // must not have substrings in -checks 136 }{ 137 {1, []string{globalChecks, extraGlobalChecks}, []string{localXyzChecks, localAbcChecks}}, 138 {2, []string{firstXyzChecks, extraGlobalChecks}, []string{globalChecks, localAbcChecks}}, 139 {3, []string{globalChecks, localAbcChecks, extraGlobalChecks}, []string{localXyzChecks}}, 140 {4, []string{firstXyzChecks, extraGlobalChecks}, []string{globalChecks, localAbcChecks}}, 141 } 142 t.Run("caseTidyChecks", func(t *testing.T) { 143 variant := "android_arm64_armv8-a_shared" 144 for _, test := range testCases { 145 libName := fmt.Sprintf("libfoo_%d", test.libNumber) 146 flags := ctx.ModuleForTests(libName, variant).Rule("clangTidy").Args["tidyFlags"] 147 splitFlags := strings.Split(flags, " ") 148 foundCheckFlag := false 149 for _, flag := range splitFlags { 150 if strings.HasPrefix(flag, "-checks=") { 151 foundCheckFlag = true 152 for _, check := range test.checks { 153 if !strings.Contains(flag, check) { 154 t.Errorf("tidyFlags for %s does not contain %s.", libName, check) 155 } 156 } 157 for _, check := range test.noChecks { 158 if strings.Contains(flag, check) { 159 t.Errorf("tidyFlags for %s should not contain %s.", libName, check) 160 } 161 } 162 break 163 } 164 } 165 if !foundCheckFlag { 166 t.Errorf("tidyFlags for %s does not contain -checks=.", libName) 167 } 168 } 169 }) 170} 171 172func TestWithTidy(t *testing.T) { 173 // When WITH_TIDY=1 or (ALLOW_LOCAL_TIDY_TRUE=1 and local tidy:true) 174 // a C++ library should depend on .tidy files. 175 testCases := []struct { 176 withTidy, allowLocalTidyTrue string // "_" means undefined 177 needTidyFile []bool // for {libfoo_0, libfoo_1} and {libbar_0, libbar_1} 178 }{ 179 {"_", "_", []bool{false, false, false}}, 180 {"_", "0", []bool{false, false, false}}, 181 {"_", "1", []bool{false, true, false}}, 182 {"_", "true", []bool{false, true, false}}, 183 {"0", "_", []bool{false, false, false}}, 184 {"0", "1", []bool{false, true, false}}, 185 {"1", "_", []bool{true, true, false}}, 186 {"1", "false", []bool{true, true, false}}, 187 {"1", "1", []bool{true, true, false}}, 188 {"true", "_", []bool{true, true, false}}, 189 } 190 bp := ` 191 cc_library_shared { 192 name: "libfoo_0", // depends on .tidy if WITH_TIDY=1 193 srcs: ["foo.c"], 194 } 195 cc_library_shared { // depends on .tidy if WITH_TIDY=1 or ALLOW_LOCAL_TIDY_TRUE=1 196 name: "libfoo_1", 197 srcs: ["foo.c"], 198 tidy: true, 199 } 200 cc_library_shared { // no .tidy 201 name: "libfoo_2", 202 srcs: ["foo.c"], 203 tidy: false, 204 } 205 cc_library_static { 206 name: "libbar_0", // depends on .tidy if WITH_TIDY=1 207 srcs: ["bar.c"], 208 } 209 cc_library_static { // depends on .tidy if WITH_TIDY=1 or ALLOW_LOCAL_TIDY_TRUE=1 210 name: "libbar_1", 211 srcs: ["bar.c"], 212 tidy: true, 213 } 214 cc_library_static { // no .tidy 215 name: "libbar_2", 216 srcs: ["bar.c"], 217 tidy: false, 218 }` 219 for index, test := range testCases { 220 testName := fmt.Sprintf("case%d,%v,%v", index, test.withTidy, test.allowLocalTidyTrue) 221 t.Run(testName, func(t *testing.T) { 222 testEnv := map[string]string{} 223 if test.withTidy != "_" { 224 testEnv["WITH_TIDY"] = test.withTidy 225 } 226 if test.allowLocalTidyTrue != "_" { 227 testEnv["ALLOW_LOCAL_TIDY_TRUE"] = test.allowLocalTidyTrue 228 } 229 ctx := android.GroupFixturePreparers(prepareForCcTest, android.FixtureMergeEnv(testEnv)).RunTestWithBp(t, bp) 230 for n := 0; n < 3; n++ { 231 checkLibraryRule := func(foo, variant, ruleName string) { 232 libName := fmt.Sprintf("lib%s_%d", foo, n) 233 tidyFile := "out/soong/.intermediates/" + libName + "/" + variant + "/obj/" + foo + ".tidy" 234 depFiles := ctx.ModuleForTests(libName, variant).Rule(ruleName).Validations.Strings() 235 if test.needTidyFile[n] { 236 android.AssertStringListContains(t, libName+" needs .tidy file", depFiles, tidyFile) 237 } else { 238 android.AssertStringListDoesNotContain(t, libName+" does not need .tidy file", depFiles, tidyFile) 239 } 240 } 241 checkLibraryRule("foo", "android_arm64_armv8-a_shared", "ld") 242 checkLibraryRule("bar", "android_arm64_armv8-a_static", "ar") 243 } 244 }) 245 } 246} 247 248func TestWithGeneratedCode(t *testing.T) { 249 bp := ` 250 cc_library_shared { 251 name: "libfoo", 252 srcs: ["foo_1.y", "foo_2.yy", "foo_3.l", "foo_4.ll", "foo_5.proto", 253 "foo_6.aidl", "foo_7.rscript", "foo_8.fs", "foo_9.sysprop", 254 "foo_src.cpp"], 255 tidy: true, 256 }` 257 variant := "android_arm64_armv8-a_shared" 258 259 testEnv := map[string]string{} 260 testEnv["ALLOW_LOCAL_TIDY_TRUE"] = "1" 261 262 ctx := android.GroupFixturePreparers(prepareForCcTest, android.FixtureMergeEnv(testEnv)).RunTestWithBp(t, bp) 263 264 t.Run("tidy should be only run for source code, not for generated code", func(t *testing.T) { 265 depFiles := ctx.ModuleForTests("libfoo", variant).Rule("ld").Validations.Strings() 266 267 tidyFileForCpp := "out/soong/.intermediates/libfoo/" + variant + "/obj/foo_src.tidy" 268 269 android.AssertArrayString(t, 270 "only one .tidy file for source code should exist for libfoo", 271 []string{tidyFileForCpp}, depFiles) 272 }) 273} 274