| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | // Copyright 2015 The Go Authors. All rights reserved. | 
					
						
							|  |  |  | // Use of this source code is governed by a BSD-style | 
					
						
							|  |  |  | // license that can be found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This program can be used as go_darwin_arm_exec by the Go tool. | 
					
						
							|  |  |  | // It executes binaries on an iOS device using the XCode toolchain | 
					
						
							|  |  |  | // and the ios-deploy program: https://github.com/phonegap/ios-deploy | 
					
						
							|  |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"flag" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"go/build" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"log" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const debug = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func main() { | 
					
						
							|  |  |  | 	log.SetFlags(0) | 
					
						
							|  |  |  | 	log.SetPrefix("go_darwin_arm_exec: ") | 
					
						
							|  |  |  | 	if debug { | 
					
						
							|  |  |  | 		log.Println(strings.Join(os.Args, " ")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(os.Args) < 2 { | 
					
						
							|  |  |  | 		log.Fatal("usage: go_darwin_arm_exec a.out") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := run(os.Args[1], os.Args[2:]); err != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "go_darwin_arm_exec: %v\n", err) | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | func run(bin string, args []string) (err error) { | 
					
						
							|  |  |  | 	type waitPanic struct { | 
					
						
							|  |  |  | 		err error | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if r := recover(); r != nil { | 
					
						
							|  |  |  | 			if w, ok := r.(waitPanic); ok { | 
					
						
							|  |  |  | 				err = w.err | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			panic(r) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	defer exec.Command("killall", "ios-deploy").Run() // cleanup | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	exec.Command("killall", "ios-deploy").Run() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tmpdir, err := ioutil.TempDir("", "go_darwin_arm_exec_") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 	if !debug { | 
					
						
							|  |  |  | 		defer os.RemoveAll(tmpdir) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	appdir := filepath.Join(tmpdir, "gotest.app") | 
					
						
							|  |  |  | 	if err := os.MkdirAll(appdir, 0755); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist") | 
					
						
							|  |  |  | 	if err := ioutil.WriteFile(entitlementsPath, []byte(entitlementsPlist), 0744); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := ioutil.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist), 0744); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := ioutil.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pkgpath, err := copyLocalData(appdir) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd := exec.Command( | 
					
						
							|  |  |  | 		"codesign", | 
					
						
							|  |  |  | 		"-f", | 
					
						
							|  |  |  | 		"-s", "E8BMC3FE2Z", // certificate associated with golang.org | 
					
						
							|  |  |  | 		"--entitlements", entitlementsPath, | 
					
						
							|  |  |  | 		appdir, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if debug { | 
					
						
							|  |  |  | 		log.Println(strings.Join(cmd.Args, " ")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cmd.Stdout = os.Stdout | 
					
						
							|  |  |  | 	cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 	if err := cmd.Run(); err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("codesign: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := os.Chdir(tmpdir); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ios-deploy invokes lldb to give us a shell session with the app. | 
					
						
							|  |  |  | 	cmd = exec.Command( | 
					
						
							|  |  |  | 		// lldb tries to be clever with terminals. | 
					
						
							|  |  |  | 		// So we wrap it in script(1) and be clever | 
					
						
							|  |  |  | 		// right back at it. | 
					
						
							|  |  |  | 		"script", | 
					
						
							|  |  |  | 		"-q", "-t", "0", | 
					
						
							|  |  |  | 		"/dev/null", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"ios-deploy", | 
					
						
							|  |  |  | 		"--debug", | 
					
						
							|  |  |  | 		"-u", | 
					
						
							|  |  |  | 		"-r", | 
					
						
							|  |  |  | 		"-n", | 
					
						
							|  |  |  | 		`--args=`+strings.Join(args, " ")+``, | 
					
						
							|  |  |  | 		"--bundle", appdir, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if debug { | 
					
						
							|  |  |  | 		log.Println(strings.Join(cmd.Args, " ")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lldbr, lldb, err := os.Pipe() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w := new(bufWriter) | 
					
						
							|  |  |  | 	cmd.Stdout = w | 
					
						
							|  |  |  | 	cmd.Stderr = w // everything of interest is on stderr | 
					
						
							|  |  |  | 	cmd.Stdin = lldbr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := cmd.Start(); err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("ios-deploy failed to start: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Manage the -test.timeout here, outside of the test. There is a lot | 
					
						
							|  |  |  | 	// of moving parts in an iOS test harness (notably lldb) that can | 
					
						
							|  |  |  | 	// swallow useful stdio or cause its own ruckus. | 
					
						
							|  |  |  | 	var timedout chan struct{} | 
					
						
							|  |  |  | 	if t := parseTimeout(args); t > 1*time.Second { | 
					
						
							|  |  |  | 		timedout = make(chan struct{}) | 
					
						
							|  |  |  | 		time.AfterFunc(t-1*time.Second, func() { | 
					
						
							|  |  |  | 			close(timedout) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	exited := make(chan error) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		exited <- cmd.Wait() | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 	waitFor := func(stage, str string, timeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-timedout: | 
					
						
							|  |  |  | 			w.printBuf() | 
					
						
							|  |  |  | 			if p := cmd.Process; p != nil { | 
					
						
							|  |  |  | 				p.Kill() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return fmt.Errorf("timeout (stage %s)", stage) | 
					
						
							|  |  |  | 		case err := <-exited: | 
					
						
							|  |  |  | 			w.printBuf() | 
					
						
							|  |  |  | 			return fmt.Errorf("failed (stage %s): %v", stage, err) | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 		case i := <-w.find(str, timeout): | 
					
						
							|  |  |  | 			if i >= 0 { | 
					
						
							|  |  |  | 				w.clearTo(i + len(str)) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				log.Printf("timed out on stage %s, continuing", stage) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	do := func(cmd string) { | 
					
						
							|  |  |  | 		fmt.Fprintln(lldb, cmd) | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 		if err := waitFor(fmt.Sprintf("prompt after %q", cmd), "(lldb)", 0); err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 			panic(waitPanic{err}) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for installation and connection. | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 	if err := waitFor("ios-deploy before run", "(lldb)     connect\r\nProcess 0 connected\r\n", 0); err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Script LLDB. Oh dear. | 
					
						
							|  |  |  | 	do(`process handle SIGHUP  --stop false --pass true --notify false`) | 
					
						
							|  |  |  | 	do(`process handle SIGPIPE --stop false --pass true --notify false`) | 
					
						
							|  |  |  | 	do(`process handle SIGUSR1 --stop false --pass true --notify false`) | 
					
						
							|  |  |  | 	do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work | 
					
						
							|  |  |  | 	do(`process handle SIGBUS  --stop false --pass true --notify false`) // does not work | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go | 
					
						
							| 
									
										
											  
											
												misc/ios: extra stdout check before run
On one recent job I saw an unexpected SIGSTOP, which I suspect is
simply the job timeout. But the lack of other diagnostics suggests
lldb just didn't see the "run" command.
-----
process handle SIGHUP  --stop false --pass true --notify false
process handle SIGPIPE --stop false --pass true --notify false
process handle SIGUSR1 --stop false --pass true --notify false
process handle SIGSEGV --stop false --pass true --notify false
process handle SIGBUS  --stop false --pass true --notify false
breakpoint set -n getwd
run
(lldb) NAME        PASS   STOP   NOTIFY
==========  =====  =====  ======
SIGHUP      true   false  false
(lldb) NAME        PASS   STOP   NOTIFY
==========  =====  =====  ======
SIGPIPE     true   false  false
(lldb) NAME        PASS   STOP   NOTIFY
==========  =====  =====  ======
SIGUSR1     true   false  false
(lldb) NAME        PASS   STOP   NOTIFY
==========  =====  =====  ======
SIGSEGV     true   false  false
(lldb) NAME        PASS   STOP   NOTIFY
==========  =====  =====  ======
SIGBUS      true   false  false
(lldb) Breakpoint 1: where = libsystem_c.dylib`getwd, address = 0x2f7f7294
(lldb) Process 23755 stopped
* thread #1: tid = 0x104c02, 0x1febb000 dyld`_dyld_start, stop reason = signal SIGSTOP
    frame #0: 0x1febb000 dyld`_dyld_start
dyld`_dyld_start:
-> 0x1febb000:  mov    r8, sp
   0x1febb004:  sub    sp, sp, #0x10
   0x1febb008:  bic    sp, sp, #0x7
   0x1febb00c:  ldr    r3, [pc, #112]            ; _dyld_start + 132
(lldb) go_darwin_arm_exec: timeout (stage br getwd)
FAIL	compress/gzip	359.226s
Change-Id: Ifc2123f5ceaa6d3f9b31bb5cb6e77a2c8ec23818
Reviewed-on: https://go-review.googlesource.com/6613
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
											
										 
											2015-03-03 14:18:56 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 	fmt.Fprintln(lldb, `run`) | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 	// Sometimes we don't see "reason = breakpoint", so we time out | 
					
						
							|  |  |  | 	// and try to continue. | 
					
						
							| 
									
										
										
										
											2015-03-25 13:22:32 -04:00
										 |  |  | 	if err := waitFor("br getwd", "stop reason = breakpoint", 10*time.Second); err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 	if err := waitFor("br getwd prompt", "(lldb)", 0); err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Move the current working directory into the faux gopath. | 
					
						
							|  |  |  | 	do(`breakpoint delete 1`) | 
					
						
							|  |  |  | 	do(`expr char* $mem = (char*)malloc(512)`) | 
					
						
							|  |  |  | 	do(`expr $mem = (char*)getwd($mem, 512)`) | 
					
						
							|  |  |  | 	do(`expr $mem = (char*)strcat($mem, "/` + pkgpath + `")`) | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 	do(`call (void)chdir($mem)`) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Watch for SIGSEGV. Ideally lldb would never break on SIGSEGV. | 
					
						
							|  |  |  | 	// http://golang.org/issue/10043 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 		<-w.find("stop reason = EXC_BAD_ACCESS", 0) | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 		// cannot use do here, as the defer/recover is not available | 
					
						
							|  |  |  | 		// on this goroutine. | 
					
						
							|  |  |  | 		fmt.Fprintln(lldb, `bt`) | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 		waitFor("finish backtrace", "(lldb)", 0) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		w.printBuf() | 
					
						
							|  |  |  | 		if p := cmd.Process; p != nil { | 
					
						
							|  |  |  | 			p.Kill() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Run the tests. | 
					
						
							|  |  |  | 	w.trimSuffix("(lldb) ") | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 	fmt.Fprintln(lldb, `process continue`) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for the test to complete. | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case <-timedout: | 
					
						
							|  |  |  | 		w.printBuf() | 
					
						
							|  |  |  | 		if p := cmd.Process; p != nil { | 
					
						
							|  |  |  | 			p.Kill() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return errors.New("timeout running tests") | 
					
						
							|  |  |  | 	case err := <-exited: | 
					
						
							|  |  |  | 		// The returned lldb error code is usually non-zero. | 
					
						
							|  |  |  | 		// We check for test success by scanning for the final | 
					
						
							|  |  |  | 		// PASS returned by the test harness, assuming the worst | 
					
						
							|  |  |  | 		// in its absence. | 
					
						
							|  |  |  | 		if w.isPass() { | 
					
						
							|  |  |  | 			err = nil | 
					
						
							|  |  |  | 		} else if err == nil { | 
					
						
							|  |  |  | 			err = errors.New("test failure") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.printBuf() | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type bufWriter struct { | 
					
						
							|  |  |  | 	mu     sync.Mutex | 
					
						
							|  |  |  | 	buf    []byte | 
					
						
							|  |  |  | 	suffix []byte // remove from each Write | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 	findTxt   []byte   // search buffer on each Write | 
					
						
							|  |  |  | 	findCh    chan int // report find position | 
					
						
							|  |  |  | 	findAfter *time.Timer | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *bufWriter) Write(in []byte) (n int, err error) { | 
					
						
							|  |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	n = len(in) | 
					
						
							|  |  |  | 	in = bytes.TrimSuffix(in, w.suffix) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf = append(w.buf, in...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(w.findTxt) > 0 { | 
					
						
							|  |  |  | 		if i := bytes.Index(w.buf, w.findTxt); i >= 0 { | 
					
						
							|  |  |  | 			w.findCh <- i | 
					
						
							|  |  |  | 			close(w.findCh) | 
					
						
							|  |  |  | 			w.findTxt = nil | 
					
						
							|  |  |  | 			w.findCh = nil | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 			if w.findAfter != nil { | 
					
						
							|  |  |  | 				w.findAfter.Stop() | 
					
						
							|  |  |  | 				w.findAfter = nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *bufWriter) trimSuffix(p string) { | 
					
						
							|  |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 	w.suffix = []byte(p) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *bufWriter) printBuf() { | 
					
						
							|  |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 	fmt.Fprintf(os.Stderr, "%s", w.buf) | 
					
						
							|  |  |  | 	w.buf = nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *bufWriter) clearTo(i int) { | 
					
						
							|  |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 	if debug { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "--- go_darwin_arm_exec clear ---\n%s\n--- go_darwin_arm_exec clear ---\n", w.buf[:i]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.buf = w.buf[i:] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | // find returns a channel that will have exactly one byte index sent | 
					
						
							|  |  |  | // to it when the text str appears in the buffer. If the text does not | 
					
						
							|  |  |  | // appear before timeout, -1 is sent. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // A timeout of zero means no timeout. | 
					
						
							|  |  |  | func (w *bufWriter) find(str string, timeout time.Duration) <-chan int { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 	if len(w.findTxt) > 0 { | 
					
						
							|  |  |  | 		panic(fmt.Sprintf("find(%s): already trying to find %s", str, w.findTxt)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	txt := []byte(str) | 
					
						
							|  |  |  | 	ch := make(chan int, 1) | 
					
						
							|  |  |  | 	if i := bytes.Index(w.buf, txt); i >= 0 { | 
					
						
							|  |  |  | 		ch <- i | 
					
						
							|  |  |  | 		close(ch) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		w.findTxt = txt | 
					
						
							|  |  |  | 		w.findCh = ch | 
					
						
							| 
									
										
										
										
											2015-03-24 20:52:11 -04:00
										 |  |  | 		if timeout > 0 { | 
					
						
							|  |  |  | 			w.findAfter = time.AfterFunc(timeout, func() { | 
					
						
							|  |  |  | 				w.mu.Lock() | 
					
						
							|  |  |  | 				defer w.mu.Unlock() | 
					
						
							|  |  |  | 				if w.findCh == ch { | 
					
						
							|  |  |  | 					w.findTxt = nil | 
					
						
							|  |  |  | 					w.findCh = nil | 
					
						
							|  |  |  | 					w.findAfter = nil | 
					
						
							|  |  |  | 					ch <- -1 | 
					
						
							|  |  |  | 					close(ch) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return ch | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *bufWriter) isPass() bool { | 
					
						
							|  |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The final stdio of lldb is non-deterministic, so we | 
					
						
							|  |  |  | 	// scan the whole buffer. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Just to make things fun, lldb sometimes translates \n | 
					
						
							|  |  |  | 	// into \r\n. | 
					
						
							|  |  |  | 	return bytes.Contains(w.buf, []byte("\nPASS\n")) || bytes.Contains(w.buf, []byte("\nPASS\r")) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseTimeout(testArgs []string) (timeout time.Duration) { | 
					
						
							|  |  |  | 	var args []string | 
					
						
							|  |  |  | 	for _, arg := range testArgs { | 
					
						
							|  |  |  | 		if strings.Contains(arg, "test.timeout") { | 
					
						
							|  |  |  | 			args = append(args, arg) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	f := flag.NewFlagSet("", flag.ContinueOnError) | 
					
						
							|  |  |  | 	f.DurationVar(&timeout, "test.timeout", 0, "") | 
					
						
							|  |  |  | 	f.Parse(args) | 
					
						
							|  |  |  | 	if debug { | 
					
						
							|  |  |  | 		log.Printf("parseTimeout of %s, got %s", args, timeout) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return timeout | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func copyLocalDir(dst, src string) error { | 
					
						
							|  |  |  | 	if err := os.Mkdir(dst, 0755); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d, err := os.Open(src) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer d.Close() | 
					
						
							|  |  |  | 	fi, err := d.Readdir(-1) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, f := range fi { | 
					
						
							|  |  |  | 		if f.IsDir() { | 
					
						
							|  |  |  | 			if f.Name() == "testdata" { | 
					
						
							|  |  |  | 				if err := cp(dst, filepath.Join(src, f.Name())); err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := cp(dst, filepath.Join(src, f.Name())); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func cp(dst, src string) error { | 
					
						
							|  |  |  | 	out, err := exec.Command("cp", "-a", src, dst).CombinedOutput() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		os.Stderr.Write(out) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func copyLocalData(dstbase string) (pkgpath string, err error) { | 
					
						
							|  |  |  | 	cwd, err := os.Getwd() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	finalPkgpath, underGoRoot, err := subdir() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cwd = strings.TrimSuffix(cwd, finalPkgpath) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Copy all immediate files and testdata directories between | 
					
						
							|  |  |  | 	// the package being tested and the source root. | 
					
						
							|  |  |  | 	pkgpath = "" | 
					
						
							|  |  |  | 	for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) { | 
					
						
							|  |  |  | 		if debug { | 
					
						
							|  |  |  | 			log.Printf("copying %s", pkgpath) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		pkgpath = filepath.Join(pkgpath, element) | 
					
						
							|  |  |  | 		dst := filepath.Join(dstbase, pkgpath) | 
					
						
							|  |  |  | 		src := filepath.Join(cwd, pkgpath) | 
					
						
							|  |  |  | 		if err := copyLocalDir(dst, src); err != nil { | 
					
						
							|  |  |  | 			return "", err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Copy timezone file. | 
					
						
							| 
									
										
										
										
											2015-03-02 16:05:11 -05:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// Typical apps have the zoneinfo.zip in the root of their app bundle, | 
					
						
							|  |  |  | 	// read by the time package as the working directory at initialization. | 
					
						
							|  |  |  | 	// As we move the working directory to the GOROOT pkg directory, we | 
					
						
							|  |  |  | 	// install the zoneinfo.zip file in the pkgpath. | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	if underGoRoot { | 
					
						
							| 
									
										
										
										
											2015-03-02 16:05:11 -05:00
										 |  |  | 		err := cp( | 
					
						
							|  |  |  | 			filepath.Join(dstbase, pkgpath), | 
					
						
							|  |  |  | 			filepath.Join(cwd, "lib", "time", "zoneinfo.zip"), | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 			return "", err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return finalPkgpath, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // subdir determines the package based on the current working directory, | 
					
						
							|  |  |  | // and returns the path to the package source relative to $GOROOT (or $GOPATH). | 
					
						
							|  |  |  | func subdir() (pkgpath string, underGoRoot bool, err error) { | 
					
						
							|  |  |  | 	cwd, err := os.Getwd() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", false, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if root := runtime.GOROOT(); strings.HasPrefix(cwd, root) { | 
					
						
							|  |  |  | 		subdir, err := filepath.Rel(root, cwd) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", false, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return subdir, true, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, p := range filepath.SplitList(build.Default.GOPATH) { | 
					
						
							|  |  |  | 		if !strings.HasPrefix(cwd, p) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		subdir, err := filepath.Rel(p, cwd) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			return subdir, false, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "", false, fmt.Errorf( | 
					
						
							|  |  |  | 		"working directory %q is not in either GOROOT(%q) or GOPATH(%q)", | 
					
						
							|  |  |  | 		cwd, | 
					
						
							|  |  |  | 		runtime.GOROOT(), | 
					
						
							|  |  |  | 		build.Default.GOPATH, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const infoPlist = `<?xml version="1.0" encoding="UTF-8"?> | 
					
						
							|  |  |  | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 
					
						
							|  |  |  | <plist version="1.0"> | 
					
						
							|  |  |  | <dict> | 
					
						
							|  |  |  | <key>CFBundleName</key><string>golang.gotest</string> | 
					
						
							|  |  |  | <key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array> | 
					
						
							|  |  |  | <key>CFBundleExecutable</key><string>gotest</string> | 
					
						
							|  |  |  | <key>CFBundleVersion</key><string>1.0</string> | 
					
						
							|  |  |  | <key>CFBundleIdentifier</key><string>golang.gotest</string> | 
					
						
							|  |  |  | <key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string> | 
					
						
							|  |  |  | <key>LSRequiresIPhoneOS</key><true/> | 
					
						
							|  |  |  | <key>CFBundleDisplayName</key><string>gotest</string> | 
					
						
							|  |  |  | </dict> | 
					
						
							|  |  |  | </plist> | 
					
						
							|  |  |  | ` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const devID = `YE84DJ86AZ` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const entitlementsPlist = `<?xml version="1.0" encoding="UTF-8"?> | 
					
						
							|  |  |  | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 
					
						
							|  |  |  | <plist version="1.0"> | 
					
						
							|  |  |  | <dict> | 
					
						
							|  |  |  | 	<key>keychain-access-groups</key> | 
					
						
							|  |  |  | 	<array><string>` + devID + `.golang.gotest</string></array> | 
					
						
							|  |  |  | 	<key>get-task-allow</key> | 
					
						
							|  |  |  | 	<true/> | 
					
						
							|  |  |  | 	<key>application-identifier</key> | 
					
						
							|  |  |  | 	<string>` + devID + `.golang.gotest</string> | 
					
						
							|  |  |  | 	<key>com.apple.developer.team-identifier</key> | 
					
						
							|  |  |  | 	<string>` + devID + `</string> | 
					
						
							|  |  |  | </dict> | 
					
						
							|  |  |  | </plist>` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const resourceRules = `<?xml version="1.0" encoding="UTF-8"?> | 
					
						
							|  |  |  | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 
					
						
							|  |  |  | <plist version="1.0"> | 
					
						
							|  |  |  | <dict> | 
					
						
							|  |  |  |         <key>rules</key> | 
					
						
							|  |  |  |         <dict> | 
					
						
							|  |  |  |                 <key>.*</key><true/> | 
					
						
							|  |  |  | 		<key>Info.plist</key>  | 
					
						
							|  |  |  | 		<dict> | 
					
						
							|  |  |  | 			<key>omit</key> <true/> | 
					
						
							|  |  |  | 			<key>weight</key> <real>10</real> | 
					
						
							|  |  |  | 		</dict> | 
					
						
							|  |  |  | 		<key>ResourceRules.plist</key> | 
					
						
							|  |  |  | 		<dict> | 
					
						
							|  |  |  | 			<key>omit</key> <true/> | 
					
						
							|  |  |  | 			<key>weight</key> <real>100</real> | 
					
						
							|  |  |  | 		</dict> | 
					
						
							|  |  |  | 	</dict> | 
					
						
							|  |  |  | </dict> | 
					
						
							|  |  |  | </plist> | 
					
						
							|  |  |  | ` |