Commit 174b677412
Verified · cmc
Layout: unified · split
gitbay/Builds/BuildDetailViewModel.swift +8 −2
| @@ -5,12 +5,15 @@ nonisolated struct BuildDetail: Sendable { | |||
| 5 | let build: Build | 5 | let build: Build |
| 6 | let log: String | 6 | let log: String |
| 7 | let lines: BuildLog | 7 | let lines: BuildLog |
| 8 | /// Fetched with `--tail`. | ||
| 9 | let tail: Bool | ||
| 8 | 10 | ||
| 9 | /// `tail` is a log's last lines only, which may start inside a step | 11 | /// `tail` is a log's last lines only, which may start inside a step |
| 10 | /// and cut its marker off, so it is drawn as one block. | 12 | /// and cut its marker off, so it is drawn as one block. |
| 11 | init(build: Build, log: String, tail: Bool = false) { | 13 | init(build: Build, log: String, tail: Bool = false) { |
| 12 | self.build = build | 14 | self.build = build |
| 13 | self.log = log | 15 | self.log = log |
| 16 | self.tail = tail | ||
| 14 | var split = build | 17 | var split = build |
| 15 | if tail { | 18 | if tail { |
| 16 | split.steps = nil | 19 | split.steps = nil |
| @@ -49,7 +52,10 @@ final class BuildDetailViewModel { | |||
| 49 | 52 | ||
| 50 | func load() async { | 53 | func load() async { |
| 51 | do { | 54 | do { |
| 52 | state = .loaded(try await fetch()) | 55 | let detail = try await fetch() |
| 56 | // A fetch from before the mode changed is not this screen's. | ||
| 57 | guard detail.tail == tail else { return } | ||
| 58 | state = .loaded(detail) | ||
| 53 | } catch { | 59 | } catch { |
| 54 | state = .from(error) | 60 | state = .from(error) |
| 55 | } | 61 | } |
| @@ -83,7 +89,7 @@ final class BuildDetailViewModel { | |||
| 83 | func poll() async { | 89 | func poll() async { |
| 84 | while let build = state.value?.build, Self.isLive(status: build.status) { | 90 | while let build = state.value?.build, Self.isLive(status: build.status) { |
| 85 | guard (try? await Task.sleep(for: pollInterval)) != nil else { return } | 91 | guard (try? await Task.sleep(for: pollInterval)) != nil else { return } |
| 86 | if let detail = try? await fetch() { | 92 | if let detail = try? await fetch(), detail.tail == tail { |
| 87 | state = .loaded(detail) | 93 | state = .loaded(detail) |
| 88 | } | 94 | } |
| 89 | } | 95 | } |
gitbayTests/BuildLogTests.swift +11
| @@ -248,4 +248,15 @@ struct BuildLogTailTests { | |||
| 248 | #expect(stub.seen.last { $0.url.absoluteString.contains("argv=log") }?.url.query()?.contains("tail") == false) | 248 | #expect(stub.seen.last { $0.url.absoluteString.contains("argv=log") }?.url.query()?.contains("tail") == false) |
| 249 | #expect(model.state.value?.lines.stepped == true) | 249 | #expect(model.state.value?.lines.stepped == true) |
| 250 | } | 250 | } |
| 251 | |||
| 252 | @Test func aFetchFromTheOtherModeIsNotShown() async throws { | ||
| 253 | let (client, stub) = try makeClient() | ||
| 254 | stub.enqueue(.init(status: 200, json: buildShowJSON, match: "argv=show")) | ||
| 255 | stub.enqueue(.init(status: 200, json: logEnvelope(failedLog), match: "argv=log")) | ||
| 256 | let model = BuildDetailViewModel(client: client, repoPath: "krz/gitbay", number: 1) | ||
| 257 | await model.load() | ||
| 258 | // The detail records the mode it was fetched in. | ||
| 259 | #expect(model.state.value?.tail == false) | ||
| 260 | #expect(BuildDetail(build: try #require(model.state.value).build, log: "x\n", tail: true).tail) | ||
| 261 | } | ||
| 251 | } | 262 | } |