| Chih-Hung Hsieh | 104f51f | 2022-04-20 15:48:41 -0700 | [diff] [blame] | 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 |  | 
|  | 15 | package cc | 
|  | 16 |  | 
|  | 17 | import ( | 
|  | 18 | "fmt" | 
| Chih-Hung Hsieh | 80e3e03 | 2022-06-02 19:55:15 -0700 | [diff] [blame] | 19 | "strings" | 
| Chih-Hung Hsieh | 104f51f | 2022-04-20 15:48:41 -0700 | [diff] [blame] | 20 | "testing" | 
|  | 21 |  | 
|  | 22 | "android/soong/android" | 
|  | 23 | ) | 
|  | 24 |  | 
| Chih-Hung Hsieh | 794b81d | 2022-06-11 18:10:58 -0700 | [diff] [blame] | 25 | func 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 |  | 
| Chih-Hung Hsieh | 80e3e03 | 2022-06-02 19:55:15 -0700 | [diff] [blame] | 101 | func 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" | 
| Chih-Hung Hsieh | 43b920e | 2022-06-09 17:58:41 -0700 | [diff] [blame] | 131 | extraGlobalChecks := ",${config.TidyGlobalNoChecks}" | 
| Chih-Hung Hsieh | 80e3e03 | 2022-06-02 19:55:15 -0700 | [diff] [blame] | 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 |  | 
| Chih-Hung Hsieh | 104f51f | 2022-04-20 15:48:41 -0700 | [diff] [blame] | 172 | func 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 | } | 
| JaeMan Park | 3dba4d2 | 2024-01-05 15:29:48 +0900 | [diff] [blame] | 247 |  | 
|  | 248 | func 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 | } |