github.com/distbuild/reclient@v0.0.0-20240401075343-3de72e395564/internal/pkg/inputprocessor/action/clanglint/preprocessor_test.go (about) 1 // Copyright 2023 Google LLC 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 clanglint 16 17 import ( 18 "context" 19 "os" 20 "path/filepath" 21 "testing" 22 23 spb "github.com/bazelbuild/reclient/api/scandeps" 24 "github.com/bazelbuild/reclient/internal/pkg/inputprocessor" 25 "github.com/bazelbuild/reclient/internal/pkg/inputprocessor/action/cppcompile" 26 27 "github.com/bazelbuild/remote-apis-sdks/go/pkg/command" 28 "github.com/google/go-cmp/cmp" 29 ) 30 31 const executablePath = "clang-tidy" 32 33 func TestSpec(t *testing.T) { 34 pwd, err := os.Getwd() 35 if err != nil { 36 t.Fatalf("Unable to get current working directory: %v", err) 37 } 38 ctx := context.Background() 39 s := &stubCPPDepScanner{ 40 res: []string{"foo.h"}, 41 err: nil, 42 } 43 p := Preprocessor{ 44 Preprocessor: &cppcompile.Preprocessor{ 45 BasePreprocessor: &inputprocessor.BasePreprocessor{ 46 Ctx: ctx, 47 Executor: &stubExecutor{outStr: ".\nother-version-info\nother-version-info-2"}, 48 }, 49 CPPDepScanner: s, 50 }, 51 } 52 p.Options = inputprocessor.Options{ 53 ExecRoot: pwd, 54 Cmd: []string{executablePath, 55 "-header-filter=\"(packages)\"", 56 "-extra-arg-before=-Xclang", 57 "test.cpp", 58 "--", 59 "-Xclang", 60 "-verify", 61 "-Ifoo", 62 "-I", 63 "bar", 64 "-std=c++14", 65 }} 66 67 if err := p.ParseFlags(); err != nil { 68 t.Fatalf("ParseFlags() failed: %v", err) 69 } 70 p.ComputeSpec() 71 got, err := p.Spec() 72 if err != nil { 73 t.Fatalf("Spec() failed: %v", err) 74 } 75 want := &command.InputSpec{ 76 Inputs: []string{ 77 "test.cpp", 78 "foo.h", 79 }, 80 VirtualInputs: []*command.VirtualInput{ 81 &command.VirtualInput{Path: filepath.Clean("foo"), IsEmptyDirectory: true}, 82 &command.VirtualInput{Path: filepath.Clean("bar"), IsEmptyDirectory: true}, 83 }, 84 } 85 opt := cmp.Transformer("filterOutExecutable", func(in []string) []string { 86 out := []string{} 87 for _, input := range in { 88 if input != executablePath { 89 out = append(out, input) 90 } 91 } 92 return out 93 }) 94 if diff := cmp.Diff(want, got.InputSpec, opt); diff != "" { 95 t.Errorf("Spec() returned diff (-want +got): %v", diff) 96 } 97 wantCmd := []string{ 98 filepath.Join(pwd, executablePath), 99 "-resource-dir", 100 "..", 101 "-Ifoo", 102 "-I", 103 "bar", 104 "-std=c++14", // expect to NOT be normalized (b/215203594) 105 "-Qunused-arguments", 106 filepath.Join(pwd, "test.cpp"), 107 } 108 if diff := cmp.Diff(wantCmd, s.gotCmd, opt); diff != "" { 109 t.Errorf("CPP command from clang-tidy command %v had diff (-want +got): %s", p.Flags, diff) 110 } 111 } 112 113 type stubCPPDepScanner struct { 114 gotCmd []string 115 gotFileName string 116 gotDirectory string 117 118 res []string 119 err error 120 } 121 122 func (s *stubCPPDepScanner) ProcessInputs(_ context.Context, _ string, command []string, filename, directory string, _ []string) ([]string, bool, error) { 123 s.gotCmd = command 124 s.gotFileName = filename 125 s.gotDirectory = directory 126 127 return s.res, false, s.err 128 } 129 130 func (s *stubCPPDepScanner) Capabilities() *spb.CapabilitiesResponse { 131 return nil 132 } 133 134 type stubExecutor struct { 135 gotCmd *command.Command 136 137 outStr string 138 errStr string 139 err error 140 } 141 142 func (s *stubExecutor) Execute(ctx context.Context, cmd *command.Command) (string, string, error) { 143 s.gotCmd = cmd 144 return s.outStr, s.errStr, s.err 145 }