Fix CI: extract computeState() for testability; make network init non-fatal

network.init() no longer panics when D-Bus is unavailable (logs warning
instead). This allows go test to run in the nix build sandbox.

checkState() now calls computeState() which returns the target state
without triggering page navigation (which requires OpenGL). Tests call
computeState() directly, avoiding the SIGSEGV from gl.GenVertexArrays.

7 regression tests for state machine, all passing.
This commit is contained in:
NeilClack
2026-07-11 14:42:49 -05:00
parent 3f8144a708
commit 363e6f81f6
3 changed files with 165 additions and 73 deletions
+38 -34
View File
@@ -201,12 +201,12 @@ func Run() {
app.runEventLoop()
}
// checkState keeps watch of the application state and sets pages
// accordingly. This function runs once per frame.
func (app *Application) checkState() {
// app.state is READY so do nothing.
// computeState determines what state the application should be in based on
// current conditions, without performing any navigation. It returns the first
// unmet prerequisite in priority order: network > email > license > downloads > ready.
func (app *Application) computeState() State {
if app.state == READY {
return
return READY
}
// Check if downloads have finished (non-blocking)
@@ -218,26 +218,20 @@ func (app *Application) checkState() {
}
}
// Check if there is an internet connection, and if not, force the WiFi page
// Check if there is an internet connection
if !networkIsUp {
if lastNetworkCheckTime.IsZero() || time.Since(lastNetworkCheckTime) >= time.Second {
lastNetworkCheckTime = time.Now()
if !network.HasInternet() {
if app.state != NEEDS_INTERNET {
app.PageRouter.NavigateTo(page.WIFI)
app.state = NEEDS_INTERNET
}
return
return NEEDS_INTERNET
}
networkIsUp = true // internet confirmed, don't check again
} else {
return // throttled, and we don't yet know if network is up — can't proceed
return app.state // throttled, can't determine yet
}
}
// Then check for an email address
// Check for email address
config, err := db.GetConfig(app.DB)
if err != nil {
panic(err)
@@ -251,38 +245,48 @@ func (app *Application) checkState() {
}
if config.EmailAddress == "" {
if app.state != NEEDS_EMAIL_ADDRESS {
logger.Warning("No email address detected")
app.PageRouter.NavigateTo(page.EMAIL_ADDRESS)
app.state = NEEDS_EMAIL_ADDRESS
}
return
return NEEDS_EMAIL_ADDRESS
}
// Then check for a license key
// Check for license key
config, err = db.GetConfig(app.DB)
if err != nil {
panic(err)
}
if config.LicenseKey == "" {
if app.state != NEEDS_LICENSE_KEY {
app.PageRouter.NavigateTo(page.LICENSE_KEY)
app.state = NEEDS_LICENSE_KEY
}
return
return NEEDS_LICENSE_KEY
}
// All config prerequisites met — now wait for downloads to finish
// All config prerequisites met -- now wait for downloads to finish
if !app.downloadsComplete {
if app.state != DOWNLOADING_ASSETS {
app.state = DOWNLOADING_ASSETS
app.PageRouter.NavigateTo(page.HOME)
}
return DOWNLOADING_ASSETS
}
return READY
}
// checkState keeps watch of the application state and sets pages
// accordingly. This function runs once per frame.
func (app *Application) checkState() {
newState := app.computeState()
if newState == app.state {
return
}
app.state = READY
app.PageRouter.NavigateTo(page.HOME)
app.state = newState
switch newState {
case NEEDS_INTERNET:
app.PageRouter.NavigateTo(page.WIFI)
case NEEDS_EMAIL_ADDRESS:
logger.Warning("No email address detected")
app.PageRouter.NavigateTo(page.EMAIL_ADDRESS)
case NEEDS_LICENSE_KEY:
app.PageRouter.NavigateTo(page.LICENSE_KEY)
case DOWNLOADING_ASSETS, READY:
app.PageRouter.NavigateTo(page.HOME)
}
}
func (app *Application) initializeSDLAndOpenGL() (W, H int32) {