From f922bea3516f4fe1b212e9a7a8b1f5c5a0d13ae1 Mon Sep 17 00:00:00 2001 From: Jon Rafkind Date: Wed, 12 Jan 2022 12:00:45 -0800 Subject: [PATCH 1/2] use a context to stop the xcuitest if an error occurs --- ios/testmanagerd/xcuitestrunner.go | 34 +++++++++++++++--------- ios/testmanagerd/xcuitestrunner_11.go | 13 ++++++--- main.go | 38 ++++++++++++++++++++------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/ios/testmanagerd/xcuitestrunner.go b/ios/testmanagerd/xcuitestrunner.go index 5a00b45d..a7a3ca2b 100644 --- a/ios/testmanagerd/xcuitestrunner.go +++ b/ios/testmanagerd/xcuitestrunner.go @@ -1,6 +1,7 @@ package testmanagerd import ( + "context" "fmt" "path" "strings" @@ -163,6 +164,7 @@ type ProxyDispatcher struct { testRunnerReadyWithCapabilities dtx.MethodWithResponse dtxConnection *dtx.Connection id string + cancel context.CancelFunc } func (p ProxyDispatcher) Dispatch(m dtx.Message) { @@ -185,6 +187,9 @@ func (p ProxyDispatcher) Dispatch(m dtx.Message) { messageBytes, _ := dtx.Encode(m.Identifier, 1, m.ChannelCode, false, dtx.ResponseWithReturnValueInPayload, payload, dtx.NewPrimitiveDictionary()) log.Debug("sending response for capabs") p.dtxConnection.Send(messageBytes) + case "_XCT_didFailToBootstrapWithError:": + log.Debug("failed to bootstrap") + p.cancel() default: log.WithFields(log.Fields{"sel": method}).Infof("device called local method") @@ -196,10 +201,10 @@ func (p ProxyDispatcher) Dispatch(m dtx.Message) { log.Tracef("dispatcher received: %s", m.String()) } -func newDtxProxy(dtxConnection *dtx.Connection) dtxproxy { +func newDtxProxy(dtxConnection *dtx.Connection, cancel context.CancelFunc) dtxproxy { testBundleReadyChannel := make(chan dtx.Message, 1) //(xide XCTestManager_IDEInterface) - proxyDispatcher := ProxyDispatcher{testBundleReadyChannel: testBundleReadyChannel, dtxConnection: dtxConnection} + proxyDispatcher := ProxyDispatcher{testBundleReadyChannel: testBundleReadyChannel, dtxConnection: dtxConnection, cancel: cancel} IDEDaemonProxy := dtxConnection.RequestChannelIdentifier(ideToDaemonProxyChannelName, proxyDispatcher) ideInterface := XCTestManager_IDEInterface{IDEDaemonProxy: IDEDaemonProxy, testBundleReadyChannel: testBundleReadyChannel} @@ -211,10 +216,10 @@ func newDtxProxy(dtxConnection *dtx.Connection) dtxproxy { } } -func newDtxProxyWithConfig(dtxConnection *dtx.Connection, testConfig nskeyedarchiver.XCTestConfiguration) dtxproxy { +func newDtxProxyWithConfig(dtxConnection *dtx.Connection, testConfig nskeyedarchiver.XCTestConfiguration, cancel context.CancelFunc) dtxproxy { testBundleReadyChannel := make(chan dtx.Message, 1) //(xide XCTestManager_IDEInterface) - proxyDispatcher := ProxyDispatcher{testBundleReadyChannel: testBundleReadyChannel, dtxConnection: dtxConnection, testRunnerReadyWithCapabilities: testRunnerReadyWithCapabilitiesConfig(testConfig)} + proxyDispatcher := ProxyDispatcher{testBundleReadyChannel: testBundleReadyChannel, dtxConnection: dtxConnection, testRunnerReadyWithCapabilities: testRunnerReadyWithCapabilitiesConfig(testConfig), cancel: cancel} IDEDaemonProxy := dtxConnection.RequestChannelIdentifier(ideToDaemonProxyChannelName, proxyDispatcher) ideInterface := XCTestManager_IDEInterface{IDEDaemonProxy: IDEDaemonProxy, testConfig: testConfig, testBundleReadyChannel: testBundleReadyChannel} @@ -231,22 +236,25 @@ const testmanagerdiOS14 = "com.apple.testmanagerd.lockdown.secure" const testBundleSuffix = "UITests.xctrunner" -func RunXCUITest(bundleID string, device ios.DeviceEntry) error { +func RunXCUITest(bundleID string, device ios.DeviceEntry, quit context.Context) error { testRunnerBundleID := bundleID + testBundleSuffix - return RunXCUIWithBundleIds(bundleID, testRunnerBundleID, "", device, nil, nil) + return RunXCUIWithBundleIds(bundleID, testRunnerBundleID, "", device, nil, nil, quit) } var closeChan = make(chan interface{}) var closedChan = make(chan interface{}) func runXUITestWithBundleIdsXcode12(bundleID string, testRunnerBundleID string, xctestConfigFileName string, - device ios.DeviceEntry, conn *dtx.Connection, args []string, env []string) error { + device ios.DeviceEntry, conn *dtx.Connection, args []string, env []string, mainQuit context.Context) error { + + localQuit, localCancel := context.WithCancel(mainQuit) + testSessionId, xctestConfigPath, testConfig, testInfo, err := setupXcuiTest(device, bundleID, testRunnerBundleID, xctestConfigFileName) if err != nil { return err } defer conn.Close() - ideDaemonProxy := newDtxProxyWithConfig(conn, testConfig) + ideDaemonProxy := newDtxProxyWithConfig(conn, testConfig, localCancel) conn2, err := dtx.NewConnection(device, testmanagerdiOS14) if err != nil { @@ -254,7 +262,7 @@ func runXUITestWithBundleIdsXcode12(bundleID string, testRunnerBundleID string, } defer conn2.Close() log.Debug("connections ready") - ideDaemonProxy2 := newDtxProxyWithConfig(conn2, testConfig) + ideDaemonProxy2 := newDtxProxyWithConfig(conn2, testConfig, localCancel) ideDaemonProxy2.ideInterface.testConfig = testConfig caps, err := ideDaemonProxy.daemonConnection.initiateControlSessionWithCapabilities(nskeyedarchiver.XCTCapabilities{}) if err != nil { @@ -294,7 +302,7 @@ func runXUITestWithBundleIdsXcode12(bundleID string, testRunnerBundleID string, if err != nil { log.Error(err) } - <-closeChan + <-localQuit.Done() log.Infof("Killing WebDriverAgent with pid %d ...", pid) err = pControl.KillProcess(pid) if err != nil { @@ -314,7 +322,7 @@ func RunXCUIWithBundleIds( device ios.DeviceEntry, wdaargs []string, wdaenv []string, -) error { + quit context.Context) error { version, err := ios.GetProductVersion(device) if err != nil { return err @@ -322,7 +330,7 @@ func RunXCUIWithBundleIds( log.Debugf("%v", version) if version.LessThan(ios.IOS14()) { log.Infof("iOS version: %s detected, running with ios11 support", version) - return RunXCUIWithBundleIds11(bundleID, testRunnerBundleID, xctestConfigFileName, device, wdaargs, wdaenv) + return RunXCUIWithBundleIds11(bundleID, testRunnerBundleID, xctestConfigFileName, device, wdaargs, wdaenv, quit) } @@ -330,7 +338,7 @@ func RunXCUIWithBundleIds( if err != nil { return err } - return runXUITestWithBundleIdsXcode12(bundleID, testRunnerBundleID, xctestConfigFileName, device, conn, wdaargs, wdaenv) + return runXUITestWithBundleIdsXcode12(bundleID, testRunnerBundleID, xctestConfigFileName, device, conn, wdaargs, wdaenv, quit) } diff --git a/ios/testmanagerd/xcuitestrunner_11.go b/ios/testmanagerd/xcuitestrunner_11.go index 8b48b47e..753f4efd 100644 --- a/ios/testmanagerd/xcuitestrunner_11.go +++ b/ios/testmanagerd/xcuitestrunner_11.go @@ -6,6 +6,7 @@ import ( "github.com/danielpaulus/go-ios/ios/instruments" log "github.com/sirupsen/logrus" "strings" + "context" ) func RunXCUIWithBundleIds11( @@ -14,7 +15,9 @@ func RunXCUIWithBundleIds11( xctestConfigFileName string, device ios.DeviceEntry, args []string, - env []string) error { + env []string, + quit context.Context) error { + localQuit, localCancel := context.WithCancel(quit) log.Debugf("set up xcuitest") testSessionId, xctestConfigPath, testConfig, testInfo, err := setupXcuiTest(device, bundleID, testRunnerBundleID, xctestConfigFileName) if err != nil { @@ -23,7 +26,7 @@ func RunXCUIWithBundleIds11( log.Debugf("test session setup ok") conn, err := dtx.NewConnection(device, testmanagerd) defer conn.Close() - ideDaemonProxy := newDtxProxyWithConfig(conn, testConfig) + ideDaemonProxy := newDtxProxyWithConfig(conn, testConfig, localCancel) conn2, err := dtx.NewConnection(device, testmanagerd) if err != nil { @@ -31,7 +34,7 @@ func RunXCUIWithBundleIds11( } defer conn2.Close() log.Debug("connections ready") - ideDaemonProxy2 := newDtxProxyWithConfig(conn2, testConfig) + ideDaemonProxy2 := newDtxProxyWithConfig(conn2, testConfig, localCancel) ideDaemonProxy2.ideInterface.testConfig = testConfig //TODO: fixme protocolVersion := uint64(25) @@ -65,15 +68,17 @@ func RunXCUIWithBundleIds11( log.Error(err) } log.Debugf("done starting test") - <-closeChan + <-localQuit.Done() log.Infof("Killing WebDriverAgent with pid %d ...", pid) err = pControl.KillProcess(pid) if err != nil { return err } + /* log.Info("WDA killed with success") var signal interface{} closedChan <- signal + */ return nil } diff --git a/main.go b/main.go index baebabe4..7c91530f 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "encoding/json" "fmt" "github.com/danielpaulus/go-ios/ios/testmanagerd" @@ -420,8 +421,17 @@ The commands work as following: b, _ = arguments.Bool("runtest") if b { + quit, cancel := context.WithCancel(context.Background()) + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + signal := <-c + log.Infof("os signal:%d received, closing..", signal) + cancel() + }() + bundleID, _ := arguments.String("") - err := testmanagerd.RunXCUITest(bundleID, device) + err := testmanagerd.RunXCUITest(bundleID, device, quit) if err != nil { log.WithFields(log.Fields{"error": err}).Info("Failed running Xcuitest") } @@ -445,25 +455,33 @@ The commands work as following: log.WithFields(log.Fields{"bundleid": bundleID, "testbundleid": testbundleID, "xctestconfig": xctestconfig}).Error("please specify either NONE of bundleid, testbundleid and xctestconfig or ALL of them. At least one was empty.") return } + quit, cancel := context.WithCancel(context.Background()) log.WithFields(log.Fields{"bundleid": bundleID, "testbundleid": testbundleID, "xctestconfig": xctestconfig}).Info("Running wda") go func() { - err := testmanagerd.RunXCUIWithBundleIds(bundleID, testbundleID, xctestconfig, device, wdaargs, wdaenv) - - if err != nil { - log.WithFields(log.Fields{"error": err}).Fatal("Failed running WDA") - } + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + signal := <-c + log.Infof("os signal:%d received, closing..", signal) + cancel() + signal = <-c + log.Infof("os signal:%d received. force kill..", signal) + os.Exit(1) }() - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - signal := <-c - log.Infof("os signal:%d received, closing..", signal) + err := testmanagerd.RunXCUIWithBundleIds(bundleID, testbundleID, xctestconfig, device, wdaargs, wdaenv, quit) + + if err != nil { + log.WithFields(log.Fields{"error": err}).Fatal("Failed running WDA") + } + + /* err := testmanagerd.CloseXCUITestRunner() if err != nil { log.Error("Failed closing wda-testrunner") os.Exit(1) } log.Info("Done Closing") + */ return } From d826bf23a063c7e7eb84fcb8b584b2307228f7bc Mon Sep 17 00:00:00 2001 From: Jon Rafkind Date: Wed, 12 Jan 2022 12:03:08 -0800 Subject: [PATCH 2/2] dont listen for the closed channel --- ios/testmanagerd/xcuitestrunner.go | 2 ++ ios/testmanagerd/xcuitestrunner_11.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/testmanagerd/xcuitestrunner.go b/ios/testmanagerd/xcuitestrunner.go index a7a3ca2b..a13f4a66 100644 --- a/ios/testmanagerd/xcuitestrunner.go +++ b/ios/testmanagerd/xcuitestrunner.go @@ -309,8 +309,10 @@ func runXUITestWithBundleIdsXcode12(bundleID string, testRunnerBundleID string, return err } log.Info("WDA killed with success") + /* var signal interface{} closedChan <- signal + */ return nil } diff --git a/ios/testmanagerd/xcuitestrunner_11.go b/ios/testmanagerd/xcuitestrunner_11.go index 753f4efd..1ef77467 100644 --- a/ios/testmanagerd/xcuitestrunner_11.go +++ b/ios/testmanagerd/xcuitestrunner_11.go @@ -74,8 +74,8 @@ func RunXCUIWithBundleIds11( if err != nil { return err } - /* log.Info("WDA killed with success") + /* var signal interface{} closedChan <- signal */