@@ -27,15 +27,42 @@ struct BisectToolchains: AsyncParsableCommand {
27
27
""" )
28
28
var script : String
29
29
30
- @Option ( help: " Oldest tag. Expected to pass " )
31
- var goodTag : String
30
+ @Option ( help: " Oldest Date. Expected to Pass. We use the first snapshot produced before the given date " )
31
+ var oldDate : String
32
+
33
+ var oldDateAsDate : Date {
34
+ let d = DateFormatter ( )
35
+ d. dateFormat = " yyyy-MM-dd "
36
+ guard let result = d. date ( from: oldDate) else {
37
+ log ( " Improperly formatted date: \( oldDate) ! Expected format: yyyy_MM_dd. " )
38
+ fatalError ( )
39
+ }
40
+ return result
41
+ }
32
42
33
- @Option ( help: " Newest tag. Expected to fail. If not set, use newest snapshot " )
34
- var badTag : String ?
43
+ @Option ( help: """
44
+ Newest Date. Expected to fail. If not set, use newest snapshot. We use the
45
+ first snapshot after new date
46
+ """ )
47
+ var newDate : String ?
48
+
49
+ var newDateAsDate : Date ? {
50
+ guard let newDate = self . newDate else { return nil }
51
+ let d = DateFormatter ( )
52
+ d. dateFormat = " yyyy-MM-dd "
53
+ guard let result = d. date ( from: newDate) else {
54
+ log ( " Improperly formatted date: \( newDate) ! Expected format: yyyy_MM_dd. " )
55
+ fatalError ( )
56
+ }
57
+ return result
58
+ }
35
59
36
60
@Flag ( help: " Invert the test so that we assume the newest succeeds " )
37
61
var invert = false
38
62
63
+ @Argument ( help: " Extra constant arguments to pass to the test " )
64
+ var extraArgs : [ String ] = [ ]
65
+
39
66
mutating func run( ) async throws {
40
67
if !FileManager. default. fileExists ( atPath: workspace) {
41
68
do {
@@ -49,20 +76,25 @@ struct BisectToolchains: AsyncParsableCommand {
49
76
}
50
77
51
78
// Load our tags from swift's github repo
52
- let tags = try ! await getTagsFromSwiftRepo ( branch: branch)
53
-
54
- guard let goodTagIndex = tags. firstIndex ( where: { $0. name == self . goodTag } ) else {
55
- log ( " Failed to find tag: \( self . goodTag) " )
79
+ let tags = try ! await getTagsFromSwiftRepo ( branch: branch, dryRun: true )
80
+
81
+ // Newest is first. So 0 maps to the newest tag. We do this so someone can
82
+ // just say 50 toolchains ago. To get a few weeks worth. This is easier than
83
+ // writing dates a lot.
84
+ let oldDateAsDate = self . oldDateAsDate
85
+ guard let goodTagIndex = tags. firstIndex ( where: { $0. tag. date ( branch: self . branch) < oldDateAsDate } ) else {
86
+ log ( " Failed to find tag with date: \( oldDateAsDate) " )
56
87
fatalError ( )
57
88
}
58
89
59
- let badTagIndex : Array < Tag > . Index
60
- if let badTag = self . badTag {
61
- guard let n = tags. firstIndex ( where: { $0. name == badTag } ) else {
62
- log ( " Failed to find tag: \( badTag) " )
90
+ let badTagIndex : Int
91
+ if let newDateAsDate = self . newDateAsDate {
92
+ let b = tags. firstIndex ( where: { $0. tag. date ( branch: self . branch) < newDateAsDate } )
93
+ guard let b else {
94
+ log ( " Failed to find tag newer than date: \( newDateAsDate) " )
63
95
fatalError ( )
64
96
}
65
- badTagIndex = n
97
+ badTagIndex = b
66
98
} else {
67
99
badTagIndex = 0
68
100
}
@@ -73,30 +105,73 @@ struct BisectToolchains: AsyncParsableCommand {
73
105
fatalError ( )
74
106
}
75
107
76
- log ( " [INFO] Testing \( totalTags) toolchains " )
77
-
78
108
var startIndex = goodTagIndex
79
109
var endIndex = badTagIndex
80
- while startIndex != endIndex && startIndex != ( endIndex - 1 ) {
110
+
111
+ // First check if the newest toolchain succeeds. We assume this in our bisection.
112
+ do {
113
+ log ( " Testing that Oldest Tag Succeeds: \( tags [ startIndex] . tag) ) " )
114
+ let result = try ! await downloadToolchainAndRunTest (
115
+ platform: platform, tag: tags [ startIndex] . tag, branch: branch, workspace: workspace, script: script,
116
+ extraArgs: extraArgs)
117
+ var success = result == 0
118
+ if self . invert {
119
+ success = !success
120
+ }
121
+ if !success {
122
+ log ( " [INFO] Oldest snapshot fails?! We assume that the oldest snapshot is known good! " )
123
+ } else {
124
+ log ( " [INFO] Oldest snapshot passes test. Snapshot: \( tags [ startIndex] ) " )
125
+ }
126
+ }
127
+
128
+ do {
129
+ log ( " Testing that Newest Tag Fails: \( tags [ endIndex] . tag) ) " )
130
+ let result = try ! await downloadToolchainAndRunTest (
131
+ platform: platform, tag: tags [ endIndex] . tag, branch: branch, workspace: workspace, script: script,
132
+ extraArgs: extraArgs)
133
+ var success = result != 0
134
+ if self . invert {
135
+ success = !success
136
+ }
137
+ if !success {
138
+ log ( " [INFO] Newest snapshot succeceds?! We assume that the newest snapshot is known bad! " )
139
+ } else {
140
+ log ( " [INFO] Newest snapshot passes test. Snapshot: \( tags [ endIndex] ) " )
141
+ }
142
+ }
143
+
144
+ log ( " [INFO] Testing \( totalTags) toolchains " )
145
+ while startIndex != endIndex && startIndex != endIndex {
81
146
let mid = ( startIndex + endIndex) / 2
147
+
148
+ let midValue = tags [ mid] . tag
82
149
log (
83
- " [INFO] Visiting Mid: \( mid) with (Start, End) = ( \( startIndex) , \( endIndex) ). Tag: \( tags [ mid ] ) "
150
+ " [INFO] Visiting Mid: \( mid) with (Start, End) = ( \( startIndex) , \( endIndex) ). Tag: \( midValue ) "
84
151
)
85
152
let result = try ! await downloadToolchainAndRunTest (
86
- platform: platform, tag: tags [ mid] , branch: branch, workspace: workspace, script: script)
153
+ platform: platform, tag: midValue, branch: branch, workspace: workspace, script: script,
154
+ extraArgs: extraArgs)
87
155
88
156
var success = result == 0
89
157
if self . invert {
90
158
success = !success
91
159
}
92
160
161
+ let midIsEndIndex = mid == endIndex
162
+
93
163
if success {
94
164
log ( " [INFO] PASSES! Setting start to mid! " )
95
165
startIndex = mid
96
166
} else {
97
167
log ( " [INFO] FAILS! Setting end to mid " )
98
168
endIndex = mid
99
169
}
170
+
171
+ if midIsEndIndex {
172
+ log ( " Last successful value: \( tags [ mid+ 1 ] ) " )
173
+ break
174
+ }
100
175
}
101
176
}
102
177
}
0 commit comments