blob: 115c3da07c067166e65f95625c2a5301b65315af [file] [log] [blame]
David Benjaminaf18cdd2016-04-23 01:40:03 -04001// Copyright (c) 2016, Google Inc.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
David Benjamin0d1b0962016-08-01 09:50:57 -040013// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
David Benjaminaf18cdd2016-04-23 01:40:03 -040014
David Benjaminf9459522016-03-07 15:30:26 -050015package main
16
17import (
David Benjamined9c8fc2016-06-08 09:40:32 -040018 "bytes"
David Benjaminf9459522016-03-07 15:30:26 -050019 "encoding/json"
20 "flag"
21 "fmt"
22 "io"
23 "io/ioutil"
24 "os"
25 "os/exec"
26 "path/filepath"
David Benjamined9c8fc2016-06-08 09:40:32 -040027 "strconv"
David Benjaminf9459522016-03-07 15:30:26 -050028 "strings"
29)
30
31var (
32 buildDir = flag.String("build-dir", "build", "Specifies the build directory to push.")
David Benjamin00b10692016-05-19 00:13:22 -040033 adbPath = flag.String("adb", "adb", "Specifies the adb binary to use. Defaults to looking in PATH.")
David Benjaminf9459522016-03-07 15:30:26 -050034 device = flag.String("device", "", "Specifies the device or emulator. See adb's -s argument.")
35 aarch64 = flag.Bool("aarch64", false, "Build the test runners for aarch64 instead of arm.")
36 arm = flag.Int("arm", 7, "Which arm revision to build for.")
David Benjamin8de8b3d2016-05-12 23:07:47 -040037 suite = flag.String("suite", "all", "Specifies the test suites to run (all, unit, or ssl).")
David Benjaminf9459522016-03-07 15:30:26 -050038 allTestsArgs = flag.String("all-tests-args", "", "Specifies space-separated arguments to pass to all_tests.go")
39 runnerArgs = flag.String("runner-args", "", "Specifies space-separated arguments to pass to ssl/test/runner")
David Benjamin8de8b3d2016-05-12 23:07:47 -040040 jsonOutput = flag.String("json-output", "", "The file to output JSON results to.")
David Benjaminf9459522016-03-07 15:30:26 -050041)
42
David Benjamin8de8b3d2016-05-12 23:07:47 -040043func enableUnitTests() bool {
44 return *suite == "all" || *suite == "unit"
45}
46
47func enableSSLTests() bool {
48 return *suite == "all" || *suite == "ssl"
49}
50
David Benjaminf9459522016-03-07 15:30:26 -050051func adb(args ...string) error {
52 if len(*device) > 0 {
53 args = append([]string{"-s", *device}, args...)
54 }
David Benjamin00b10692016-05-19 00:13:22 -040055 cmd := exec.Command(*adbPath, args...)
David Benjaminf9459522016-03-07 15:30:26 -050056 cmd.Stdout = os.Stdout
57 cmd.Stderr = os.Stderr
58 return cmd.Run()
59}
60
David Benjamined9c8fc2016-06-08 09:40:32 -040061func adbShell(shellCmd string) (int, error) {
62 var args []string
63 if len(*device) > 0 {
64 args = append([]string{"-s", *device}, args...)
65 }
66 args = append(args, "shell")
67
68 const delimiter = "___EXIT_CODE___"
69
70 // Older versions of adb and Android do not preserve the exit
71 // code, so work around this.
72 // https://code.google.com/p/android/issues/detail?id=3254
73 shellCmd += "; echo " + delimiter + " $?"
74 args = append(args, shellCmd)
75
76 cmd := exec.Command(*adbPath, args...)
77 stdout, err := cmd.StdoutPipe()
78 if err != nil {
79 return 0, err
80 }
81 cmd.Stderr = os.Stderr
82 if err := cmd.Start(); err != nil {
83 return 0, err
84 }
85
86 var stdoutBytes bytes.Buffer
87 for {
88 var buf [1024]byte
89 n, err := stdout.Read(buf[:])
90 stdoutBytes.Write(buf[:n])
91 os.Stdout.Write(buf[:n])
92 if err != nil {
93 break
94 }
95 }
96
97 if err := cmd.Wait(); err != nil {
98 return 0, err
99 }
100
101 stdoutStr := stdoutBytes.String()
102 idx := strings.LastIndex(stdoutStr, delimiter)
103 if idx < 0 {
104 return 0, fmt.Errorf("Could not find delimiter in output.")
105 }
106
107 return strconv.Atoi(strings.TrimSpace(stdoutStr[idx+len(delimiter):]))
108}
109
David Benjaminf9459522016-03-07 15:30:26 -0500110func goTool(args ...string) error {
111 cmd := exec.Command("go", args...)
112 cmd.Stdout = os.Stdout
113 cmd.Stderr = os.Stderr
114
David Benjamin1147be02016-05-19 13:23:11 -0400115 cmd.Env = os.Environ()
David Benjaminf9459522016-03-07 15:30:26 -0500116 if *aarch64 {
117 cmd.Env = append(cmd.Env, "GOARCH=arm64")
118 } else {
119 cmd.Env = append(cmd.Env, "GOARCH=arm")
120 cmd.Env = append(cmd.Env, fmt.Sprintf("GOARM=%d", *arm))
121 }
122 return cmd.Run()
123}
124
125// setWorkingDirectory walks up directories as needed until the current working
126// directory is the top of a BoringSSL checkout.
127func setWorkingDirectory() {
128 for i := 0; i < 64; i++ {
129 if _, err := os.Stat("BUILDING.md"); err == nil {
130 return
131 }
132 os.Chdir("..")
133 }
134
135 panic("Couldn't find BUILDING.md in a parent directory!")
136}
137
138type test []string
139
140func parseTestConfig(filename string) ([]test, error) {
141 in, err := os.Open(filename)
142 if err != nil {
143 return nil, err
144 }
145 defer in.Close()
146
147 decoder := json.NewDecoder(in)
148 var result []test
149 if err := decoder.Decode(&result); err != nil {
150 return nil, err
151 }
152 return result, nil
153}
154
155func copyFile(dst, src string) error {
156 srcFile, err := os.Open(src)
157 if err != nil {
158 return err
159 }
160 defer srcFile.Close()
161
162 srcInfo, err := srcFile.Stat()
163 if err != nil {
164 return err
165 }
166
167 dir := filepath.Dir(dst)
168 if err := os.MkdirAll(dir, 0777); err != nil {
169 return err
170 }
171
172 dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, srcInfo.Mode())
173 if err != nil {
174 return err
175 }
176 defer dstFile.Close()
177
178 _, err = io.Copy(dstFile, srcFile)
179 return err
180}
181
182func main() {
183 flag.Parse()
David Benjaminf9459522016-03-07 15:30:26 -0500184
David Benjamin8de8b3d2016-05-12 23:07:47 -0400185 if *suite == "all" && *jsonOutput != "" {
186 fmt.Printf("To use -json-output flag, select only one test suite with -suite.\n")
David Benjaminf9459522016-03-07 15:30:26 -0500187 os.Exit(1)
188 }
189
David Benjamin8de8b3d2016-05-12 23:07:47 -0400190 setWorkingDirectory()
191
David Benjaminf9459522016-03-07 15:30:26 -0500192 // Clear the target directory.
193 if err := adb("shell", "rm -Rf /data/local/tmp/boringssl-tmp"); err != nil {
194 fmt.Printf("Failed to clear target directory: %s\n", err)
195 os.Exit(1)
196 }
197
198 // Stage everything in a temporary directory.
199 tmpDir, err := ioutil.TempDir("", "boringssl-android")
200 if err != nil {
201 fmt.Printf("Error making temporary directory: %s\n", err)
202 os.Exit(1)
203 }
204 defer os.RemoveAll(tmpDir)
205
David Benjamin8de8b3d2016-05-12 23:07:47 -0400206 var binaries, files []string
207
208 if enableUnitTests() {
209 files = append(files,
210 "util/all_tests.json",
211 "BUILDING.md",
212 )
213
214 tests, err := parseTestConfig("util/all_tests.json")
215 if err != nil {
216 fmt.Printf("Failed to parse input: %s\n", err)
217 os.Exit(1)
David Benjaminf9459522016-03-07 15:30:26 -0500218 }
David Benjamin8de8b3d2016-05-12 23:07:47 -0400219
220 seenBinary := make(map[string]struct{})
221 for _, test := range tests {
222 if _, ok := seenBinary[test[0]]; !ok {
223 binaries = append(binaries, test[0])
224 seenBinary[test[0]] = struct{}{}
David Benjaminf9459522016-03-07 15:30:26 -0500225 }
David Benjamin8de8b3d2016-05-12 23:07:47 -0400226 for _, arg := range test[1:] {
227 if strings.Contains(arg, "/") {
228 files = append(files, arg)
229 }
230 }
231 }
232
233 fmt.Printf("Building all_tests...\n")
234 if err := goTool("build", "-o", filepath.Join(tmpDir, "util/all_tests"), "util/all_tests.go"); err != nil {
235 fmt.Printf("Error building all_tests.go: %s\n", err)
236 os.Exit(1)
237 }
238 }
239
240 if enableSSLTests() {
241 binaries = append(binaries, "ssl/test/bssl_shim")
242 files = append(files,
243 "BUILDING.md",
David Benjamin8de8b3d2016-05-12 23:07:47 -0400244 "ssl/test/runner/cert.pem",
245 "ssl/test/runner/channel_id_key.pem",
David Benjamin218f51b2017-02-27 16:41:47 -0500246 "ssl/test/runner/ecdsa_p224_cert.pem",
247 "ssl/test/runner/ecdsa_p224_key.pem",
David Benjamin0c222952016-07-12 15:08:24 -0400248 "ssl/test/runner/ecdsa_p256_cert.pem",
249 "ssl/test/runner/ecdsa_p256_key.pem",
250 "ssl/test/runner/ecdsa_p384_cert.pem",
251 "ssl/test/runner/ecdsa_p384_key.pem",
252 "ssl/test/runner/ecdsa_p521_cert.pem",
253 "ssl/test/runner/ecdsa_p521_key.pem",
David Benjamin0ef8c7b2017-04-06 11:48:59 -0400254 "ssl/test/runner/ed25519_cert.pem",
255 "ssl/test/runner/ed25519_key.pem",
David Benjamin8de8b3d2016-05-12 23:07:47 -0400256 "ssl/test/runner/key.pem",
David Benjamin7944a9f2016-07-12 22:27:01 -0400257 "ssl/test/runner/rsa_1024_cert.pem",
258 "ssl/test/runner/rsa_1024_key.pem",
David Benjamine8b554d2016-11-15 10:43:13 +0900259 "ssl/test/runner/rsa_chain_cert.pem",
260 "ssl/test/runner/rsa_chain_key.pem",
261 "util/all_tests.json",
David Benjamin8de8b3d2016-05-12 23:07:47 -0400262 )
263
264 fmt.Printf("Building runner...\n")
265 if err := goTool("test", "-c", "-o", filepath.Join(tmpDir, "ssl/test/runner/runner"), "./ssl/test/runner/"); err != nil {
266 fmt.Printf("Error building runner: %s\n", err)
267 os.Exit(1)
David Benjaminf9459522016-03-07 15:30:26 -0500268 }
269 }
270
271 fmt.Printf("Copying test binaries...\n")
272 for _, binary := range binaries {
273 if err := copyFile(filepath.Join(tmpDir, "build", binary), filepath.Join(*buildDir, binary)); err != nil {
274 fmt.Printf("Failed to copy %s: %s\n", binary, err)
275 os.Exit(1)
276 }
277 }
278
279 fmt.Printf("Copying data files...\n")
280 for _, file := range files {
281 if err := copyFile(filepath.Join(tmpDir, file), file); err != nil {
282 fmt.Printf("Failed to copy %s: %s\n", file, err)
283 os.Exit(1)
284 }
285 }
286
David Benjaminf9459522016-03-07 15:30:26 -0500287 fmt.Printf("Uploading files...\n")
288 if err := adb("push", "-p", tmpDir, "/data/local/tmp/boringssl-tmp"); err != nil {
289 fmt.Printf("Failed to push runner: %s\n", err)
290 os.Exit(1)
291 }
292
David Benjamined9c8fc2016-06-08 09:40:32 -0400293 var unitTestExit int
David Benjamin8de8b3d2016-05-12 23:07:47 -0400294 if enableUnitTests() {
295 fmt.Printf("Running unit tests...\n")
David Benjamined9c8fc2016-06-08 09:40:32 -0400296 unitTestExit, err = adbShell(fmt.Sprintf("cd /data/local/tmp/boringssl-tmp && ./util/all_tests -json-output results.json %s", *allTestsArgs))
297 if err != nil {
David Benjamin8de8b3d2016-05-12 23:07:47 -0400298 fmt.Printf("Failed to run unit tests: %s\n", err)
299 os.Exit(1)
300 }
David Benjaminf9459522016-03-07 15:30:26 -0500301 }
302
David Benjamined9c8fc2016-06-08 09:40:32 -0400303 var sslTestExit int
David Benjamin8de8b3d2016-05-12 23:07:47 -0400304 if enableSSLTests() {
305 fmt.Printf("Running SSL tests...\n")
David Benjamined9c8fc2016-06-08 09:40:32 -0400306 sslTestExit, err = adbShell(fmt.Sprintf("cd /data/local/tmp/boringssl-tmp/ssl/test/runner && ./runner -json-output ../../../results.json %s", *runnerArgs))
307 if err != nil {
David Benjamin8de8b3d2016-05-12 23:07:47 -0400308 fmt.Printf("Failed to run SSL tests: %s\n", err)
309 os.Exit(1)
310 }
311 }
312
313 if *jsonOutput != "" {
314 if err := adb("pull", "-p", "/data/local/tmp/boringssl-tmp/results.json", *jsonOutput); err != nil {
315 fmt.Printf("Failed to extract results.json: %s\n", err)
316 os.Exit(1)
317 }
David Benjaminf9459522016-03-07 15:30:26 -0500318 }
David Benjamined9c8fc2016-06-08 09:40:32 -0400319
320 if unitTestExit != 0 {
321 os.Exit(unitTestExit)
322 }
323
324 if sslTestExit != 0 {
325 os.Exit(sslTestExit)
326 }
David Benjaminf9459522016-03-07 15:30:26 -0500327}