Skip to content

Commit 6a94ebe

Browse files
fix: quit runwda when wda dies (danielpaulus#389)
With this changes ios runwda will exit with error when WDA app dies (e.g. killed with ios kill, killed manually with AppSwitcher, or when iPhone is turned off). dtxConnection's BreakdownCallback is implemented with Functionals Options Pattern, so it's fully optional and shouldn't affect any other dtxConnection usages.
1 parent fe5e60b commit 6a94ebe

File tree

5 files changed

+102
-32
lines changed

5 files changed

+102
-32
lines changed

ios/dtx_codec/connection.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dtx
22

33
import (
4+
"errors"
45
"io"
56
"math"
67
"strings"
@@ -15,6 +16,8 @@ import (
1516

1617
type MethodWithResponse func(msg Message) (interface{}, error)
1718

19+
var ErrConnectionClosed = errors.New("Connection closed")
20+
1821
// Connection manages channels, including the GlobalChannel, for a DtxConnection and dispatches received messages
1922
// to the right channel.
2023
type Connection struct {
@@ -25,6 +28,10 @@ type Connection struct {
2528
capabilities map[string]interface{}
2629
mutex sync.Mutex
2730
requestChannelMessages chan Message
31+
32+
closed chan struct{}
33+
err error
34+
closeOnce sync.Once
2835
}
2936

3037
// Dispatcher is a simple interface containing a Dispatch func to receive dtx.Messages
@@ -41,11 +48,24 @@ type GlobalDispatcher struct {
4148

4249
const requestChannel = "_requestChannelWithCode:identifier:"
4350

51+
// Closed is closed when the underlying DTX connection was closed for any reason (either initiated by calling Close() or due to an error)
52+
func (dtxConn *Connection) Closed() <-chan struct{} {
53+
return dtxConn.closed
54+
}
55+
56+
// Err is non-nil when the connection was closed (when Close was called this will be ErrConnectionClosed)
57+
func (dtxConn *Connection) Err() error {
58+
return dtxConn.err
59+
}
60+
4461
// Close closes the underlying deviceConnection
4562
func (dtxConn *Connection) Close() error {
4663
if dtxConn.deviceConnection != nil {
47-
return dtxConn.deviceConnection.Close()
64+
err := dtxConn.deviceConnection.Close()
65+
dtxConn.close(err)
66+
return err
4867
}
68+
dtxConn.close(ErrConnectionClosed)
4969
return nil
5070
}
5171

@@ -121,6 +141,7 @@ func newDtxConnection(conn ios.DeviceConnectionInterface) (*Connection, error) {
121141

122142
// The global channel has channelCode 0, so we need to start with channelCodeCounter==1
123143
dtxConnection := &Connection{deviceConnection: conn, channelCodeCounter: 1, requestChannelMessages: requestChannelMessages}
144+
dtxConnection.closed = make(chan struct{})
124145

125146
// The global channel is automatically present and used for requesting other channels and some other methods like notifyPublishedCapabilities
126147
globalChannel := Channel{
@@ -149,6 +170,7 @@ func reader(dtxConn *Connection) {
149170
reader := dtxConn.deviceConnection.Reader()
150171
msg, err := ReadMessage(reader)
151172
if err != nil {
173+
defer dtxConn.close(err)
152174
errText := err.Error()
153175
if err == io.EOF || strings.Contains(errText, "use of closed network") {
154176
log.Debug("DTX Connection with EOF")
@@ -228,3 +250,10 @@ func (dtxConn *Connection) RequestChannelIdentifier(identifier string, messageDi
228250

229251
return channel
230252
}
253+
254+
func (dtxConn *Connection) close(err error) {
255+
dtxConn.closeOnce.Do(func() {
256+
dtxConn.err = err
257+
close(dtxConn.closed)
258+
})
259+
}

ios/testmanagerd/xcuitestrunner.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -393,16 +393,29 @@ func runXUITestWithBundleIdsXcode15Ctx(
393393
}
394394

395395
select {
396+
case <-conn1.Closed():
397+
log.Debug("conn1 closed")
398+
if conn1.Err() != dtx.ErrConnectionClosed {
399+
log.WithError(conn1.Err()).Error("conn1 closed unexpectedly")
400+
}
401+
break
402+
case <-conn2.Closed():
403+
log.Debug("conn2 closed")
404+
if conn2.Err() != dtx.ErrConnectionClosed {
405+
log.WithError(conn2.Err()).Error("conn2 closed unexpectedly")
406+
}
407+
break
396408
case <-testListener.Done():
397409
break
398410
case <-ctx.Done():
399-
log.Infof("Killing test runner with pid %d ...", testRunnerLaunch.Pid)
400-
err = killTestRunner(appserviceConn, testRunnerLaunch.Pid)
401-
if err != nil {
402-
log.Infof("Nothing to kill, process with pid %d is already dead", testRunnerLaunch.Pid)
403-
} else {
404-
log.Info("Test runner killed with success")
405-
}
411+
break
412+
}
413+
log.Infof("Killing test runner with pid %d ...", testRunnerLaunch.Pid)
414+
err = killTestRunner(appserviceConn, testRunnerLaunch.Pid)
415+
if err != nil {
416+
log.Infof("Nothing to kill, process with pid %d is already dead", testRunnerLaunch.Pid)
417+
} else {
418+
log.Info("Test runner killed with success")
406419
}
407420

408421
log.Debugf("Done running test")

ios/testmanagerd/xcuitestrunner_11.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,29 @@ func runXCUIWithBundleIdsXcode11Ctx(
7878
}
7979

8080
select {
81+
case <-conn.Closed():
82+
log.Debug("conn closed")
83+
if conn.Err() != dtx.ErrConnectionClosed {
84+
log.WithError(conn.Err()).Error("conn closed unexpectedly")
85+
}
86+
break
87+
case <-conn2.Closed():
88+
log.Debug("conn2 closed")
89+
if conn2.Err() != dtx.ErrConnectionClosed {
90+
log.WithError(conn2.Err()).Error("conn2 closed unexpectedly")
91+
}
92+
break
8193
case <-testListener.Done():
8294
break
8395
case <-ctx.Done():
84-
log.Infof("Killing test runner with pid %d ...", pid)
85-
err = pControl.KillProcess(pid)
86-
if err != nil {
87-
log.Infof("Nothing to kill, process with pid %d is already dead", pid)
88-
} else {
89-
log.Info("Test runner killed with success")
90-
}
96+
break
97+
}
98+
log.Infof("Killing test runner with pid %d ...", pid)
99+
err = pControl.KillProcess(pid)
100+
if err != nil {
101+
log.Infof("Nothing to kill, process with pid %d is already dead", pid)
102+
} else {
103+
log.Info("Test runner killed with success")
91104
}
92105

93106
log.Debugf("Done running test")

ios/testmanagerd/xcuitestrunner_12.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,29 @@ func runXUITestWithBundleIdsXcode12Ctx(ctx context.Context, bundleID string, tes
7777
}
7878

7979
select {
80+
case <-conn.Closed():
81+
log.Debug("conn closed")
82+
if conn.Err() != dtx.ErrConnectionClosed {
83+
log.WithError(conn.Err()).Error("conn closed unexpectedly")
84+
}
85+
break
86+
case <-conn2.Closed():
87+
log.Debug("conn2 closed")
88+
if conn2.Err() != dtx.ErrConnectionClosed {
89+
log.WithError(conn2.Err()).Error("conn2 closed unexpectedly")
90+
}
91+
break
8092
case <-testListener.Done():
8193
break
8294
case <-ctx.Done():
83-
log.Infof("Killing test runner with pid %d ...", pid)
84-
err = pControl.KillProcess(pid)
85-
if err != nil {
86-
log.Infof("Nothing to kill, process with pid %d is already dead", pid)
87-
} else {
88-
log.Info("Test runner killed with success")
89-
}
95+
break
96+
}
97+
log.Infof("Killing test runner with pid %d ...", pid)
98+
err = pControl.KillProcess(pid)
99+
if err != nil {
100+
log.Infof("Nothing to kill, process with pid %d is already dead", pid)
101+
} else {
102+
log.Info("Test runner killed with success")
90103
}
91104

92105
log.Debugf("Done running test")

main.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,28 +1124,30 @@ func runWdaCommand(device ios.DeviceEntry, arguments docopt.Opts) bool {
11241124
log.WithFields(log.Fields{"bundleid": bundleID, "testbundleid": testbundleID, "xctestconfig": xctestconfig}).Info("Running wda")
11251125

11261126
errorChannel := make(chan error)
1127+
defer close(errorChannel)
11271128
ctx, stopWda := context.WithCancel(context.Background())
11281129
go func() {
11291130
_, err := testmanagerd.RunXCUIWithBundleIdsCtx(ctx, bundleID, testbundleID, xctestconfig, device, wdaargs, wdaenv, nil, nil, testmanagerd.NewTestListener(io.Discard, io.Discard, os.TempDir()))
11301131
if err != nil {
1131-
log.WithFields(log.Fields{"error": err}).Fatal("Failed running WDA")
11321132
errorChannel <- err
11331133
}
1134-
close(errorChannel)
1134+
stopWda()
11351135
}()
11361136
c := make(chan os.Signal, 1)
11371137
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
1138-
signal := <-c
1139-
log.Infof("os signal:%d received, closing..", signal)
11401138

1141-
stopWda()
1142-
1143-
err := <-errorChannel
1144-
if err != nil {
1145-
log.Errorf("Failed running wda-testrunner: %s", err)
1139+
select {
1140+
case err := <-errorChannel:
1141+
log.WithError(err).Error("Failed running WDA")
1142+
stopWda()
11461143
os.Exit(1)
1144+
case <-ctx.Done():
1145+
log.Error("WDA process ended unexpectedly")
1146+
os.Exit(1)
1147+
case signal := <-c:
1148+
log.Infof("os signal:%d received, closing...", signal)
1149+
stopWda()
11471150
}
1148-
11491151
log.Info("Done Closing")
11501152
}
11511153
return b

0 commit comments

Comments
 (0)