@@ -27,15 +27,42 @@ struct BisectToolchains: AsyncParsableCommand {
2727 """ )
2828 var script : String
2929
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+ }
3242
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+ }
3559
3660 @Flag ( help: " Invert the test so that we assume the newest succeeds " )
3761 var invert = false
3862
63+ @Argument ( help: " Extra constant arguments to pass to the test " )
64+ var extraArgs : [ String ] = [ ]
65+
3966 mutating func run( ) async throws {
4067 if !FileManager. default. fileExists ( atPath: workspace) {
4168 do {
@@ -49,20 +76,25 @@ struct BisectToolchains: AsyncParsableCommand {
4976 }
5077
5178 // 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) " )
5687 fatalError ( )
5788 }
5889
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) " )
6395 fatalError ( )
6496 }
65- badTagIndex = n
97+ badTagIndex = b
6698 } else {
6799 badTagIndex = 0
68100 }
@@ -73,30 +105,73 @@ struct BisectToolchains: AsyncParsableCommand {
73105 fatalError ( )
74106 }
75107
76- log ( " [INFO] Testing \( totalTags) toolchains " )
77-
78108 var startIndex = goodTagIndex
79109 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 {
81146 let mid = ( startIndex + endIndex) / 2
147+
148+ let midValue = tags [ mid] . tag
82149 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 ) "
84151 )
85152 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)
87155
88156 var success = result == 0
89157 if self . invert {
90158 success = !success
91159 }
92160
161+ let midIsEndIndex = mid == endIndex
162+
93163 if success {
94164 log ( " [INFO] PASSES! Setting start to mid! " )
95165 startIndex = mid
96166 } else {
97167 log ( " [INFO] FAILS! Setting end to mid " )
98168 endIndex = mid
99169 }
170+
171+ if midIsEndIndex {
172+ log ( " Last successful value: \( tags [ mid+ 1 ] ) " )
173+ break
174+ }
100175 }
101176 }
102177}
0 commit comments