import Testing @_spi(SGTKInternal) import Gtk @_spi(Portico) import Portico import Adw @MainActor @Suite(.serialized) struct ConditionalTests { @Test func ifElseFlatMapsIntoParent() { guard Gtk.initCheck() else { return } let flag = StateBox(true) let box = AnyView(VStack(spacing: 12) { Label(str: "A") if Binding(flag).wrappedValue { Label(str: "B") } else { Label(str: "C") } Label(str: "D") }).makeWidget(MountContext()) as! Gtk.Box #expect(directLabels(of: box) == ["A", "B", "D"]) #expect(noWrapper(in: box)) flag.set(false) #expect(directLabels(of: box) == ["A", "C", "D"]) } @Test func firstFlipBuildsSecondBranchOnce() { guard Gtk.initCheck() else { return } ConditionProbe.reset() let flag = StateBox(true) let context = MountContext() _ = AnyView(VStack { if Binding(flag).wrappedValue { ProbeLabel(.first) } else { ProbeLabel(.second) } }).makeWidget(context) #expect(ConditionProbe.first == 1) #expect(ConditionProbe.second == 0) flag.set(false) #expect(ConditionProbe.first == 1) #expect(ConditionProbe.second == 1) } @Test func secondFlipRebuildsNothing() { guard Gtk.initCheck() else { return } ConditionProbe.reset() let flag = StateBox(true) let context = MountContext() _ = AnyView(VStack { if Binding(flag).wrappedValue { ProbeLabel(.first) } else { ProbeLabel(.second) } }).makeWidget(context) flag.set(false) flag.set(true) flag.set(false) #expect(ConditionProbe.first == 1) #expect(ConditionProbe.second == 1) } @Test func switchSelectsThirdCase() { guard Gtk.initCheck() else { return } let state = StateBox(Status.one) let binding = Binding(state) let box = AnyView(VStack { Label(str: "before") switch binding.wrappedValue { case .one: Label(str: "one") case .two: Label(str: "two") case .three: Label(str: "three") } Label(str: "after") }).makeWidget(MountContext()) as! Gtk.Box #expect(directLabels(of: box) == ["before", "one", "after"]) state.set(.three) #expect(directLabels(of: box) == ["before", "three", "after"]) state.set(.two) #expect(directLabels(of: box) == ["before", "two", "after"]) } @Test func nestedConditionalsOrderCorrectly() { guard Gtk.initCheck() else { return } let outer = StateBox(true) let inner = StateBox(false) let outerBinding = Binding(outer) let innerBinding = Binding(inner) let box = AnyView(VStack { Label(str: "A") if outerBinding.wrappedValue { if innerBinding.wrappedValue { Label(str: "B") } else { Label(str: "C") } Label(str: "D") } else { Label(str: "E") } Label(str: "F") }).makeWidget(MountContext()) as! Gtk.Box #expect(directLabels(of: box) == ["A", "C", "D", "F"]) inner.set(true) #expect(directLabels(of: box) == ["A", "B", "D", "F"]) outer.set(false) #expect(directLabels(of: box) == ["A", "E", "F"]) outer.set(true) #expect(directLabels(of: box) == ["A", "B", "D", "F"]) } @Test func ifLetBuildsOnFirstAvailability() { guard Gtk.initCheck() else { return } let payload = StateBox(nil) let binding = Binding(payload) let box = AnyView(VStack { if let value = binding.wrappedValue { Label(str: value) } }).makeWidget(MountContext()) as! Gtk.Box #expect(directLabels(of: box).isEmpty) payload.set("first") #expect(directLabels(of: box) == ["first"]) payload.set("second") #expect(directLabels(of: box) == ["first"]) } @Test func forLoopGrowsAndShrinks() { guard Gtk.initCheck() else { return } let count = StateBox(2) let binding = Binding(count) let box = AnyView(VStack { for index in 0.. [String] { var result: [String] = [] var child = box.getFirstChild() while let current = child { if current.getVisible() { result.append(Gtk.Label(retaining: current.pointer).getText()) } child = current.getNextSibling() } return result } private func noWrapper(in box: Gtk.Box) -> Bool { var child = box.getFirstChild() while let current = child { if current.cssName == "stack" || current.cssName == "revealer" { return false } child = current.getNextSibling() } return true } } private enum Status { case one, two, three } @MainActor private enum ConditionProbe { enum Branch { case first, second } static var first = 0 static var second = 0 static func reset() { first = 0; second = 0 } } @MainActor private struct ProbeLabel: View, Mountable { let branch: ConditionProbe.Branch var body: Never { fatalError() } init(_ branch: ConditionProbe.Branch) { self.branch = branch } func mount(_ ctx: MountContext) -> Gtk.Widget { switch branch { case .first: ConditionProbe.first += 1 case .second: ConditionProbe.second += 1 } return Gtk.Label(str: "probe") } }