Skip to content

Commit 22c2295

Browse files
committed
allow the script to control time
1 parent c46cdb0 commit 22c2295

File tree

6 files changed

+153
-43
lines changed

6 files changed

+153
-43
lines changed

core/settings/settings.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ func (s *Settings) showSettings() {
298298
s.view.SetMultisGoal(strconv.Itoa(s.contest.MultisGoal))
299299
}
300300

301+
func (s *Settings) RefreshView() {
302+
s.showSettings()
303+
}
304+
301305
func (s *Settings) formattedContestStartTime() string {
302306
if s.contest.StartTime.IsZero() {
303307
return ""

main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"os"
88

9-
"github.com/ftl/hellocontest/core/app"
109
"github.com/ftl/hellocontest/script"
1110
"github.com/ftl/hellocontest/ui"
1211
)
@@ -17,7 +16,7 @@ var version = "development"
1716
var sponsors string
1817

1918
func main() {
20-
var startupScript app.Script = nil
19+
var startupScript ui.Script = nil
2120
args := os.Args
2221

2322
if len(os.Args) > 1 {

script/clock.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package script
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"time"
7+
)
8+
9+
type Clock struct {
10+
offset time.Duration
11+
}
12+
13+
func (c *Clock) Now() time.Time {
14+
return time.Now().Add(c.offset)
15+
}
16+
17+
func (c *Clock) Set(now time.Time) {
18+
c.offset = now.Sub(time.Now())
19+
log.Printf("Clock offset set to %v, current time is %v", c.offset, c.Now())
20+
}
21+
22+
func (c *Clock) SetFromRFC3339(s string) {
23+
t, err := time.Parse(time.RFC3339, s)
24+
if err != nil {
25+
panic(fmt.Errorf("cannot set script clock: %w", err))
26+
}
27+
c.Set(t)
28+
}
29+
30+
func (c *Clock) Add(offset time.Duration) {
31+
c.offset += offset
32+
log.Printf("Clock offset adjusted to %v, current time is %v", c.offset, c.Now())
33+
}
34+
35+
func (c *Clock) Reset() {
36+
c.offset = 0
37+
log.Printf("Clock offset reset, current time is %v", c.Now())
38+
}

script/screenshots.go

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"path/filepath"
1010
"time"
1111

12-
"github.com/ftl/hellocontest/core/app"
12+
"github.com/ftl/hellocontest/core"
1313
)
1414

1515
const ScreenshotsFolder = "./docs/screenshots"
@@ -18,14 +18,15 @@ var ScreenshotsScript = &Script{
1818
sections: []*Section{
1919
{
2020
steps: []Step{
21+
SetTimebase("2023-06-28T19:00:00Z"),
2122
Wait(2 * time.Second),
2223
},
2324
},
2425
{
2526
enter: AskForScreenshot("about dialog", 1*time.Second),
2627
steps: []Step{
27-
func(_ context.Context, app *app.Controller, ui func(func())) time.Duration {
28-
ui(app.About)
28+
func(_ context.Context, r *Runtime) time.Duration {
29+
r.UI(r.App.About)
2930
return 0
3031
},
3132
TriggerScreenshot("about"),
@@ -49,34 +50,46 @@ var ScreenshotsScript = &Script{
4950
{
5051
enter: AskForScreenshot("new CWT, enter name CWT 2025 Test", 1*time.Second),
5152
steps: []Step{
52-
func(_ context.Context, app *app.Controller, ui func(func())) time.Duration {
53-
ui(app.New)
54-
ui(func() {
55-
app.NewContestController.SelectContestIdentifier("CW-OPS")
56-
app.NewContestController.EnterContestName("CWT 2025 Test")
57-
app.NewContestController.RefreshView()
53+
func(_ context.Context, r *Runtime) time.Duration {
54+
r.UI(r.App.New)
55+
r.UI(func() {
56+
r.App.NewContestController.SelectContestIdentifier("CW-OPS")
57+
r.App.NewContestController.EnterContestName("CWT Screenshot Demo")
58+
r.App.NewContestController.RefreshView()
5859
})
5960
return 0
6061
},
6162
TriggerScreenshot("new_cwt"),
62-
Describe("close the dialog with 'NEW', save the contest with the proposed filename", 10*time.Second),
63+
Describe("close the dialog with 'NEW', save the contest with the proposed filename\nthe settings dialog will show up, just wait for the next set of instructions", 10*time.Second),
64+
func(_ context.Context, r *Runtime) time.Duration {
65+
r.UI(func() {
66+
r.App.Settings.EnterStationCallsign("DL0ABC")
67+
r.App.Settings.EnterStationOperator("DL1ABC")
68+
r.App.Settings.EnterStationLocator("AA00xx")
69+
r.App.Settings.SetContestStartTimeNow()
70+
r.App.Settings.EnterContestExchangeValue(core.EntryField("myExchange_1"), "Walter")
71+
r.App.Settings.EnterContestExchangeValue(core.EntryField("myExchange_2"), "DL")
72+
r.App.Settings.RefreshView()
73+
})
74+
return 0
75+
},
6376
Describe("set the current hour as start time, select a current call history file", 20*time.Second),
6477
Describe("contest settings dialog, complete", 1*time.Second),
6578
TriggerScreenshot("contest_settings_complete"),
6679
Describe("contest settings dialog, section 'My Exchange' with name Flo and dxcc_prefix DL", 10*time.Second),
6780
TriggerScreenshot("contest_settings_myexchange_cwt"),
68-
Describe("close the contest settings dialog, screenshot of empty main window", 5*time.Second),
81+
Describe("close the contest settings dialog, screenshot of empty main window", 10*time.Second),
6982
TriggerScreenshot("main_window_empty"),
7083
},
7184
},
7285
{
7386
enter: AskForScreenshot("main window QSO data entry", 0),
7487
steps: []Step{
75-
func(_ context.Context, app *app.Controller, ui func(func())) time.Duration {
76-
ui(func() {
77-
app.Entry.Clear()
78-
app.Entry.Enter("DL3NEY")
79-
app.Entry.RefreshView()
88+
func(_ context.Context, r *Runtime) time.Duration {
89+
r.UI(func() {
90+
r.App.Entry.Clear()
91+
r.App.Entry.Enter("DL3NEY")
92+
r.App.Entry.RefreshView()
8093
})
8194
return 0
8295
},
@@ -93,8 +106,8 @@ var ScreenshotsScript = &Script{
93106
{
94107
steps: []Step{
95108
Describe("all screenshots taken, closing the application", 0),
96-
func(_ context.Context, app *app.Controller, ui func(func())) time.Duration {
97-
ui(app.Quit)
109+
func(_ context.Context, r *Runtime) time.Duration {
110+
r.UI(r.App.Quit)
98111
return 0
99112
},
100113
},
@@ -111,7 +124,7 @@ func DescribeScreenshot(description string, delay time.Duration) Step {
111124
}
112125

113126
func ClearScreenshotsFolder() Step {
114-
return func(_ context.Context, _ *app.Controller, _ func(func())) time.Duration {
127+
return func(_ context.Context, _ *Runtime) time.Duration {
115128
log.Printf("[clearing screenshots folder]")
116129
d, err := os.Open(ScreenshotsFolder)
117130
if err != nil {
@@ -137,7 +150,7 @@ func ClearScreenshotsFolder() Step {
137150
}
138151

139152
func DeleteScreenshot(name string) Step {
140-
return func(_ context.Context, _ *app.Controller, _ func(func())) time.Duration {
153+
return func(_ context.Context, _ *Runtime) time.Duration {
141154
filename := filepath.Join(ScreenshotsFolder, name)
142155
err := os.RemoveAll(filename)
143156
if err != nil {
@@ -152,7 +165,7 @@ func TriggerScreenshot(filename string) Step {
152165
}
153166

154167
func TriggerScreenshotWithDelay(name string, delay time.Duration) Step {
155-
return func(_ context.Context, _ *app.Controller, _ func(func())) time.Duration {
168+
return func(_ context.Context, _ *Runtime) time.Duration {
156169
filename := filepath.Join(ScreenshotsFolder, name+".png")
157170
backupFilename := filepath.Join(ScreenshotsFolder, name+".bak.png")
158171
_ = backupFilename

script/script.go

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
)
1010

1111
type Script struct {
12+
clock *Clock
1213
sections []*Section
1314

1415
currentSection int
@@ -23,51 +24,72 @@ type Section struct {
2324
currentStep int
2425
}
2526

26-
type Step func(ctx context.Context, app *app.Controller, ui func(func())) time.Duration
27+
type Runtime struct {
28+
Clock *Clock
29+
App *app.Controller
30+
UI func(func())
31+
}
32+
33+
type Step func(ctx context.Context, r *Runtime) time.Duration
2734

28-
type Condition func(ctx context.Context, app *app.Controller, ui func(func())) (bool, time.Duration)
35+
type Condition func(ctx context.Context, r *Runtime) (bool, time.Duration)
36+
37+
func (s *Script) Now() time.Time {
38+
if s.clock == nil {
39+
return time.Now()
40+
}
41+
return s.clock.Now()
42+
}
2943

3044
func (s *Script) Step(ctx context.Context, app *app.Controller, ui func(func())) bool {
3145
if s.currentSection >= len(s.sections) {
3246
return false
3347
}
48+
if s.clock == nil {
49+
s.clock = &Clock{}
50+
}
3451

3552
section := s.sections[s.currentSection]
53+
runtime := &Runtime{
54+
Clock: s.clock,
55+
App: app,
56+
UI: ui,
57+
}
3658

37-
cont := section.Step(ctx, app, ui)
59+
cont := section.Step(ctx, runtime)
3860
if !cont {
3961
s.currentSection += 1
4062
}
4163

4264
return true
4365
}
4466

45-
func (s *Section) Step(ctx context.Context, app *app.Controller, ui func(func())) bool {
67+
func (s *Section) Step(ctx context.Context, r *Runtime) bool {
4668
if s.currentStep == 0 {
47-
return s.checkEntryCondition(ctx, app, ui)
69+
return s.checkEntryCondition(ctx, r)
4870
}
4971

5072
// TODO: execute the alternative if checkEntryCondition returns false
5173

52-
return s.executeStep(ctx, app, ui)
74+
return s.executeStep(ctx, r)
5375
}
5476

55-
func (s *Section) checkEntryCondition(ctx context.Context, app *app.Controller, ui func(func())) bool {
77+
func (s *Section) checkEntryCondition(ctx context.Context, r *Runtime) bool {
5678
s.currentStep += 1
5779
if s.enter == nil {
5880
return true
5981
}
6082

6183
now := time.Now()
62-
enter, delay := s.enter(ctx, app, ui)
84+
enter, delay := s.enter(ctx, r)
6385
if delay != 0 {
6486
s.waitUntil = now.Add(delay)
6587
}
6688

6789
return enter
6890
}
6991

70-
func (s *Section) executeStep(ctx context.Context, app *app.Controller, ui func(func())) bool {
92+
func (s *Section) executeStep(ctx context.Context, r *Runtime) bool {
7193
now := time.Now()
7294
if now.Before(s.waitUntil) {
7395
return true
@@ -79,30 +101,30 @@ func (s *Section) executeStep(ctx context.Context, app *app.Controller, ui func(
79101

80102
step := s.steps[s.currentStep-1]
81103

82-
nextDuration := step(ctx, app, ui)
104+
nextDuration := step(ctx, r)
83105
s.waitUntil = now.Add(nextDuration)
84106
s.currentStep += 1
85107

86108
return true
87109
}
88110

89111
func Ask(description string, delay time.Duration) Condition {
90-
return func(_ context.Context, app *app.Controller, ui func(func())) (bool, time.Duration) {
112+
return func(_ context.Context, r *Runtime) (bool, time.Duration) {
91113
enter := make(chan bool, 1)
92114
defer close(enter)
93-
ui(func() {
94-
enter <- app.ShowQuestion("%s\n\nin %v", description, delay)
115+
r.UI(func() {
116+
enter <- r.App.ShowQuestion("%s\n\nin %v", description, delay)
95117
})
96118
doEnter := <-enter
97119
return doEnter, delay
98120
}
99121
}
100122

101123
func Describe(description string, delay time.Duration) Step {
102-
return func(_ context.Context, app *app.Controller, ui func(func())) time.Duration {
124+
return func(_ context.Context, r *Runtime) time.Duration {
103125
ready := make(chan struct{})
104-
ui(func() {
105-
app.ShowInfo("%s\n\nin %v", description, delay)
126+
r.UI(func() {
127+
r.App.ShowInfo("%s\n\nin %v", description, delay)
106128
close(ready)
107129
})
108130
<-ready
@@ -111,8 +133,29 @@ func Describe(description string, delay time.Duration) Step {
111133
}
112134

113135
func Wait(duration time.Duration) Step {
114-
return func(context.Context, *app.Controller, func(func())) time.Duration {
136+
return func(context.Context, *Runtime) time.Duration {
115137
log.Printf("[WAITING FOR %v]", duration)
116138
return duration
117139
}
118140
}
141+
142+
func SetTimebase(timebase string) Step {
143+
return func(ctx context.Context, r *Runtime) time.Duration {
144+
r.Clock.SetFromRFC3339(timebase)
145+
return 0
146+
}
147+
}
148+
149+
func AddTimebaseOffset(offset time.Duration) Step {
150+
return func(ctx context.Context, r *Runtime) time.Duration {
151+
r.Clock.Add(offset)
152+
return 0
153+
}
154+
}
155+
156+
func ResetTimebase() Step {
157+
return func(ctx context.Context, r *Runtime) time.Duration {
158+
r.Clock.Reset()
159+
return 0
160+
}
161+
}

0 commit comments

Comments
 (0)