1// Copyright 2019 The ChromiumOS Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package main 6 7import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "os" 13 "path" 14 "path/filepath" 15 "strings" 16 "syscall" 17 "testing" 18) 19 20func TestAddCommonFlags(t *testing.T) { 21 withTestContext(t, func(ctx *testContext) { 22 ctx.cfg.commonFlags = []string{"-someflag"} 23 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 24 ctx.newCommand(gccX86_64, mainCc))) 25 if err := verifyArgOrder(cmd, "-someflag", mainCc); err != nil { 26 t.Error(err) 27 } 28 }) 29} 30 31func TestAddGccConfigFlags(t *testing.T) { 32 withTestContext(t, func(ctx *testContext) { 33 ctx.cfg.gccFlags = []string{"-someflag"} 34 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 35 ctx.newCommand(gccX86_64, mainCc))) 36 if err := verifyArgOrder(cmd, "-someflag", mainCc); err != nil { 37 t.Error(err) 38 } 39 }) 40} 41 42func TestAddClangConfigFlags(t *testing.T) { 43 withTestContext(t, func(ctx *testContext) { 44 ctx.cfg.clangFlags = []string{"-someflag"} 45 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 46 ctx.newCommand(clangX86_64, mainCc))) 47 if err := verifyArgOrder(cmd, "-someflag", mainCc); err != nil { 48 t.Error(err) 49 } 50 }) 51} 52 53func TestLogGeneralExecError(t *testing.T) { 54 withTestContext(t, func(ctx *testContext) { 55 ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 56 return errors.New("someerror") 57 } 58 stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))) 59 if err := verifyInternalError(stderr); err != nil { 60 t.Fatal(err) 61 } 62 if !strings.Contains(stderr, gccX86_64) { 63 t.Errorf("could not find compiler path on stderr. Got: %s", stderr) 64 } 65 if !strings.Contains(stderr, "someerror") { 66 t.Errorf("could not find original error on stderr. Got: %s", stderr) 67 } 68 }) 69} 70 71func TestForwardStdin(t *testing.T) { 72 withTestContext(t, func(ctx *testContext) { 73 io.WriteString(&ctx.stdinBuffer, "someinput") 74 ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 75 stdinStr := ctx.readAllString(stdin) 76 if stdinStr != "someinput" { 77 return fmt.Errorf("unexpected stdin. Got: %s", stdinStr) 78 } 79 return nil 80 } 81 ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, "-", mainCc))) 82 }) 83} 84 85func TestLogMissingCCacheExecError(t *testing.T) { 86 withTestContext(t, func(ctx *testContext) { 87 ctx.cfg.useCCache = true 88 89 ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 90 return syscall.ENOENT 91 } 92 ctx.stderrBuffer.Reset() 93 stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))) 94 if err := verifyNonInternalError(stderr, "ccache not found under .*. Please install it"); err != nil { 95 t.Fatal(err) 96 } 97 }) 98} 99 100func TestGomaDisablesRusage(t *testing.T) { 101 withTestContext(t, func(ctx *testContext) { 102 gomaPath := path.Join(ctx.tempDir, "gomacc") 103 ctx.writeFile(gomaPath, "") 104 ctx.env = []string{"GOMACC_PATH=" + gomaPath} 105 logFileName := filepath.Join(ctx.tempDir, "rusage.log") 106 ctx.env = []string{ 107 "TOOLCHAIN_RUSAGE_OUTPUT=" + logFileName, 108 "GOMACC_PATH=" + gomaPath, 109 } 110 cmd := ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))) 111 // Ensure Goma was used 112 if err := verifyPath(cmd, gomaPath); err != nil { 113 t.Fatal(err) 114 } 115 if err := verifyArgOrder(cmd, gccX86_64+".real", mainCc); err != nil { 116 t.Error(err) 117 } 118 // Ensure rusage log was not created 119 if _, err := os.Stat(logFileName); err == nil { 120 t.Errorf("Logfile shouldn't have been created at TOOLCHAIN_RUSAGE_OUTPUT path %q but was", logFileName) 121 } else if !os.IsNotExist(err) { 122 t.Fatalf("error checking for rusage logfile at %q: %v", logFileName, err) 123 } 124 }) 125} 126 127func TestLogRusageAndForceDisableWError(t *testing.T) { 128 withTestContext(t, func(ctx *testContext) { 129 ctx.NoteTestWritesToUmask() 130 131 logFileName := filepath.Join(ctx.tempDir, "rusage.log") 132 ctx.env = []string{ 133 "FORCE_DISABLE_WERROR=1", 134 "TOOLCHAIN_RUSAGE_OUTPUT=" + logFileName, 135 } 136 ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 137 switch ctx.cmdCount { 138 case 1: 139 io.WriteString(stderr, arbitraryWerrorStderr) 140 return newExitCodeError(1) 141 case 2: 142 return nil 143 default: 144 t.Fatalf("unexpected command: %#v", cmd) 145 return nil 146 } 147 } 148 ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc))) 149 if _, err := os.Stat(logFileName); os.IsNotExist(err) { 150 t.Errorf("no logfile created at TOOLCHAIN_RUSAGE_OUTPUT path %q", logFileName) 151 } else if err != nil { 152 t.Fatalf("error checking for rusage logfile at %q: %v", logFileName, err) 153 } 154 if ctx.cmdCount != 2 { 155 t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount) 156 } 157 }) 158} 159 160func TestErrorOnLogRusageAndBisect(t *testing.T) { 161 withTestContext(t, func(ctx *testContext) { 162 ctx.NoteTestWritesToUmask() 163 164 ctx.env = []string{ 165 "BISECT_STAGE=xyz", 166 "TOOLCHAIN_RUSAGE_OUTPUT=rusage.log", 167 } 168 stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))) 169 if err := verifyNonInternalError(stderr, "TOOLCHAIN_RUSAGE_OUTPUT is meaningless with BISECT_STAGE"); err != nil { 170 t.Error(err) 171 } 172 }) 173} 174 175func TestErrorOnBisectAndForceDisableWError(t *testing.T) { 176 withTestContext(t, func(ctx *testContext) { 177 ctx.NoteTestWritesToUmask() 178 179 ctx.env = []string{ 180 "BISECT_STAGE=xyz", 181 "FORCE_DISABLE_WERROR=1", 182 } 183 stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc))) 184 if err := verifyNonInternalError(stderr, "BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR"); err != nil { 185 t.Error(err) 186 } 187 }) 188} 189 190func TestPrintUserCompilerError(t *testing.T) { 191 buffer := bytes.Buffer{} 192 printCompilerError(&buffer, newUserErrorf("abcd")) 193 if buffer.String() != "abcd\n" { 194 t.Errorf("Unexpected string. Got: %s", buffer.String()) 195 } 196} 197 198func TestPrintOtherCompilerError(t *testing.T) { 199 buffer := bytes.Buffer{} 200 printCompilerError(&buffer, errors.New("abcd")) 201 if buffer.String() != "Internal error. Please report to [email protected].\nabcd\n" { 202 t.Errorf("Unexpected string. Got: %s", buffer.String()) 203 } 204} 205 206func TestPrintOtherCompilerErrorForAndroidLLVM(t *testing.T) { 207 buffer := bytes.Buffer{} 208 209 oldConfigName := ConfigName 210 defer func() { ConfigName = oldConfigName }() 211 212 ConfigName = "android" 213 printCompilerError(&buffer, errors.New("abcd")) 214 if buffer.String() != "Internal error. Please report to [email protected].\nabcd\n" { 215 t.Errorf("Unexpected string. Got: %s", buffer.String()) 216 } 217} 218 219func TestCalculateAndroidWrapperPath(t *testing.T) { 220 t.Parallel() 221 222 testCases := []struct { 223 mainBuilderPath string 224 absWrapperPath string 225 want string 226 }{ 227 { 228 mainBuilderPath: "/foo/bar", 229 absWrapperPath: "/bar/baz", 230 want: "/foo/baz.real", 231 }, 232 { 233 mainBuilderPath: "/my_wrapper", 234 absWrapperPath: "/bar/baz", 235 want: "/baz.real", 236 }, 237 { 238 mainBuilderPath: "no_seps", 239 absWrapperPath: "/bar/baz", 240 want: "baz.real", 241 }, 242 { 243 mainBuilderPath: "./a_sep", 244 absWrapperPath: "/bar/baz", 245 want: "./baz.real", 246 }, 247 } 248 249 for _, tc := range testCases { 250 if result := calculateAndroidWrapperPath(tc.mainBuilderPath, tc.absWrapperPath); result != tc.want { 251 t.Errorf("Failed calculating the wrapper path with (%q, %q); got %q, want %q", tc.mainBuilderPath, tc.absWrapperPath, result, tc.want) 252 } 253 } 254} 255 256// If "crash-diagnostics-dir" flag is not provided, add one in 257func TestCrashDiagDefault(t *testing.T) { 258 withTestContext(t, func(ctx *testContext) { 259 ctx.env = []string{ 260 "CROS_ARTIFACTS_TMP_DIR=/tmp/foo", 261 } 262 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 263 ctx.newCommand(clangX86_64, mainCc))) 264 265 // Verify that we added the default flag 266 if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/tmp/foo/toolchain/clang_crash_diagnostics"); err != nil { 267 t.Error(err) 268 } 269 }) 270} 271 272// If "crash-diagnostics-dir" flag is already provided by the user, only use that flag and don't add a dupe 273func TestCrashDiagUserFlag(t *testing.T) { 274 withTestContext(t, func(ctx *testContext) { 275 ctx.env = []string{ 276 "CROS_ARTIFACTS_TMP_DIR=/tmp/foo", 277 } 278 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 279 ctx.newCommand(clangX86_64, mainCc, "-fcrash-diagnostics-dir=/build/something/foozz"))) 280 281 // Verify that user flag is not removed 282 if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/build/something/foozz"); err != nil { 283 t.Error(err) 284 } 285 286 // Verify that we did not add the default flag 287 if err := verifyArgCount(cmd, 0, "-fcrash-diagnostics-dir=/tmp/foo/toolchain/clang_crash_diagnostics"); err != nil { 288 t.Error(err) 289 } 290 }) 291} 292