1// Copyright 2019 The Chromium OS Authors. All rights reserved.
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	"errors"
9	"fmt"
10	"io"
11	"path"
12	"strings"
13	"testing"
14)
15
16func TestCheckClangSyntaxByNestedCall(t *testing.T) {
17	withTestContext(t, func(ctx *testContext) {
18		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
19			if ctx.cmdCount == 1 {
20				if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
21					return err
22				}
23				if err := verifyArgOrder(cmd, mainCc, "-fsyntax-only", `-stdlib=libstdc\+\+`); err != nil {
24					return err
25				}
26			}
27			return nil
28		}
29		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
30			ctx.newCommand(gccX86_64, "-clang-syntax", mainCc)))
31		if ctx.cmdCount != 2 {
32			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
33		}
34		if err := verifyPath(cmd, gccX86_64+".real"); err != nil {
35			t.Error(err)
36		}
37		if err := verifyArgCount(cmd, 0, "-clang-syntax"); err != nil {
38			t.Error(err)
39		}
40	})
41}
42
43func TestForwardStdOutAndStderrFromClangSyntaxCheck(t *testing.T) {
44	withTestContext(t, func(ctx *testContext) {
45		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
46			if ctx.cmdCount == 1 {
47				fmt.Fprint(stdout, "somemessage")
48				fmt.Fprint(stderr, "someerror")
49			}
50			return nil
51		}
52		ctx.must(callCompiler(ctx, ctx.cfg,
53			ctx.newCommand(gccX86_64, "-clang-syntax", mainCc)))
54		if ctx.stdoutString() != "somemessage" {
55			t.Errorf("stdout was not forwarded. Got: %s", ctx.stdoutString())
56		}
57		if ctx.stderrString() != "someerror" {
58			t.Errorf("stderr was not forwarded. Got: %s", ctx.stderrString())
59		}
60	})
61}
62
63func TestForwardStdinToClangSyntaxCheck(t *testing.T) {
64	withTestContext(t, func(ctx *testContext) {
65		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
66			// Note: This is called for the clang syntax call as well as for
67			// the gcc call, and we assert that stdin is cloned and forwarded
68			// to both.
69			stdinStr := ctx.readAllString(stdin)
70			if stdinStr != "someinput" {
71				return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
72			}
73			return nil
74		}
75		io.WriteString(&ctx.stdinBuffer, "someinput")
76		ctx.must(callCompiler(ctx, ctx.cfg,
77			ctx.newCommand(gccX86_64, "-clang-syntax", "-", mainCc)))
78	})
79}
80
81func TestForwardExitCodeFromClangSyntaxCheck(t *testing.T) {
82	withTestContext(t, func(ctx *testContext) {
83		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
84			if ctx.cmdCount == 1 {
85				return newExitCodeError(23)
86			}
87			return nil
88		}
89		exitCode := callCompiler(ctx, ctx.cfg,
90			ctx.newCommand(gccX86_64, "-clang-syntax", mainCc))
91		if exitCode != 23 {
92			t.Errorf("unexpected exit code. Got: %d", exitCode)
93		}
94	})
95}
96
97func TestReportGeneralErrorsFromClangSyntaxCheck(t *testing.T) {
98	withTestContext(t, func(ctx *testContext) {
99		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
100			if ctx.cmdCount == 1 {
101				return errors.New("someerror")
102			}
103			return nil
104		}
105		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg,
106			ctx.newCommand(gccX86_64, "-clang-syntax", mainCc)))
107		if err := verifyInternalError(stderr); err != nil {
108			t.Fatal(err)
109		}
110		if !strings.Contains(stderr, "someerror") {
111			t.Errorf("unexpected error. Got: %s", stderr)
112		}
113	})
114}
115
116func TestIgnoreClangSyntaxCheckWhenCallingClang(t *testing.T) {
117	withTestContext(t, func(ctx *testContext) {
118		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
119			if ctx.cmdCount > 1 {
120				return fmt.Errorf("Unexpected call %#v", cmd)
121			}
122			return nil
123		}
124		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
125			ctx.newCommand(clangX86_64, "-clang-syntax", mainCc)))
126		if err := verifyArgCount(cmd, 0, "-clang-syntax"); err != nil {
127			t.Error(err)
128		}
129	})
130}
131
132func TestUseGomaForClangSyntaxCheck(t *testing.T) {
133	withTestContext(t, func(ctx *testContext) {
134		gomaPath := path.Join(ctx.tempDir, "gomacc")
135		// Create a file so the gomacc path is valid.
136		ctx.writeFile(gomaPath, "")
137		ctx.env = []string{"GOMACC_PATH=" + gomaPath}
138		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
139			if ctx.cmdCount == 1 {
140				if err := verifyPath(cmd, gomaPath); err != nil {
141					return err
142				}
143				if err := verifyArgOrder(cmd, "usr/bin/clang", mainCc); err != nil {
144					return err
145				}
146			}
147			return nil
148		}
149		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
150			ctx.newCommand(gccX86_64, "-clang-syntax", mainCc)))
151		if ctx.cmdCount != 2 {
152			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
153		}
154		if err := verifyPath(cmd, gomaPath); err != nil {
155			t.Error(err)
156		}
157	})
158}
159
160func TestPartiallyOmitCCacheForClangSyntaxCheck(t *testing.T) {
161	withTestContext(t, func(ctx *testContext) {
162		ctx.cfg.useCCache = true
163		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
164			if ctx.cmdCount == 1 {
165				if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
166					return err
167				}
168			}
169			return nil
170		}
171		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
172			ctx.newCommand(gccX86_64, "-clang-syntax", mainCc)))
173		if ctx.cmdCount != 2 {
174			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
175		}
176		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
177			t.Error(err)
178		}
179	})
180}
181