1// Copyright 2018 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 main 16 17import ( 18 "fmt" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "testing" 23 24 "android/soong/ui/build/paths" 25) 26 27var tmpDir string 28var origPATH string 29 30func TestMain(m *testing.M) { 31 os.Exit(func() int { 32 var err error 33 tmpDir, err = ioutil.TempDir("", "interposer_test") 34 if err != nil { 35 panic(err) 36 } 37 defer os.RemoveAll(tmpDir) 38 39 origPATH = os.Getenv("PATH") 40 err = os.Setenv("PATH", "") 41 if err != nil { 42 panic(err) 43 } 44 45 return m.Run() 46 }()) 47} 48 49func setup(t *testing.T) string { 50 f, err := ioutil.TempFile(tmpDir, "interposer") 51 if err != nil { 52 t.Fatal(err) 53 } 54 defer f.Close() 55 56 err = ioutil.WriteFile(f.Name()+"_origpath", []byte(origPATH), 0666) 57 if err != nil { 58 t.Fatal(err) 59 } 60 return f.Name() 61} 62 63func TestInterposer(t *testing.T) { 64 interposer := setup(t) 65 66 logConfig := func(name string) paths.PathConfig { 67 if name == "true" { 68 return paths.PathConfig{ 69 Log: false, 70 Error: false, 71 } 72 } else if name == "path_interposer_test_not_allowed" { 73 return paths.PathConfig{ 74 Log: false, 75 Error: true, 76 } 77 } 78 return paths.PathConfig{ 79 Log: true, 80 Error: false, 81 } 82 } 83 84 testCases := []struct { 85 name string 86 args []string 87 88 exitCode int 89 err error 90 logEntry string 91 }{ 92 { 93 name: "direct call", 94 args: []string{interposer}, 95 96 exitCode: 1, 97 err: usage, 98 }, 99 { 100 name: "relative call", 101 args: []string{filepath.Base(interposer)}, 102 103 exitCode: 1, 104 err: usage, 105 }, 106 { 107 name: "true", 108 args: []string{"/my/path/true"}, 109 }, 110 { 111 name: "relative true", 112 args: []string{"true"}, 113 }, 114 { 115 name: "exit code", 116 args: []string{"bash", "-c", "exit 42"}, 117 118 exitCode: 42, 119 logEntry: "bash", 120 }, 121 { 122 name: "signal", 123 args: []string{"bash", "-c", "kill -9 $$"}, 124 125 exitCode: 137, 126 logEntry: "bash", 127 }, 128 { 129 name: "does not exist", 130 args: []string{"path_interposer_test_does_not_exist"}, 131 132 exitCode: 1, 133 err: fmt.Errorf(`exec: "path_interposer_test_does_not_exist": executable file not found in $PATH`), 134 logEntry: "path_interposer_test_does_not_exist", 135 }, 136 { 137 name: "not allowed", 138 args: []string{"path_interposer_test_not_allowed"}, 139 140 exitCode: 1, 141 err: fmt.Errorf(`"path_interposer_test_not_allowed" is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.`), 142 logEntry: "path_interposer_test_not_allowed", 143 }, 144 } 145 146 for _, testCase := range testCases { 147 t.Run(testCase.name, func(t *testing.T) { 148 logged := false 149 logFunc := func(logSocket string, entry *paths.LogEntry, done chan interface{}) { 150 defer close(done) 151 152 logged = true 153 if entry.Basename != testCase.logEntry { 154 t.Errorf("unexpected log entry:\nwant: %q\n got: %q", testCase.logEntry, entry.Basename) 155 } 156 } 157 158 exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, testCase.args, mainOpts{ 159 sendLog: logFunc, 160 config: logConfig, 161 }) 162 163 errstr := func(err error) string { 164 if err == nil { 165 return "" 166 } 167 return err.Error() 168 } 169 if errstr(testCase.err) != errstr(err) { 170 t.Errorf("unexpected error:\nwant: %v\n got: %v", testCase.err, err) 171 } 172 if testCase.exitCode != exitCode { 173 t.Errorf("expected exit code %d, got %d", testCase.exitCode, exitCode) 174 } 175 if !logged && testCase.logEntry != "" { 176 t.Errorf("no log entry, but expected %q", testCase.logEntry) 177 } 178 }) 179 } 180} 181 182func TestMissingPath(t *testing.T) { 183 interposer := setup(t) 184 err := os.Remove(interposer + "_origpath") 185 if err != nil { 186 t.Fatal("Failed to remove:", err) 187 } 188 189 exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, []string{"true"}, mainOpts{}) 190 if err != usage { 191 t.Errorf("Unexpected error:\n got: %v\nwant: %v", err, usage) 192 } 193 if exitCode != 1 { 194 t.Errorf("expected exit code %d, got %d", 1, exitCode) 195 } 196} 197