Skip to content

Commit 72af7ea

Browse files
Added ios17 xcuitest runner (#347)
* Added ios17 xcuitest runner * Added missing refactoring changes * Fixed runtest CLI command docs --------- Co-authored-by: danielpaulus <[email protected]>
1 parent d3366ca commit 72af7ea

File tree

12 files changed

+1233
-243
lines changed

12 files changed

+1233
-243
lines changed

ios/accessibility/accessibility.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const serviceName string = "com.apple.accessibility.axAuditDaemon.remoteserver"
99

1010
// New creates and connects to the given device, a new ControlInterface instance
1111
func New(device ios.DeviceEntry) (ControlInterface, error) {
12-
conn, err := dtx.NewConnection(device, serviceName)
12+
conn, err := dtx.NewUsbmuxdConnection(device, serviceName)
1313
if err != nil {
1414
return ControlInterface{}, err
1515
}

ios/dtx_codec/connection.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,27 @@ func notifyOfPublishedCapabilities(msg Message) {
9696
log.Debug("capabs received")
9797
}
9898

99-
// NewConnection connects and starts reading from a Dtx based service on the device
100-
func NewConnection(device ios.DeviceEntry, serviceName string) (*Connection, error) {
99+
// NewUsbmuxdConnection connects and starts reading from a Dtx based service on the device
100+
func NewUsbmuxdConnection(device ios.DeviceEntry, serviceName string) (*Connection, error) {
101101
conn, err := ios.ConnectToService(device, serviceName)
102102
if err != nil {
103103
return nil, err
104104
}
105+
106+
return newDtxConnection(conn)
107+
}
108+
109+
// NewTunnelConnection connects and starts reading from a Dtx based service on the device, using tunnel interface instead of usbmuxd
110+
func NewTunnelConnection(device ios.DeviceEntry, serviceName string) (*Connection, error) {
111+
conn, err := ios.ConnectToServiceTunnelIface(device, serviceName)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
return newDtxConnection(conn)
117+
}
118+
119+
func newDtxConnection(conn ios.DeviceConnectionInterface) (*Connection, error) {
105120
requestChannelMessages := make(chan Message, 5)
106121

107122
// The global channel has channelCode 0, so we need to start with channelCodeCounter==1

ios/instruments/helper.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ func (p loggingDispatcher) Dispatch(m dtx.Message) {
2424
}
2525

2626
func connectInstruments(device ios.DeviceEntry) (*dtx.Connection, error) {
27-
dtxConn, err := dtx.NewConnection(device, serviceName)
27+
dtxConn, err := dtx.NewUsbmuxdConnection(device, serviceName)
2828
if err != nil {
2929
log.Debugf("Failed connecting to %s, trying %s", serviceName, serviceNameiOS14)
30-
dtxConn, err = dtx.NewConnection(device, serviceNameiOS14)
30+
dtxConn, err = dtx.NewUsbmuxdConnection(device, serviceNameiOS14)
3131
if err != nil {
3232
return nil, err
3333
}

ios/nskeyedarchiver/objectivec_classes.go

Lines changed: 156 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"time"
66

77
"github.com/google/uuid"
8-
log "github.com/sirupsen/logrus"
98
"howett.net/plist"
109
)
1110

@@ -26,13 +25,18 @@ func SetupDecoders() {
2625
"DTTapHeartbeatMessage": NewDTTapHeartbeatMessage,
2726
"XCTCapabilities": NewXCTCapabilities,
2827
"NSUUID": NewNSUUIDFromBytes,
29-
"XCActivityRecord": DecodeXCActivityRecord,
28+
"XCActivityRecord": NewXCActivityRecord,
29+
"XCTAttachment": NewXCTAttachment,
3030
"DTKTraceTapMessage": NewDTKTraceTapMessage,
3131
"NSValue": NewNSValue,
32+
"NSArray": NewNSArray,
3233
"XCTTestIdentifier": NewXCTTestIdentifier,
3334
"DTTapStatusMessage": NewDTTapStatusMessage,
3435
"DTTapMessage": NewDTTapMessage,
3536
"DTCPUClusterInfo": NewDTCPUClusterInfo,
37+
"XCTIssue": NewXCTIssue,
38+
"XCTSourceCodeContext": NewXCTSourceCodeContext,
39+
"XCTSourceCodeLocation": NewXCTSourceCodeLocation,
3640
}
3741
}
3842
}
@@ -80,7 +84,7 @@ func NewXCTestConfiguration(
8084
contents["reportActivities"] = true
8185
contents["reportResultsToIDE"] = true
8286
contents["sessionIdentifier"] = NewNSUUID(sessionIdentifier)
83-
contents["systemAttachmentLifetime"] = 2
87+
contents["systemAttachmentLifetime"] = 0
8488
// contents["targetApplicationArguments"] = []interface{}{} //TODO: triggers a bug
8589
contents["targetApplicationBundleID"] = targetApplicationBundleID
8690
// contents["targetApplicationEnvironment"] = //TODO: triggers a bug map[string]interface{}{}
@@ -96,7 +100,20 @@ func NewXCTestConfiguration(
96100
contents["testsToSkip"] = plist.UID(0)
97101
contents["testTimeoutsEnabled"] = false
98102
contents["treatMissingBaselinesAsFailures"] = false
99-
contents["userAttachmentLifetime"] = 1
103+
contents["userAttachmentLifetime"] = 0
104+
contents["preferredScreenCaptureFormat"] = 2
105+
contents["IDECapabilities"] = XCTCapabilities{CapabilitiesDictionary: map[string]interface{}{
106+
"expected failure test capability": true,
107+
"test case run configurations": true,
108+
"test timeout capability": true,
109+
"test iterations": true,
110+
"request diagnostics for specific devices": true,
111+
"delayed attachment transfer": true,
112+
"skipped test capability": true,
113+
"daemon container sandbox extension": true,
114+
"ubiquitous test identifiers": true,
115+
"XCTIssue capability": true,
116+
}}
100117
return XCTestConfiguration{contents}
101118
}
102119

@@ -147,20 +164,28 @@ type XCActivityRecord struct {
147164
"attachments":<interface {}(howett.net/plist.UID)>)
148165
149166
*/
150-
Finish interface{}
151-
Start interface{}
167+
Finish NSDate
168+
Start NSDate
152169
Title string
153170
UUID NSUUID
154171
ActivityType string
155-
Attachments interface{}
172+
Attachments []XCTAttachment
156173
}
157174

158-
func DecodeXCActivityRecord(object map[string]interface{}, objects []interface{}) interface{} {
175+
func NewXCActivityRecord(object map[string]interface{}, objects []interface{}) interface{} {
159176
finish_ref := object["finish"].(plist.UID)
160-
finish := objects[finish_ref]
177+
finish := NSDate{}
178+
if _, ok := objects[finish_ref].(map[string]interface{}); ok {
179+
finish_raw := objects[finish_ref].(map[string]interface{})
180+
finish = NewNSDate(finish_raw, objects).(NSDate)
181+
}
161182

162183
start_ref := object["start"].(plist.UID)
163-
start := objects[start_ref]
184+
start := NSDate{}
185+
if _, ok := objects[start_ref].(map[string]interface{}); ok {
186+
start_raw := objects[start_ref].(map[string]interface{})
187+
start = NewNSDate(start_raw, objects).(NSDate)
188+
}
164189

165190
uuid_ref := object["uuid"].(plist.UID)
166191
uuid_raw := objects[uuid_ref].(map[string]interface{})
@@ -170,16 +195,54 @@ func DecodeXCActivityRecord(object map[string]interface{}, objects []interface{}
170195
title := objects[title_ref].(string)
171196

172197
attachments_ref := object["attachments"].(plist.UID)
173-
attachments := objects[attachments_ref]
198+
attachments_raw := objects[attachments_ref].(map[string]interface{})
199+
200+
attachments := make([]XCTAttachment, 0)
201+
for _, obj := range NewNSArray(attachments_raw, objects).(NSArray).Values {
202+
attachments = append(attachments, obj.(XCTAttachment))
203+
}
174204

175205
activityType_ref := object["activityType"].(plist.UID)
176206
activityType := objects[activityType_ref].(string)
177207

178-
log.Info(objects[9])
179-
180208
return XCActivityRecord{Finish: finish, Start: start, UUID: uuid, Title: title, Attachments: attachments, ActivityType: activityType}
181209
}
182210

211+
const (
212+
LifetimeKeepAlways = uint64(0)
213+
LifetimeDeleteOnSuccess = uint64(1)
214+
)
215+
216+
type XCTAttachment struct {
217+
lifetime uint64
218+
UniformTypeIdentifier string
219+
fileNameOverride string
220+
Payload []uint8
221+
Timestamp float64
222+
Name string
223+
userInfo map[string]interface{}
224+
}
225+
226+
func NewXCTAttachment(object map[string]interface{}, objects []interface{}) interface{} {
227+
lifetime := object["lifetime"].(uint64)
228+
uniformTypeIdentifier := objects[object["uniformTypeIdentifier"].(plist.UID)].(string)
229+
fileNameOverride := objects[object["fileNameOverride"].(plist.UID)].(string)
230+
payload := objects[object["payload"].(plist.UID)].([]uint8)
231+
timestamp := objects[object["timestamp"].(plist.UID)].(float64)
232+
name := objects[object["name"].(plist.UID)].(string)
233+
userInfo, _ := extractDictionary(objects[object["userInfo"].(plist.UID)].(map[string]interface{}), objects)
234+
235+
return XCTAttachment{
236+
lifetime: lifetime,
237+
UniformTypeIdentifier: uniformTypeIdentifier,
238+
fileNameOverride: fileNameOverride,
239+
Payload: payload,
240+
Timestamp: timestamp,
241+
Name: name,
242+
userInfo: userInfo,
243+
}
244+
}
245+
183246
func NewNSUUIDFromBytes(object map[string]interface{}, objects []interface{}) interface{} {
184247
val := object["NS.uuidbytes"].([]byte)
185248
return NSUUID{uuidbytes: val}
@@ -193,6 +256,21 @@ func NewNSUUID(id uuid.UUID) NSUUID {
193256
return NSUUID{bytes}
194257
}
195258

259+
func archiveNSUUID(uid interface{}, objects []interface{}) ([]interface{}, plist.UID) {
260+
nsuuid := uid.(NSUUID)
261+
object := map[string]interface{}{}
262+
263+
object["NS.uuidbytes"] = nsuuid.uuidbytes
264+
uuidReference := len(objects)
265+
objects = append(objects, object)
266+
267+
classref := uuidReference + 1
268+
object[class] = plist.UID(classref)
269+
objects = append(objects, buildClassDict("NSUUID", "NSObject"))
270+
271+
return objects, plist.UID(uuidReference)
272+
}
273+
196274
func archiveXCTCapabilities(capsIface interface{}, objects []interface{}) ([]interface{}, plist.UID) {
197275
caps := capsIface.(XCTCapabilities)
198276
object := map[string]interface{}{}
@@ -209,23 +287,8 @@ func archiveXCTCapabilities(capsIface interface{}, objects []interface{}) ([]int
209287
return objects, plist.UID(capsReference)
210288
}
211289

212-
func archiveNSUUID(uid interface{}, objects []interface{}) ([]interface{}, plist.UID) {
213-
nsuuid := uid.(NSUUID)
214-
object := map[string]interface{}{}
215-
216-
object["NS.uuidbytes"] = nsuuid.uuidbytes
217-
uuidReference := len(objects)
218-
objects = append(objects, object)
219-
220-
classref := uuidReference + 1
221-
object[class] = plist.UID(classref)
222-
objects = append(objects, buildClassDict("NSUUID", "NSObject"))
223-
224-
return objects, plist.UID(uuidReference)
225-
}
226-
227290
type NSURL struct {
228-
path string
291+
Path string
229292
}
230293

231294
func NewNSURL(path string) NSURL {
@@ -247,7 +310,7 @@ func archiveNSURL(nsurlInterface interface{}, objects []interface{}) ([]interfac
247310

248311
pathRef := classref + 1
249312
object["NS.relative"] = plist.UID(pathRef)
250-
objects = append(objects, fmt.Sprintf("file://%s", nsurl.path))
313+
objects = append(objects, fmt.Sprintf("file://%s", nsurl.Path))
251314

252315
return objects, plist.UID(urlReference)
253316
}
@@ -284,6 +347,19 @@ func NewNSValue(object map[string]interface{}, objects []interface{}) interface{
284347
return NSValue{NSRectval: rectval, NSSpecial: special}
285348
}
286349

350+
type NSArray struct {
351+
Values []interface{}
352+
}
353+
354+
func NewNSArray(object map[string]interface{}, objects []interface{}) interface{} {
355+
objectRefs := object["NS.objects"].([]interface{})
356+
357+
uidList := toUidList(objectRefs)
358+
extractObjects, _ := extractObjects(uidList, objects)
359+
360+
return NSArray{Values: extractObjects}
361+
}
362+
287363
type XCTTestIdentifier struct {
288364
O uint64
289365
C []string
@@ -344,6 +420,10 @@ func NewNSError(object map[string]interface{}, objects []interface{}) interface{
344420
return NSError{ErrorCode: errorCode, Domain: domain, UserInfo: userinfo}
345421
}
346422

423+
func (err NSError) Error() string {
424+
return fmt.Sprintf("Error code: %d, Domain: %s, User info: %v", err.ErrorCode, err.Domain, err.UserInfo)
425+
}
426+
347427
// Apples Reference Date is Jan 1st 2001 00:00
348428
const nsReferenceDate = 978307200000
349429

@@ -450,3 +530,49 @@ func archiveNSMutableDictionary(object interface{}, objects []interface{}) ([]in
450530
mut := object.(NSMutableDictionary)
451531
return serializeMap(mut.internalDict, objects, buildClassDict("NSMutableDictionary", "NSDictionary", "NSObject"))
452532
}
533+
534+
type XCTIssue struct {
535+
RuntimeIssueSeverity uint64
536+
DetailedDescription string
537+
CompactDescription string
538+
SourceCodeContext XCTSourceCodeContext
539+
}
540+
541+
func NewXCTIssue(object map[string]interface{}, objects []interface{}) interface{} {
542+
runtimeIssueSeverity := object["runtimeIssueSeverity"].(uint64)
543+
detailedDescriptionRef := object["detailed-description"].(plist.UID)
544+
sourceCodeContextRef := object["source-code-context"].(plist.UID)
545+
compactDescriptionRef := object["compact-description"].(plist.UID)
546+
547+
detailedDescription := objects[detailedDescriptionRef].(string)
548+
compactDescription := objects[compactDescriptionRef].(string)
549+
sourceCodeContext := NewXCTSourceCodeContext(objects[sourceCodeContextRef].(map[string]interface{}), objects).(XCTSourceCodeContext)
550+
551+
return XCTIssue{RuntimeIssueSeverity: runtimeIssueSeverity, DetailedDescription: detailedDescription, CompactDescription: compactDescription, SourceCodeContext: sourceCodeContext}
552+
}
553+
554+
type XCTSourceCodeContext struct {
555+
Location XCTSourceCodeLocation
556+
}
557+
558+
func NewXCTSourceCodeContext(object map[string]interface{}, objects []interface{}) interface{} {
559+
locationRef := object["location"].(plist.UID)
560+
location := NewXCTSourceCodeLocation(objects[locationRef].(map[string]interface{}), objects).(XCTSourceCodeLocation)
561+
562+
return XCTSourceCodeContext{Location: location}
563+
}
564+
565+
type XCTSourceCodeLocation struct {
566+
FileUrl NSURL
567+
LineNumber uint64
568+
}
569+
570+
func NewXCTSourceCodeLocation(object map[string]interface{}, objects []interface{}) interface{} {
571+
fileUrlRef := object["file-url"].(plist.UID)
572+
relativeRef := objects[fileUrlRef].(map[string]interface{})["NS.relative"].(plist.UID)
573+
relativePath := objects[int(relativeRef)].(string)
574+
fileUrl := NewNSURL(relativePath)
575+
lineNumber := object["line-number"].(uint64)
576+
577+
return XCTSourceCodeLocation{FileUrl: fileUrl, LineNumber: lineNumber}
578+
}

0 commit comments

Comments
 (0)