@@ -2,7 +2,6 @@ package testmanagerd
2
2
3
3
import (
4
4
"bytes"
5
- "errors"
6
5
"fmt"
7
6
"github.com/danielpaulus/go-ios/ios"
8
7
"howett.net/plist"
@@ -24,10 +23,6 @@ import (
24
23
// contributions or requests for support can be made in the relevant GitHub repository.
25
24
26
25
// xCTestRunData represents the structure of an .xctestrun file
27
- type xCTestRunData struct {
28
- TestConfig schemeData `plist:"-"`
29
- XCTestRunMetadata xCTestRunMetadata `plist:"__xctestrun_metadata__"`
30
- }
31
26
32
27
// schemeData represents the structure of a scheme-specific test configuration
33
28
type schemeData struct {
@@ -41,33 +36,28 @@ type schemeData struct {
41
36
TestingEnvironmentVariables map [string ]any
42
37
}
43
38
44
- // XCTestRunMetadata contains metadata about the .xctestrun file
45
- type xCTestRunMetadata struct {
46
- FormatVersion int `plist:"FormatVersion"`
47
- }
48
-
49
- func (data xCTestRunData ) buildTestConfig (device ios.DeviceEntry , listener * TestListener ) (TestConfig , error ) {
50
- testsToRun := data .TestConfig .OnlyTestIdentifiers
51
- testsToSkip := data .TestConfig .SkipTestIdentifiers
39
+ func (data schemeData ) buildTestConfig (device ios.DeviceEntry , listener * TestListener ) (TestConfig , error ) {
40
+ testsToRun := data .OnlyTestIdentifiers
41
+ testsToSkip := data .SkipTestIdentifiers
52
42
53
43
testEnv := make (map [string ]any )
54
- if data .TestConfig . IsUITestBundle {
55
- maps .Copy (testEnv , data .TestConfig . EnvironmentVariables )
56
- maps .Copy (testEnv , data .TestConfig . TestingEnvironmentVariables )
44
+ if data .IsUITestBundle {
45
+ maps .Copy (testEnv , data .EnvironmentVariables )
46
+ maps .Copy (testEnv , data .TestingEnvironmentVariables )
57
47
}
58
48
59
49
// Extract only the file name
60
- var testBundlePath = filepath .Base (data .TestConfig . TestBundlePath )
50
+ var testBundlePath = filepath .Base (data .TestBundlePath )
61
51
62
52
// Build the TestConfig object from parsed data
63
53
testConfig := TestConfig {
64
- TestRunnerBundleId : data .TestConfig . TestHostBundleIdentifier ,
54
+ TestRunnerBundleId : data .TestHostBundleIdentifier ,
65
55
XctestConfigName : testBundlePath ,
66
- Args : data .TestConfig . CommandLineArguments ,
56
+ Args : data .CommandLineArguments ,
67
57
Env : testEnv ,
68
58
TestsToRun : testsToRun ,
69
59
TestsToSkip : testsToSkip ,
70
- XcTest : ! data .TestConfig . IsUITestBundle ,
60
+ XcTest : ! data .IsUITestBundle ,
71
61
Device : device ,
72
62
Listener : listener ,
73
63
}
@@ -76,69 +66,66 @@ func (data xCTestRunData) buildTestConfig(device ios.DeviceEntry, listener *Test
76
66
}
77
67
78
68
// parseFile reads the .xctestrun file and decodes it into a map
79
- func parseFile (filePath string ) (xCTestRunData , error ) {
69
+ func parseFile (filePath string ) (schemeData , error ) {
80
70
file , err := os .Open (filePath )
81
71
if err != nil {
82
- return xCTestRunData {}, fmt .Errorf ("failed to open xctestrun file: %w" , err )
72
+ return schemeData {}, fmt .Errorf ("failed to open xctestrun file: %w" , err )
83
73
}
84
74
defer file .Close ()
85
75
return decode (file )
86
76
}
87
77
88
78
// decode decodes the binary xctestrun content into the xCTestRunData struct
89
- func decode (r io.Reader ) (xCTestRunData , error ) {
79
+ func decode (r io.Reader ) (schemeData , error ) {
90
80
// Read the entire content once
91
- content , err := io .ReadAll (r )
81
+ xctestrunFileContent , err := io .ReadAll (r )
92
82
if err != nil {
93
- return xCTestRunData {}, fmt .Errorf ("failed to read content: %w" , err )
94
- }
95
-
96
- // Use a single map for initial parsing
97
- var rawData map [string ]interface {}
98
- if _ , err := plist .Unmarshal (content , & rawData ); err != nil {
99
- return xCTestRunData {}, fmt .Errorf ("failed to unmarshal plist: %w" , err )
100
- }
101
-
102
- result := xCTestRunData {
103
- TestConfig : schemeData {}, // Initialize TestConfig
83
+ return schemeData {}, fmt .Errorf ("unable to read xctestrun content: %w" , err )
104
84
}
105
85
106
- // Parse metadata
107
- metadataMap , ok := rawData [ "__xctestrun_metadata__" ].( map [ string ] interface {} )
108
- if ! ok {
109
- return xCTestRunData {}, errors . New ( "invalid or missing __xctestrun_metadata__" )
86
+ // First, we only parse the version property of the xctestrun file. The rest of the parsing depends on this version.
87
+ version , err := getFormatVersion ( xctestrunFileContent )
88
+ if err != nil {
89
+ return schemeData {}, err
110
90
}
111
91
112
- // Direct decoding of metadata to avoid additional conversion
113
- switch v := metadataMap ["FormatVersion" ].(type ) {
114
- case int :
115
- result .XCTestRunMetadata .FormatVersion = v
116
- case uint64 :
117
- result .XCTestRunMetadata .FormatVersion = int (v )
92
+ switch version {
93
+ case 1 :
94
+ return parseVersion1 (xctestrunFileContent )
95
+ case 2 :
96
+ return schemeData {}, fmt .Errorf ("the provided .xctestrun file used format version 2, which is not yet supported" )
118
97
default :
119
- return xCTestRunData {}, fmt .Errorf ("unexpected FormatVersion type: %T " , metadataMap [ "FormatVersion" ] )
98
+ return schemeData {}, fmt .Errorf ("the provided .xctestrun format version %d is not supported " , version )
120
99
}
100
+ }
121
101
122
- // Verify FormatVersion
123
- if result .XCTestRunMetadata .FormatVersion != 1 {
124
- return result , fmt .Errorf ("go-ios currently only supports .xctestrun files in formatVersion 1: " +
125
- "The formatVersion of your xctestrun file is %d, feel free to open an issue in https://github.com/danielpaulus/go-ios/issues to " +
126
- "add support" , result .XCTestRunMetadata .FormatVersion )
102
+ // Helper method to get the format version of the xctestrun file
103
+ func getFormatVersion (xctestrunFileContent []byte ) (int , error ) {
104
+
105
+ type xCTestRunMetadata struct {
106
+ Metadata struct {
107
+ Version int `plist:"FormatVersion"`
108
+ } `plist:"__xctestrun_metadata__"`
127
109
}
128
110
129
- // Parse test schemes
130
- if err := parseTestSchemes ( rawData , & result . TestConfig ); err != nil {
131
- return xCTestRunData {}, err
111
+ var metadata xCTestRunMetadata
112
+ if _ , err := plist . Unmarshal ( xctestrunFileContent , & metadata ); err != nil {
113
+ return 0 , fmt . Errorf ( "failed to parse format version: %w" , err )
132
114
}
133
115
134
- return result , nil
116
+ return metadata . Metadata . Version , nil
135
117
}
136
118
137
- // parseTestSchemes extracts and parses test schemes from the raw data
138
- func parseTestSchemes (rawData map [string ]interface {}, scheme * schemeData ) error {
139
- // Dynamically find and parse test schemes
140
- for key , value := range rawData {
141
- // Skip metadata key
119
+ func parseVersion1 (content []byte ) (schemeData , error ) {
120
+ // xctestrun files in version 1 use a dynamic key for the pListRoot of the TestConfig. As in the 'key' for the TestConfig is the name
121
+ // of the app. This forces us to iterate over the root of the plist, instead of using a static struct to decode the xctestrun file.
122
+ var pListRoot map [string ]interface {}
123
+ if _ , err := plist .Unmarshal (content , & pListRoot ); err != nil {
124
+ return schemeData {}, fmt .Errorf ("failed to unmarshal plist: %w" , err )
125
+ }
126
+
127
+ for key , value := range pListRoot {
128
+ // Skip the metadata object
142
129
if key == "__xctestrun_metadata__" {
143
130
continue
144
131
}
@@ -154,19 +141,15 @@ func parseTestSchemes(rawData map[string]interface{}, scheme *schemeData) error
154
141
schemeBuf := new (bytes.Buffer )
155
142
encoder := plist .NewEncoder (schemeBuf )
156
143
if err := encoder .Encode (schemeMap ); err != nil {
157
- return fmt .Errorf ("failed to encode scheme %s: %w" , key , err )
144
+ return schemeData {}, fmt .Errorf ("failed to encode scheme %s: %w" , key , err )
158
145
}
159
146
160
147
// Decode the plist buffer into schemeData
161
148
decoder := plist .NewDecoder (bytes .NewReader (schemeBuf .Bytes ()))
162
149
if err := decoder .Decode (& schemeParsed ); err != nil {
163
- return fmt .Errorf ("failed to decode scheme %s: %w" , key , err )
150
+ return schemeData {}, fmt .Errorf ("failed to decode scheme %s: %w" , key , err )
164
151
}
165
-
166
- // Store the scheme in the result TestConfig
167
- * scheme = schemeParsed
168
- break // Only one scheme expected, break after the first valid scheme
152
+ return schemeParsed , nil
169
153
}
170
-
171
- return nil
154
+ return schemeData {}, nil
172
155
}
0 commit comments