ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci / checks (push) Successful in 10m24s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 12m13s
ci / release (push) Successful in 8m57s
modules/update links aego/update (net/http, TLS, x509, archives) only off js; the web build gets a browser backend with the same section, settings and aego/update API, where Check and Apply fail and State stays idle. A test compares both builds' registrations. depcheck: wasm-runtime and wasm-try variants, update in wasmDenied, wasmDeniedStd (net/http, crypto/tls, crypto/x509, archive/*) with the wasm-reaches-std rule. wasmsize -prev: playground-size and try-size print the change since their last run. Loaders show the compressed download size: wasmGzip in an export's files.json, aego-*-gzip and data-wasm-gzip on the site; docstool writes the .wasm.gz copies. Demo game.wasm 33.9 -> 26.0 MB, gzip 8.07 -> 6.25 MB.
136 lines
4.2 KiB
Go
136 lines
4.2 KiB
Go
//go:build !js
|
|
|
|
package update
|
|
|
|
import (
|
|
"archive/tar"
|
|
"compress/gzip"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
upd "aego/update"
|
|
)
|
|
|
|
// An exported game finds its new version, downloads it in Auto mode, swaps its folder and starts the new one.
|
|
func TestGameUpdatesItsFolder(t *testing.T) {
|
|
if runtime.GOOS == "windows" {
|
|
t.Skip("a game folder updates through its installer on Windows")
|
|
}
|
|
pub, priv, _ := upd.GenerateKey()
|
|
web := t.TempDir()
|
|
marker := filepath.Join(t.TempDir(), "started")
|
|
script := "#!/bin/sh\necho new > " + marker + "\n"
|
|
f, _ := os.Create(filepath.Join(web, "game-1.1.0-linux-amd64.tar.gz"))
|
|
gz := gzip.NewWriter(f)
|
|
tw := tar.NewWriter(gz)
|
|
tw.WriteHeader(&tar.Header{Name: "game/", Typeflag: tar.TypeDir, Mode: 0o755})
|
|
tw.WriteHeader(&tar.Header{Name: "game/game", Typeflag: tar.TypeReg, Mode: 0o755, Size: int64(len(script))})
|
|
tw.Write([]byte(script))
|
|
tw.Close()
|
|
gz.Close()
|
|
f.Close()
|
|
var release, sig []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/stable/release.json":
|
|
w.Write(release)
|
|
case "/stable/release.json.sig":
|
|
w.Write(sig)
|
|
default:
|
|
http.ServeFile(w, r, filepath.Join(web, filepath.Base(r.URL.Path)))
|
|
}
|
|
}))
|
|
defer srv.Close()
|
|
sum, size, _ := upd.Sum(filepath.Join(web, "game-1.1.0-linux-amd64.tar.gz"))
|
|
release, _ = upd.Encode(&upd.Release{Version: "1.1.0", Channel: upd.Stable, Notes: "faster",
|
|
Assets: []upd.Asset{{OS: runtime.GOOS, Arch: runtime.GOARCH, Kind: upd.KindTarball, URL: srv.URL + "/game-1.1.0-linux-amd64.tar.gz", Size: size, SHA256: sum}}})
|
|
sig, _ = upd.Sign(release, priv)
|
|
|
|
dir := filepath.Join(t.TempDir(), "game")
|
|
os.MkdirAll(dir, 0o755)
|
|
os.WriteFile(filepath.Join(dir, "game"), []byte("#!/bin/sh\n"), 0o755)
|
|
t.Setenv("XDG_CACHE_HOME", t.TempDir())
|
|
m := &Module{sec: Section{URL: srv.URL, PublicKey: pub}, ver: "1.0.0", game: "game", b: &native{in: upd.DetectGame(filepath.Join(dir, "game"))}}
|
|
m.Check()
|
|
deadline := time.Now().Add(10 * time.Second)
|
|
for {
|
|
p, v, notes, msg := m.State()
|
|
if p == Ready {
|
|
if v != "1.1.0" || notes != "faster" {
|
|
t.Fatalf("ready with %q %q", v, notes)
|
|
}
|
|
break
|
|
}
|
|
if p == Failed || time.Now().After(deadline) {
|
|
t.Fatalf("the update did not download: %v %s", p, msg)
|
|
}
|
|
time.Sleep(20 * time.Millisecond)
|
|
}
|
|
if err := m.Apply(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for time.Now().Before(deadline) {
|
|
if b, err := os.ReadFile(marker); err == nil && string(b) == "new\n" {
|
|
if _, err := os.Stat(previousOf(dir)); err != nil {
|
|
t.Fatal("the old folder was not kept")
|
|
}
|
|
return
|
|
}
|
|
time.Sleep(20 * time.Millisecond)
|
|
}
|
|
t.Fatal("the new version did not start")
|
|
}
|
|
|
|
// A release signed by another key never downloads.
|
|
func TestGameRefusesAnotherKey(t *testing.T) {
|
|
other, _, _ := upd.GenerateKey()
|
|
_, priv, _ := upd.GenerateKey()
|
|
release, _ := upd.Encode(&upd.Release{Version: "9.0.0", Channel: upd.Stable})
|
|
sig, _ := upd.Sign(release, priv)
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/stable/release.json" {
|
|
w.Write(release)
|
|
return
|
|
}
|
|
w.Write(sig)
|
|
}))
|
|
defer srv.Close()
|
|
m := &Module{sec: Section{URL: srv.URL, PublicKey: other}, ver: "1.0.0", b: &native{}}
|
|
m.Check()
|
|
deadline := time.Now().Add(5 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
if p, _, _, msg := m.State(); p == Failed {
|
|
if msg != upd.ErrUnsigned.Error() {
|
|
t.Fatalf("failed with %q", msg)
|
|
}
|
|
return
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
t.Fatal("a release signed by another key was not refused")
|
|
}
|
|
|
|
func previousOf(path string) string {
|
|
return filepath.Join(filepath.Dir(path), "."+filepath.Base(path)+".previous")
|
|
}
|
|
|
|
// The settings name their choices the way the update core parses them.
|
|
func TestSettingChoicesMatchCore(t *testing.T) {
|
|
d := Settings()
|
|
for i, want := range []string{string(upd.Auto), string(upd.Ask), string(upd.Off)} {
|
|
if d[0].Choices[i].Name != want {
|
|
t.Fatalf("mode choice %d is %q", i, d[0].Choices[i].Name)
|
|
}
|
|
}
|
|
for i, want := range []string{string(upd.Stable), string(upd.Nightly)} {
|
|
if d[1].Choices[i].Name != want {
|
|
t.Fatalf("channel choice %d is %q", i, d[1].Choices[i].Name)
|
|
}
|
|
}
|
|
}
|