diff --git a/archive.go b/archive.go index f100f75..2d1688d 100644 --- a/archive.go +++ b/archive.go @@ -41,7 +41,13 @@ func (rendered RenderedBundle) Archive() ([]byte, error) { writer, err := archive.CreateHeader(header) if err != nil { - _ = archive.Close() + if closeErr := archive.Close(); closeErr != nil { + return nil, WriteError{ + Kind: "bundle/archive-close-failed", + Path: rootPath, + Message: closeErr.Error(), + } + } return nil, WriteError{ Kind: "bundle/archive-file-create-failed", Path: file.Path, @@ -49,7 +55,13 @@ func (rendered RenderedBundle) Archive() ([]byte, error) { } } if _, err := writer.Write(file.Data); err != nil { - _ = archive.Close() + if closeErr := archive.Close(); closeErr != nil { + return nil, WriteError{ + Kind: "bundle/archive-close-failed", + Path: rootPath, + Message: closeErr.Error(), + } + } return nil, WriteError{ Kind: "bundle/archive-file-write-failed", Path: file.Path, diff --git a/ax7_compliance_test.go b/ax7_compliance_test.go new file mode 100644 index 0000000..169b822 --- /dev/null +++ b/ax7_compliance_test.go @@ -0,0 +1,2190 @@ +package play + +import core "dappco.re/go" + +type stubengine struct { + name string + platforms []string + verifyErr error +} + +func (engine stubengine) Name() string { + return engine.name +} + +func (engine stubengine) Platforms() []string { + return clonePaths(engine.platforms) +} + +func (engine stubengine) Run(string, EngineConfig) error { + return engine.verifyErr +} + +func (engine stubengine) Verify() error { + return engine.verifyErr +} + +type failingwriter struct { + failEnsure bool + failWrite bool +} + +func (writer failingwriter) EnsureDirectory(string) error { + if writer.failEnsure { + return core.NewError("ensure failed") + } + + return nil +} + +func (writer failingwriter) WriteFile(string, []byte) error { + if writer.failWrite { + return core.NewError("write failed") + } + + return nil +} + +func ax7ProcessCore(output string) *core.Core { + c := core.New() + c.Action("process.run", func(core.Context, core.Options) core.Result { + return core.Ok(output) + }) + + return c +} + +func ax7EngineConfig(profile string) EngineConfig { + return EngineConfig{ + Core: ax7ProcessCore("process ok"), + Context: core.Background(), + Profile: profile, + Output: core.NewBuffer(), + } +} + +func ax7Bundle(testingT *core.T, filesystem core.FS) Bundle { + testingT.Helper() + + bundle, err := LoadBundle(filesystem, ".") + core.RequireNoError(testingT, err) + + return bundle +} + +func ax7Registry(testingT *core.T, engine Engine) *Registry { + testingT.Helper() + + registry := NewRegistry() + core.RequireNoError(testingT, registry.Register(engine)) + + return registry +} + +func ax7BundleRequest(name string) BundleRequest { + return BundleRequest{ + Name: name, + Title: "AX7 Bundle", + Platform: "synthetic", + Licence: "freeware", + Engine: "synthetic", + ArtefactPath: "rom/game.bin", + ArtefactData: []byte("rom"), + ArtefactSHA256: hashHex([]byte("rom")), + ArtefactSize: 3, + } +} + +func TestAX7_RenderedBundle_Archive_Good(testingT *core.T) { + rendered := renderedArchiveBundle(testingT) + data, err := rendered.Archive() + core.AssertNoError(testingT, err) + core.AssertNotEmpty(testingT, data) +} + +func TestAX7_RenderedBundle_Archive_Bad(testingT *core.T) { + rendered := RenderedBundle{Path: "", Files: []RenderedFile{{Path: "manifest.yaml", Data: []byte("x")}}} + data, err := rendered.Archive() + core.AssertError(testingT, err, "bundle/path-required") + core.AssertNil(testingT, data) +} + +func TestAX7_RenderedBundle_Archive_Ugly(testingT *core.T) { + rendered := RenderedBundle{Path: "game", Files: []RenderedFile{{Path: "../escape", Data: []byte("x")}}} + data, err := rendered.Archive() + core.AssertError(testingT, err, "bundle/file-path-invalid") + core.AssertNil(testingT, data) +} + +func TestAX7_Bundle_Validate_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, validBundleFS()) + issues := bundle.Validate() + core.AssertFalse(testingT, issues.HasIssues()) +} + +func TestAX7_Bundle_Validate_Bad(testingT *core.T) { + bundle := Bundle{} + issues := bundle.Validate() + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_Bundle_Validate_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, validBundleFS()) + bundle.Manifest.Artefact.Path = "../escape" + issues := bundle.Validate() + core.AssertTrue(testingT, hasIssueCode(issues, "manifest/artefact-path-invalid")) +} + +func TestAX7_PathError_Error_Good(testingT *core.T) { + err := PathError{Kind: "bundle/missing", Path: "games/demo", Message: "not found"} + got := err.Error() + core.AssertContains(testingT, got, "games/demo") +} + +func TestAX7_PathError_Error_Bad(testingT *core.T) { + err := PathError{Kind: "bundle/missing", Message: "not found"} + got := err.Error() + core.AssertEqual(testingT, "bundle/missing: : not found", got) +} + +func TestAX7_PathError_Error_Ugly(testingT *core.T) { + err := PathError{} + got := err.Error() + core.AssertContains(testingT, got, ": ") +} + +func TestAX7_Catalogue_Walk_Good(testingT *core.T) { + catalogue := Catalogue{Bundles: catalogueBundleFS(testingT), Registry: ax7Registry(testingT, SyntheticEngine{})} + summaries, err := catalogue.Walk(".") + core.AssertNoError(testingT, err) + core.AssertLen(testingT, summaries, 2) +} + +func TestAX7_Catalogue_Walk_Bad(testingT *core.T) { + catalogue := Catalogue{} + summaries, err := catalogue.Walk(".") + core.AssertError(testingT, err, "bundle/filesystem-missing") + core.AssertNil(testingT, summaries) +} + +func TestAX7_Catalogue_Walk_Ugly(testingT *core.T) { + catalogue := Catalogue{Bundles: catalogueBundleFS(testingT), BasePath: "library", Registry: ax7Registry(testingT, SyntheticEngine{})} + summaries, err := catalogue.Walk(".") + core.AssertNoError(testingT, err) + core.AssertContains(testingT, summaries[0].Path, "library") +} + +func TestAX7_Catalogue_Print_Good(testingT *core.T) { + output := core.NewBuffer() + err := (Catalogue{}).Print(output, []BundleSummary{{Name: "demo", Title: "Demo", Platform: "synthetic", Engine: "synthetic"}}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "demo") +} + +func TestAX7_Catalogue_Print_Bad(testingT *core.T) { + output := core.NewBuffer() + err := (Catalogue{}).Print(output, nil) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "NAME") +} + +func TestAX7_Catalogue_Print_Ugly(testingT *core.T) { + output := core.NewBuffer() + err := (Catalogue{}).Print(output, []BundleSummary{{Name: "long-name", Size: 42, Verified: true}}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "[Y]") +} + +func TestAX7_Catalogue_PrintJSON_Good(testingT *core.T) { + output := core.NewBuffer() + err := (Catalogue{}).PrintJSON(output, []BundleSummary{{Name: "demo"}}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "demo") +} + +func TestAX7_Catalogue_PrintJSON_Bad(testingT *core.T) { + output := core.NewBuffer() + err := (Catalogue{}).PrintJSON(output, nil) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "null") +} + +func TestAX7_Catalogue_PrintJSON_Ugly(testingT *core.T) { + output := core.NewBuffer() + err := (Catalogue{}).PrintJSON(output, []BundleSummary{{Name: "demo", Size: 1}}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "\"size\":1") +} + +func TestAX7_EngineError_Error_Good(testingT *core.T) { + err := EngineError{Kind: "engine/binary-required", Name: "retroarch", Message: "binary path is required"} + got := err.Error() + core.AssertContains(testingT, got, "retroarch") +} + +func TestAX7_EngineError_Error_Bad(testingT *core.T) { + err := EngineError{Kind: "engine/nil", Message: "engine is required"} + got := err.Error() + core.AssertEqual(testingT, "engine/nil: engine is required", got) +} + +func TestAX7_EngineError_Error_Ugly(testingT *core.T) { + err := EngineError{} + got := err.Error() + core.AssertContains(testingT, got, ": ") +} + +func TestAX7_NewRegistry_Good(testingT *core.T) { + registry := NewRegistry() + names := registry.Names() + core.AssertEmpty(testingT, names) +} + +func TestAX7_NewRegistry_Bad(testingT *core.T) { + registry := NewRegistry() + _, found := registry.Resolve("missing") + core.AssertFalse(testingT, found) +} + +func TestAX7_NewRegistry_Ugly(testingT *core.T) { + first := NewRegistry() + second := NewRegistry() + core.AssertNotEqual(testingT, core.Sprintf("%p", first), core.Sprintf("%p", second)) +} + +func TestAX7_Registry_Register_Good(testingT *core.T) { + registry := NewRegistry() + err := registry.Register(stubengine{name: "ax7-registry-good", platforms: []string{"synthetic"}}) + core.AssertNoError(testingT, err) +} + +func TestAX7_Registry_Register_Bad(testingT *core.T) { + registry := NewRegistry() + err := registry.Register(nil) + core.AssertError(testingT, err, "engine/nil") +} + +func TestAX7_Registry_Register_Ugly(testingT *core.T) { + registry := NewRegistry() + core.RequireNoError(testingT, registry.Register(stubengine{name: "ax7-duplicate", platforms: []string{"synthetic"}})) + err := registry.Register(stubengine{name: "ax7-duplicate", platforms: []string{"synthetic"}}) + core.AssertError(testingT, err, "engine/duplicate") +} + +func TestAX7_Registry_Resolve_Good(testingT *core.T) { + registry := ax7Registry(testingT, stubengine{name: "ax7-resolve", platforms: []string{"synthetic"}}) + engine, found := registry.Resolve("ax7-resolve") + core.AssertTrue(testingT, found) + core.AssertEqual(testingT, "ax7-resolve", engine.Name()) +} + +func TestAX7_Registry_Resolve_Bad(testingT *core.T) { + registry := NewRegistry() + engine, found := registry.Resolve("missing") + core.AssertFalse(testingT, found) + core.AssertNil(testingT, engine) +} + +func TestAX7_Registry_Resolve_Ugly(testingT *core.T) { + var registry *Registry + engine, found := registry.Resolve("missing") + core.AssertFalse(testingT, found) + core.AssertNil(testingT, engine) +} + +func TestAX7_Registry_Names_Good(testingT *core.T) { + registry := NewRegistry() + core.RequireNoError(testingT, registry.Register(stubengine{name: "b", platforms: []string{"synthetic"}})) + core.RequireNoError(testingT, registry.Register(stubengine{name: "a", platforms: []string{"synthetic"}})) + core.AssertEqual(testingT, []string{"a", "b"}, registry.Names()) +} + +func TestAX7_Registry_Names_Bad(testingT *core.T) { + registry := NewRegistry() + names := registry.Names() + core.AssertEmpty(testingT, names) +} + +func TestAX7_Registry_Names_Ugly(testingT *core.T) { + var registry *Registry + names := registry.Names() + core.AssertNil(testingT, names) +} + +func TestAX7_RegisterEngine_Good(testingT *core.T) { + err := RegisterEngine(stubengine{name: "ax7-register-engine-good", platforms: []string{"synthetic"}}) + core.AssertNoError(testingT, err) + _, found := ResolveEngine("ax7-register-engine-good") + core.AssertTrue(testingT, found) +} + +func TestAX7_RegisterEngine_Bad(testingT *core.T) { + err := RegisterEngine(nil) + core.AssertError(testingT, err, "engine/nil") + core.AssertNotNil(testingT, err) +} + +func TestAX7_RegisterEngine_Ugly(testingT *core.T) { + err := RegisterEngine(stubengine{name: "", platforms: []string{"synthetic"}}) + core.AssertError(testingT, err, "engine/name-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_ResolveEngine_Good(testingT *core.T) { + core.RequireNoError(testingT, RegisterEngine(stubengine{name: "ax7-resolve-engine-good", platforms: []string{"synthetic"}})) + engine, found := ResolveEngine("ax7-resolve-engine-good") + core.AssertTrue(testingT, found) + core.AssertEqual(testingT, "ax7-resolve-engine-good", engine.Name()) +} + +func TestAX7_ResolveEngine_Bad(testingT *core.T) { + engine, found := ResolveEngine("ax7-resolve-engine-missing") + core.AssertFalse(testingT, found) + core.AssertNil(testingT, engine) +} + +func TestAX7_ResolveEngine_Ugly(testingT *core.T) { + engine, found := ResolveEngine("") + core.AssertFalse(testingT, found) + core.AssertNil(testingT, engine) +} + +func TestAX7_RegisteredEngines_Good(testingT *core.T) { + core.RequireNoError(testingT, RegisterEngine(stubengine{name: "ax7-registered-engines-good", platforms: []string{"synthetic"}})) + names := RegisteredEngines() + core.AssertContains(testingT, names, "ax7-registered-engines-good") +} + +func TestAX7_RegisteredEngines_Bad(testingT *core.T) { + names := RegisteredEngines() + core.AssertNotContains(testingT, names, "ax7-registered-engines-missing") + core.AssertNotNil(testingT, names) +} + +func TestAX7_RegisteredEngines_Ugly(testingT *core.T) { + first := RegisteredEngines() + second := RegisteredEngines() + core.AssertEqual(testingT, first, second) +} + +func TestAX7_FrameBuffer_Clone_Good(testingT *core.T) { + frame := validRGBAFrame() + clone := frame.Clone() + core.AssertEqual(testingT, frame.Width, clone.Width) + core.AssertEqual(testingT, len(frame.Data), len(clone.Data)) +} + +func TestAX7_FrameBuffer_Clone_Bad(testingT *core.T) { + frame := FrameBuffer{} + clone := frame.Clone() + core.AssertNil(testingT, clone.Data) +} + +func TestAX7_FrameBuffer_Clone_Ugly(testingT *core.T) { + frame := validRGBAFrame() + clone := frame.Clone() + frame.Data[0] = 9 + core.AssertNotEqual(testingT, frame.Data[0], clone.Data[0]) +} + +func TestAX7_FrameBuffer_Validate_Good(testingT *core.T) { + frame := validRGBAFrame() + issues := frame.Validate() + core.AssertFalse(testingT, issues.HasIssues()) +} + +func TestAX7_FrameBuffer_Validate_Bad(testingT *core.T) { + frame := FrameBuffer{} + issues := frame.Validate() + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_FrameBuffer_Validate_Ugly(testingT *core.T) { + frame := validRGBAFrame() + frame.Format = PixelFormat("bad") + issues := frame.Validate() + core.AssertTrue(testingT, hasIssueCode(issues, "frame/format-invalid")) +} + +func TestAX7_ResourceLimits_IsZero_Good(testingT *core.T) { + limits := ResourceLimits{} + got := limits.IsZero() + core.AssertTrue(testingT, got) +} + +func TestAX7_ResourceLimits_IsZero_Bad(testingT *core.T) { + limits := ResourceLimits{CPUPercent: 1} + got := limits.IsZero() + core.AssertFalse(testingT, got) +} + +func TestAX7_ResourceLimits_IsZero_Ugly(testingT *core.T) { + limits := ResourceLimits{MemoryBytes: -1} + got := limits.IsZero() + core.AssertFalse(testingT, got) +} + +func TestAX7_ManifestMigration_Migrated_Good(testingT *core.T) { + migration := ManifestMigration{Applied: []string{"manifest/legacy-format-version"}} + got := migration.Migrated() + core.AssertTrue(testingT, got) +} + +func TestAX7_ManifestMigration_Migrated_Bad(testingT *core.T) { + migration := ManifestMigration{} + got := migration.Migrated() + core.AssertFalse(testingT, got) +} + +func TestAX7_ManifestMigration_Migrated_Ugly(testingT *core.T) { + migration := ManifestMigration{Applied: []string{""}} + got := migration.Migrated() + core.AssertTrue(testingT, got) +} + +func TestAX7_MigrateManifest_Good(testingT *core.T) { + manifest := Manifest{Name: "demo", Verification: Verification{Chain: "checksums.sha256"}} + migrated, migration, err := MigrateManifest(manifest) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, CurrentManifestFormatVersion, migrated.FormatVersion) + core.AssertTrue(testingT, migration.Migrated()) +} + +func TestAX7_MigrateManifest_Bad(testingT *core.T) { + manifest := Manifest{FormatVersion: "future"} + _, _, err := MigrateManifest(manifest) + core.AssertError(testingT, err, "manifest/format-version-unsupported") +} + +func TestAX7_MigrateManifest_Ugly(testingT *core.T) { + manifest := Manifest{FormatVersion: CurrentManifestFormatVersion, Preservation: Preservation{Chain: "chain.txt"}} + migrated, migration, err := MigrateManifest(manifest) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "chain.txt", migrated.Verification.Chain) + core.AssertTrue(testingT, migration.Migrated()) +} + +func TestAX7_ParseError_Error_Good(testingT *core.T) { + err := ParseError{Kind: "manifest/multiple-documents", Message: "manifest must contain exactly one YAML document"} + got := err.Error() + core.AssertContains(testingT, got, "manifest/multiple-documents") +} + +func TestAX7_ParseError_Error_Bad(testingT *core.T) { + err := ParseError{Kind: "manifest/empty", Message: "empty manifest"} + got := err.Error() + core.AssertEqual(testingT, "manifest/empty: empty manifest", got) +} + +func TestAX7_ParseError_Error_Ugly(testingT *core.T) { + err := ParseError{} + got := err.Error() + core.AssertContains(testingT, got, ": ") +} + +func TestAX7_FramePipeline_Process_Good(testingT *core.T) { + pipeline := FramePipeline{Primary: acceleratedFrameProcessor{name: "metal", available: true}} + result, err := pipeline.Process(validRGBAFrame(), FramePolicy{Mode: AccelerationAuto}) + core.AssertNoError(testingT, err) + core.AssertTrue(testingT, result.Accelerated) +} + +func TestAX7_FramePipeline_Process_Bad(testingT *core.T) { + pipeline := FramePipeline{} + _, err := pipeline.Process(FrameBuffer{}, FramePolicy{}) + core.AssertError(testingT, err, "frame/") +} + +func TestAX7_FramePipeline_Process_Ugly(testingT *core.T) { + pipeline := FramePipeline{Primary: acceleratedFrameProcessor{name: "metal", available: false}, Fallback: acceleratedFrameProcessor{name: "cpu"}} + result, err := pipeline.Process(validRGBAFrame(), FramePolicy{Mode: AccelerationAuto}) + core.AssertNoError(testingT, err) + core.AssertTrue(testingT, result.Fallback) +} + +func TestAX7_FrameProcessor_Name_Good(testingT *core.T) { + processor := identityFrameProcessor{} + got := processor.Name() + core.AssertEqual(testingT, "identity", got) +} + +func TestAX7_FrameProcessor_Name_Bad(testingT *core.T) { + processor := identityFrameProcessor{} + got := processor.Name() + core.AssertNotEqual(testingT, "metal", got) +} + +func TestAX7_FrameProcessor_Name_Ugly(testingT *core.T) { + processor := identityFrameProcessor{} + first := processor.Name() + second := processor.Name() + core.AssertEqual(testingT, first, second) +} + +func TestAX7_FrameProcessor_Available_Good(testingT *core.T) { + processor := identityFrameProcessor{} + got := processor.Available() + core.AssertTrue(testingT, got) +} + +func TestAX7_FrameProcessor_Available_Bad(testingT *core.T) { + processor := acceleratedFrameProcessor{name: "metal", available: false} + got := processor.Available() + core.AssertFalse(testingT, got) +} + +func TestAX7_FrameProcessor_Available_Ugly(testingT *core.T) { + processor := identityFrameProcessor{} + first := processor.Available() + second := processor.Available() + core.AssertEqual(testingT, first, second) +} + +func TestAX7_FrameProcessor_Supports_Good(testingT *core.T) { + processor := identityFrameProcessor{} + got := processor.Supports(validRGBAFrame(), FramePolicy{}) + core.AssertTrue(testingT, got) +} + +func TestAX7_FrameProcessor_Supports_Bad(testingT *core.T) { + processor := acceleratedFrameProcessor{name: "metal", available: true} + got := processor.Supports(FrameBuffer{}, FramePolicy{}) + core.AssertTrue(testingT, got) +} + +func TestAX7_FrameProcessor_Supports_Ugly(testingT *core.T) { + processor := identityFrameProcessor{} + got := processor.Supports(FrameBuffer{}, FramePolicy{Mode: AccelerationRequired}) + core.AssertTrue(testingT, got) +} + +func TestAX7_FrameProcessor_Process_Good(testingT *core.T) { + processor := identityFrameProcessor{} + frame, err := processor.Process(validRGBAFrame(), FramePolicy{}) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, 320, frame.Width) +} + +func TestAX7_FrameProcessor_Process_Bad(testingT *core.T) { + processor := acceleratedFrameProcessor{processErr: core.NewError("processor failed")} + _, err := processor.Process(validRGBAFrame(), FramePolicy{}) + core.AssertError(testingT, err, "processor failed") +} + +func TestAX7_FrameProcessor_Process_Ugly(testingT *core.T) { + processor := identityFrameProcessor{} + frame, err := processor.Process(FrameBuffer{}, FramePolicy{}) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, 0, frame.Width) +} + +func TestAX7_Manifest_FramePolicy_Good(testingT *core.T) { + manifest := Manifest{Runtime: Runtime{Acceleration: AccelerationRequired, Filter: FrameFilterCRT}} + policy := manifest.FramePolicy() + core.AssertEqual(testingT, AccelerationRequired, policy.Mode) + core.AssertEqual(testingT, FrameFilterCRT, policy.Filter) +} + +func TestAX7_Manifest_FramePolicy_Bad(testingT *core.T) { + manifest := Manifest{Runtime: Runtime{Acceleration: AccelerationMode("bad"), Filter: FrameFilter("bad")}} + policy := manifest.FramePolicy() + core.AssertEqual(testingT, AccelerationAuto, policy.Mode) + core.AssertEqual(testingT, FrameFilterNone, policy.Filter) +} + +func TestAX7_Manifest_FramePolicy_Ugly(testingT *core.T) { + manifest := Manifest{} + policy := manifest.FramePolicy() + core.AssertEqual(testingT, AccelerationAuto, policy.Mode) + core.AssertEqual(testingT, FrameFilterNone, policy.Filter) +} + +func TestAX7_PipelineError_Error_Good(testingT *core.T) { + err := PipelineError{Kind: "frame/fallback-failed", Message: "cpu failed"} + got := err.Error() + core.AssertContains(testingT, got, "cpu failed") +} + +func TestAX7_PipelineError_Error_Bad(testingT *core.T) { + err := PipelineError{Kind: "frame/acceleration-required", Message: "missing"} + got := err.Error() + core.AssertEqual(testingT, "frame/acceleration-required: missing", got) +} + +func TestAX7_PipelineError_Error_Ugly(testingT *core.T) { + err := PipelineError{} + got := err.Error() + core.AssertContains(testingT, got, ": ") +} + +func TestAX7_BundlePlan_Render_Good(testingT *core.T) { + plan, issues := NewService(nil, nil).PlanBundle(ax7BundleRequest("render-good")) + core.RequireTrue(testingT, !issues.HasIssues()) + rendered, err := plan.Render() + core.AssertNoError(testingT, err) + core.AssertNotEmpty(testingT, rendered.Files) +} + +func TestAX7_BundlePlan_Render_Bad(testingT *core.T) { + plan := BundlePlan{} + rendered, err := plan.Render() + core.AssertError(testingT, err, "manifest/") + core.AssertEmpty(testingT, rendered.Files) +} + +func TestAX7_BundlePlan_Render_Ugly(testingT *core.T) { + request := ax7BundleRequest("render-ugly") + request.EngineBinaryPath = "engine/synthetic" + request.EngineBinaryData = []byte("engine") + request.EngineBinarySHA256 = hashHex([]byte("engine")) + plan, issues := NewService(nil, nil).PlanBundle(request) + core.RequireTrue(testingT, !issues.HasIssues()) + rendered, err := plan.Render() + core.AssertNoError(testingT, err) + core.AssertNotNil(testingT, renderedFileData(rendered, "engine/synthetic")) +} + +func TestAX7_SandboxPolicy_ValidateLaunch_Good(testingT *core.T) { + policy := SandboxPolicy{ReadPaths: []string{"rom/"}, WritePaths: []string{"saves/"}, Resources: ResourceLimits{CPUPercent: 50}} + issues := policy.ValidateLaunch(LaunchPlan{Entrypoint: "rom/game.bin", WritePaths: []string{"saves/state"}, Resources: ResourceLimits{CPUPercent: 25}}) + core.AssertFalse(testingT, issues.HasIssues()) +} + +func TestAX7_SandboxPolicy_ValidateLaunch_Bad(testingT *core.T) { + policy := SandboxPolicy{} + issues := policy.ValidateLaunch(LaunchPlan{NetworkAllowed: true}) + core.AssertTrue(testingT, hasIssueCode(issues, "sandbox/network-denied")) +} + +func TestAX7_SandboxPolicy_ValidateLaunch_Ugly(testingT *core.T) { + policy := SandboxPolicy{ReadPaths: []string{"rom/"}, WritePaths: []string{"saves/"}, Resources: ResourceLimits{MemoryBytes: 128}} + issues := policy.ValidateLaunch(LaunchPlan{ReadPaths: []string{"../escape"}, WritePaths: []string{"screenshots/"}, Resources: ResourceLimits{MemoryBytes: 256}}) + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_SandboxError_Error_Good(testingT *core.T) { + err := SandboxError{Kind: "sandbox/directory-create-failed", Path: "saves", Message: "denied"} + got := err.Error() + core.AssertContains(testingT, got, "saves") +} + +func TestAX7_SandboxError_Error_Bad(testingT *core.T) { + err := SandboxError{Kind: "sandbox/home-required", Message: "home directory is required"} + got := err.Error() + core.AssertEqual(testingT, "sandbox/home-required: home directory is required", got) +} + +func TestAX7_SandboxError_Error_Ugly(testingT *core.T) { + err := SandboxError{} + got := err.Error() + core.AssertContains(testingT, got, ": ") +} + +func TestAX7_Shield_Verify_Good(testingT *core.T) { + bundle := shieldBundle(testingT, []byte("shield-rom")) + report := (Shield{Registry: ax7Registry(testingT, SyntheticEngine{})}).Verify(bundle) + core.AssertTrue(testingT, report.OverallOK) +} + +func TestAX7_Shield_Verify_Bad(testingT *core.T) { + bundle := shieldBundle(testingT, []byte("shield-rom")) + report := (Shield{Registry: NewRegistry()}).Verify(bundle) + core.AssertFalse(testingT, report.OverallOK) +} + +func TestAX7_Shield_Verify_Ugly(testingT *core.T) { + bundle := shieldBundle(testingT, []byte("shield-rom")) + bundle.Manifest.Artefact.Path = "../escape" + report := (Shield{Registry: ax7Registry(testingT, SyntheticEngine{})}).Verify(bundle) + core.AssertFalse(testingT, report.OverallOK) +} + +func TestAX7_ShieldReport_Issues_Good(testingT *core.T) { + report := ShieldReport{SBOM: SBOMResult{Valid: true}, Code: CodeResult{OK: true}, Content: ContentResult{OK: true}, Threat: ThreatResult{OK: true}} + issues := report.Issues() + core.AssertFalse(testingT, issues.HasIssues()) +} + +func TestAX7_ShieldReport_Issues_Bad(testingT *core.T) { + report := ShieldReport{SBOM: SBOMResult{Issues: ValidationErrors{{Code: "sbom/missing"}}}} + issues := report.Issues() + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_ShieldReport_Issues_Ugly(testingT *core.T) { + report := ShieldReport{Threat: ThreatResult{Issues: ValidationErrors{{Code: "threat/test"}}}} + issues := report.Issues() + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_NewService_Good(testingT *core.T) { + registry := NewRegistry() + service := NewService(validBundleFS(), registry) + core.AssertEqual(testingT, registry, service.Registry) +} + +func TestAX7_NewService_Bad(testingT *core.T) { + service := NewService(nil, nil) + core.AssertNil(testingT, service.Bundles) + core.AssertNotNil(testingT, service.Registry) +} + +func TestAX7_NewService_Ugly(testingT *core.T) { + service := NewService(validBundleFS(), nil) + core.AssertNotNil(testingT, service.Bundles) + core.AssertNotNil(testingT, service.Registry) +} + +func TestAX7_Service_ListBundles_Good(testingT *core.T) { + service := NewService(listBundleFS(), ax7Registry(testingT, SyntheticEngine{})) + summaries, err := service.ListBundles(ListRequest{Root: "."}) + core.AssertNoError(testingT, err) + core.AssertLen(testingT, summaries, 2) +} + +func TestAX7_Service_ListBundles_Bad(testingT *core.T) { + service := NewService(nil, NewRegistry()) + summaries, err := service.ListBundles(ListRequest{Root: "."}) + core.AssertError(testingT, err, "bundle/filesystem-missing") + core.AssertNil(testingT, summaries) +} + +func TestAX7_Service_ListBundles_Ugly(testingT *core.T) { + service := NewService(listBundleFS(), NewRegistry()) + summaries, err := service.ListBundles(ListRequest{Root: "missing"}) + core.AssertError(testingT, err) + core.AssertNil(testingT, summaries) +} + +func TestAX7_Service_VerifyBundle_Good(testingT *core.T) { + service := NewService(verifiedBundleFS(), ax7Registry(testingT, RetroArchEngine{Binary: "retroarch"})) + result, err := service.VerifyBundle(VerifyRequest{BundlePath: "."}) + core.AssertNoError(testingT, err) + core.AssertTrue(testingT, result.Verified) +} + +func TestAX7_Service_VerifyBundle_Bad(testingT *core.T) { + service := NewService(nil, NewRegistry()) + _, err := service.VerifyBundle(VerifyRequest{BundlePath: "."}) + core.AssertError(testingT, err, "bundle/filesystem-missing") +} + +func TestAX7_Service_VerifyBundle_Ugly(testingT *core.T) { + service := NewService(verifiedBundleFS(), NewRegistry()) + result, err := service.VerifyBundle(VerifyRequest{BundlePath: "."}) + core.AssertNoError(testingT, err) + core.AssertFalse(testingT, result.Verified) +} + +func TestAX7_Service_PreparePlay_Good(testingT *core.T) { + service := NewService(verifiedBundleFS(), ax7Registry(testingT, RetroArchEngine{Binary: "retroarch"})) + plan, err := service.PreparePlay(PlayRequest{BundlePath: "."}) + core.AssertNoError(testingT, err) + core.AssertTrue(testingT, plan.Ready) +} + +func TestAX7_Service_PreparePlay_Bad(testingT *core.T) { + service := NewService(nil, NewRegistry()) + _, err := service.PreparePlay(PlayRequest{BundlePath: "."}) + core.AssertError(testingT, err, "bundle/filesystem-missing") +} + +func TestAX7_Service_PreparePlay_Ugly(testingT *core.T) { + service := NewService(unsupportedProfileBundleFS(), ax7Registry(testingT, RetroArchEngine{Binary: "retroarch"})) + plan, err := service.PreparePlay(PlayRequest{BundlePath: "."}) + core.AssertNoError(testingT, err) + core.AssertFalse(testingT, plan.Ready) +} + +func TestAX7_Service_PlanBundle_Good(testingT *core.T) { + service := NewService(nil, nil) + plan, issues := service.PlanBundle(ax7BundleRequest("plan-good")) + core.AssertFalse(testingT, issues.HasIssues()) + core.AssertEqual(testingT, "plan-good", plan.Path) +} + +func TestAX7_Service_PlanBundle_Bad(testingT *core.T) { + service := NewService(nil, nil) + _, issues := service.PlanBundle(BundleRequest{}) + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_Service_PlanBundle_Ugly(testingT *core.T) { + request := ax7BundleRequest("plan-ugly") + request.ArtefactData[0] = 'x' + service := NewService(nil, nil) + plan, issues := service.PlanBundle(request) + core.AssertFalse(testingT, issues.HasIssues()) + core.AssertEqual(testingT, []byte("xom"), plan.ArtefactData) +} + +func TestAX7_Service_RenderBundle_Good(testingT *core.T) { + rendered, err := NewService(nil, nil).RenderBundle(ax7BundleRequest("render-service-good")) + core.AssertNoError(testingT, err) + core.AssertNotEmpty(testingT, rendered.Files) +} + +func TestAX7_Service_RenderBundle_Bad(testingT *core.T) { + rendered, err := NewService(nil, nil).RenderBundle(BundleRequest{}) + core.AssertError(testingT, err, "manifest/") + core.AssertEmpty(testingT, rendered.Files) +} + +func TestAX7_Service_RenderBundle_Ugly(testingT *core.T) { + request := ax7BundleRequest("render-service-ugly") + request.BYOROM = true + rendered, err := NewService(nil, nil).RenderBundle(request) + core.AssertNoError(testingT, err) + core.AssertNotEmpty(testingT, rendered.Files) +} + +func TestAX7_Service_WriteBundle_Good(testingT *core.T) { + writer := newMemoryBundleWriter() + err := NewService(nil, nil).WriteBundle(ax7BundleRequest("write-service-good"), writer) + core.AssertNoError(testingT, err) + core.AssertNotEmpty(testingT, writer.files) +} + +func TestAX7_Service_WriteBundle_Bad(testingT *core.T) { + err := NewService(nil, nil).WriteBundle(BundleRequest{}, newMemoryBundleWriter()) + core.AssertError(testingT, err, "manifest/") + core.AssertNotNil(testingT, err) +} + +func TestAX7_Service_WriteBundle_Ugly(testingT *core.T) { + err := NewService(nil, nil).WriteBundle(ax7BundleRequest("write-service-ugly"), failingwriter{failWrite: true}) + core.AssertError(testingT, err, "bundle/file-write-failed") + core.AssertNotNil(testingT, err) +} + +func TestAX7_Manifest_Validate_Good(testingT *core.T) { + manifest, err := LoadManifest([]byte(validManifestYAML())) + core.RequireNoError(testingT, err) + issues := manifest.Validate() + core.AssertFalse(testingT, issues.HasIssues()) +} + +func TestAX7_Manifest_Validate_Bad(testingT *core.T) { + manifest := Manifest{} + issues := manifest.Validate() + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_Manifest_Validate_Ugly(testingT *core.T) { + manifest, err := LoadManifest([]byte(validManifestYAML())) + core.RequireNoError(testingT, err) + manifest.Resources.CPUPercent = -1 + issues := manifest.Validate() + core.AssertTrue(testingT, hasIssueCode(issues, "manifest/resources-cpu-invalid")) +} + +func TestAX7_ValidationIssue_Error_Good(testingT *core.T) { + issue := ValidationIssue{Code: "manifest/name-required", Field: "name", Message: "name is required"} + got := issue.Error() + core.AssertEqual(testingT, "manifest/name-required: name: name is required", got) +} + +func TestAX7_ValidationIssue_Error_Bad(testingT *core.T) { + issue := ValidationIssue{Code: "manifest/name-required"} + got := issue.Error() + core.AssertEqual(testingT, "manifest/name-required", got) +} + +func TestAX7_ValidationIssue_Error_Ugly(testingT *core.T) { + issue := ValidationIssue{Code: "manifest/name-required", Message: "name is required"} + got := issue.Error() + core.AssertEqual(testingT, "manifest/name-required: name is required", got) +} + +func TestAX7_ValidationErrors_Error_Good(testingT *core.T) { + issues := ValidationErrors{{Code: "one"}, {Code: "two"}} + got := issues.Error() + core.AssertEqual(testingT, "one; two", got) +} + +func TestAX7_ValidationErrors_Error_Bad(testingT *core.T) { + issues := ValidationErrors{} + got := issues.Error() + core.AssertEqual(testingT, "", got) +} + +func TestAX7_ValidationErrors_Error_Ugly(testingT *core.T) { + issues := ValidationErrors{{Code: "one", Field: "field", Message: "message"}} + got := issues.Error() + core.AssertContains(testingT, got, "field") +} + +func TestAX7_ValidationErrors_HasIssues_Good(testingT *core.T) { + issues := ValidationErrors{{Code: "one"}} + got := issues.HasIssues() + core.AssertTrue(testingT, got) +} + +func TestAX7_ValidationErrors_HasIssues_Bad(testingT *core.T) { + issues := ValidationErrors{} + got := issues.HasIssues() + core.AssertFalse(testingT, got) +} + +func TestAX7_ValidationErrors_HasIssues_Ugly(testingT *core.T) { + var issues ValidationErrors + got := issues.HasIssues() + core.AssertFalse(testingT, got) +} + +func TestAX7_Bundle_Verify_Good(testingT *core.T) { + core.RequireNoError(testingT, RegisterEngine(RetroArchEngine{Binary: "retroarch"})) + bundle := ax7Bundle(testingT, verifiedBundleFS()) + issues := bundle.Verify() + core.AssertFalse(testingT, issues.HasIssues()) +} + +func TestAX7_Bundle_Verify_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, brokenChecksumBundleFS()) + issues := bundle.VerifyWithRegistry(ax7Registry(testingT, RetroArchEngine{Binary: "retroarch"})) + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_Bundle_Verify_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, verifiedBundleFS()) + issues := bundle.VerifyWithRegistry(nil) + core.AssertTrue(testingT, hasIssueCode(issues, "engine/registry-missing")) +} + +func TestAX7_Bundle_VerifyWithRegistry_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, verifiedBundleFS()) + issues := bundle.VerifyWithRegistry(ax7Registry(testingT, RetroArchEngine{Binary: "retroarch"})) + core.AssertFalse(testingT, issues.HasIssues()) +} + +func TestAX7_Bundle_VerifyWithRegistry_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, verifiedBundleFS()) + issues := bundle.VerifyWithRegistry(NewRegistry()) + core.AssertTrue(testingT, hasIssueCode(issues, "engine/unavailable")) +} + +func TestAX7_Bundle_VerifyWithRegistry_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, brokenChecksumBundleFS()) + issues := bundle.VerifyWithRegistry(ax7Registry(testingT, RetroArchEngine{Binary: "retroarch"})) + core.AssertTrue(testingT, issues.HasIssues()) +} + +func TestAX7_ParseChecksumFile_Good(testingT *core.T) { + entries, err := ParseChecksumFile([]byte(validArtefactSHA256 + " rom/game.bin\n")) + core.AssertNoError(testingT, err) + core.AssertLen(testingT, entries, 1) +} + +func TestAX7_ParseChecksumFile_Bad(testingT *core.T) { + entries, err := ParseChecksumFile([]byte("not-a-checksum\n")) + core.AssertError(testingT, err, "checksum/invalid-line") + core.AssertNil(testingT, entries) +} + +func TestAX7_ParseChecksumFile_Ugly(testingT *core.T) { + entries, err := ParseChecksumFile([]byte("# comment\n\n" + validArtefactSHA256 + " rom/game.bin\n")) + core.AssertNoError(testingT, err) + core.AssertLen(testingT, entries, 1) +} + +func TestAX7_ChecksumParseError_Error_Good(testingT *core.T) { + err := ChecksumParseError{Kind: "checksum/invalid-hash", Message: "checksum entry must contain a valid sha256 value"} + got := err.Error() + core.AssertContains(testingT, got, "checksum/invalid-hash") +} + +func TestAX7_ChecksumParseError_Error_Bad(testingT *core.T) { + err := ChecksumParseError{Kind: "checksum/invalid-line", Message: "checksum lines must contain a sha256 value and path"} + got := err.Error() + core.AssertEqual(testingT, "checksum/invalid-line: checksum lines must contain a sha256 value and path", got) +} + +func TestAX7_ChecksumParseError_Error_Ugly(testingT *core.T) { + err := ChecksumParseError{} + got := err.Error() + core.AssertContains(testingT, got, ": ") +} + +func TestAX7_RenderedBundle_Write_Good(testingT *core.T) { + rendered := shieldRendered(testingT, []byte("rom")) + writer := newMemoryBundleWriter() + err := rendered.Write(writer) + core.AssertNoError(testingT, err) + core.AssertNotEmpty(testingT, writer.files) +} + +func TestAX7_RenderedBundle_Write_Bad(testingT *core.T) { + rendered := RenderedBundle{Path: "game"} + err := rendered.Write(nil) + core.AssertError(testingT, err, "bundle/writer-missing") +} + +func TestAX7_RenderedBundle_Write_Ugly(testingT *core.T) { + rendered := RenderedBundle{Path: "game", Files: []RenderedFile{{Path: "rom/game.bin", Data: []byte("rom")}}} + err := rendered.Write(failingwriter{failEnsure: true}) + core.AssertError(testingT, err, "bundle/root-create-failed") +} + +func TestAX7_WriteError_Error_Good(testingT *core.T) { + err := WriteError{Kind: "bundle/file-write-failed", Path: "rom/game.bin", Message: "denied"} + got := err.Error() + core.AssertContains(testingT, got, "rom/game.bin") +} + +func TestAX7_WriteError_Error_Bad(testingT *core.T) { + err := WriteError{Kind: "bundle/path-required", Message: "bundle path is required"} + got := err.Error() + core.AssertEqual(testingT, "bundle/path-required: bundle path is required", got) +} + +func TestAX7_WriteError_Error_Ugly(testingT *core.T) { + err := WriteError{} + got := err.Error() + core.AssertContains(testingT, got, ": ") +} + +func TestAX7_DOSBoxEngine_Name_Good(testingT *core.T) { + engine := DOSBoxEngine{} + got := engine.Name() + core.AssertEqual(testingT, "dosbox", got) +} + +func TestAX7_DOSBoxEngine_Name_Bad(testingT *core.T) { + engine := DOSBoxEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "retroarch", got) +} + +func TestAX7_DOSBoxEngine_Name_Ugly(testingT *core.T) { + engine := DOSBoxEngine{Binary: "custom-dosbox"} + got := engine.Name() + core.AssertEqual(testingT, "dosbox", got) +} + +func TestAX7_DOSBoxEngine_Platforms_Good(testingT *core.T) { + engine := DOSBoxEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "dos") +} + +func TestAX7_DOSBoxEngine_Platforms_Bad(testingT *core.T) { + engine := DOSBoxEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "snes") +} + +func TestAX7_DOSBoxEngine_Platforms_Ugly(testingT *core.T) { + engine := DOSBoxEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 1) +} + +func TestAX7_DOSBoxEngine_Acceleration_Good(testingT *core.T) { + engine := DOSBoxEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_DOSBoxEngine_Acceleration_Bad(testingT *core.T) { + engine := DOSBoxEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationRequired, acceleration.Mode) +} + +func TestAX7_DOSBoxEngine_Acceleration_Ugly(testingT *core.T) { + engine := DOSBoxEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterBilinear) +} + +func TestAX7_DOSBoxEngine_Verify_Good(testingT *core.T) { + engine := DOSBoxEngine{Binary: "dosbox"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_DOSBoxEngine_Verify_Bad(testingT *core.T) { + engine := DOSBoxEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_DOSBoxEngine_Verify_Ugly(testingT *core.T) { + engine := DOSBoxEngine{Binary: "bin/dosbox"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_DOSBoxEngine_CodeIdentity_Good(testingT *core.T) { + engine := DOSBoxEngine{Binary: "dosbox", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_DOSBoxEngine_CodeIdentity_Bad(testingT *core.T) { + engine := DOSBoxEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "dosbox", identity.Path) +} + +func TestAX7_DOSBoxEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := DOSBoxEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_DOSBoxEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (DOSBoxEngine{Binary: "dosbox"}).Run("rom/game.exe", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_DOSBoxEngine_Run_Bad(testingT *core.T) { + err := (DOSBoxEngine{}).Run("rom/game.exe", ax7EngineConfig("")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_DOSBoxEngine_Run_Ugly(testingT *core.T) { + err := (DOSBoxEngine{Binary: "dosbox"}).Run("rom/game.exe", EngineConfig{}) + core.AssertError(testingT, err, "engine/process-unavailable") + core.AssertNotNil(testingT, err) +} + +func TestAX7_DOSBoxEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, dosBundleFS()) + plan, err := (DOSBoxEngine{Binary: "dosbox"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "dosbox", plan.Engine) +} + +func TestAX7_DOSBoxEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, dosBundleFS()) + _, err := (DOSBoxEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_DOSBoxEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, dosBundleFS()) + bundle.Manifest.Platform = "snes" + _, err := (DOSBoxEngine{Binary: "dosbox"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/platform-unsupported") +} + +func TestAX7_DOSBoxXEngine_Name_Good(testingT *core.T) { + engine := DOSBoxXEngine{} + got := engine.Name() + core.AssertEqual(testingT, "dosbox-x", got) +} + +func TestAX7_DOSBoxXEngine_Name_Bad(testingT *core.T) { + engine := DOSBoxXEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "dosbox", got) +} + +func TestAX7_DOSBoxXEngine_Name_Ugly(testingT *core.T) { + engine := DOSBoxXEngine{Binary: "custom"} + got := engine.Name() + core.AssertEqual(testingT, "dosbox-x", got) +} + +func TestAX7_DOSBoxXEngine_Platforms_Good(testingT *core.T) { + engine := DOSBoxXEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "windows-9x") +} + +func TestAX7_DOSBoxXEngine_Platforms_Bad(testingT *core.T) { + engine := DOSBoxXEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "arcade") +} + +func TestAX7_DOSBoxXEngine_Platforms_Ugly(testingT *core.T) { + engine := DOSBoxXEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 4) +} + +func TestAX7_DOSBoxXEngine_Acceleration_Good(testingT *core.T) { + engine := DOSBoxXEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_DOSBoxXEngine_Acceleration_Bad(testingT *core.T) { + engine := DOSBoxXEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationOff, acceleration.Mode) +} + +func TestAX7_DOSBoxXEngine_Acceleration_Ugly(testingT *core.T) { + engine := DOSBoxXEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterCRT) +} + +func TestAX7_DOSBoxXEngine_Verify_Good(testingT *core.T) { + engine := DOSBoxXEngine{Binary: "dosbox-x"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_DOSBoxXEngine_Verify_Bad(testingT *core.T) { + engine := DOSBoxXEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_DOSBoxXEngine_Verify_Ugly(testingT *core.T) { + engine := DOSBoxXEngine{Binary: "bin/dosbox-x"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_DOSBoxXEngine_CodeIdentity_Good(testingT *core.T) { + engine := DOSBoxXEngine{Binary: "dosbox-x", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_DOSBoxXEngine_CodeIdentity_Bad(testingT *core.T) { + engine := DOSBoxXEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "dosbox-x", identity.Path) +} + +func TestAX7_DOSBoxXEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := DOSBoxXEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_DOSBoxXEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (DOSBoxXEngine{Binary: "dosbox-x"}).Run("rom/game.exe", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Profile: "dos", Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_DOSBoxXEngine_Run_Bad(testingT *core.T) { + err := (DOSBoxXEngine{}).Run("rom/game.exe", ax7EngineConfig("dos")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_DOSBoxXEngine_Run_Ugly(testingT *core.T) { + err := (DOSBoxXEngine{Binary: "dosbox-x"}).Run("rom/game.exe", EngineConfig{Profile: "unknown"}) + core.AssertError(testingT, err, "engine/profile-unsupported") + core.AssertNotNil(testingT, err) +} + +func TestAX7_DOSBoxXEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, dosBoxXBundleFS()) + plan, err := (DOSBoxXEngine{Binary: "dosbox-x"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "dosbox-x", plan.Engine) +} + +func TestAX7_DOSBoxXEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, dosBoxXBundleFS()) + _, err := (DOSBoxXEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_DOSBoxXEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, dosBoxXBundleFS()) + bundle.Manifest.Runtime.Profile = "unknown" + _, err := (DOSBoxXEngine{Binary: "dosbox-x"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/profile-unsupported") +} + +func TestAX7_FUSEEngine_Name_Good(testingT *core.T) { + engine := FUSEEngine{} + got := engine.Name() + core.AssertEqual(testingT, "fuse", got) +} + +func TestAX7_FUSEEngine_Name_Bad(testingT *core.T) { + engine := FUSEEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "vice", got) +} + +func TestAX7_FUSEEngine_Name_Ugly(testingT *core.T) { + engine := FUSEEngine{Binary: "custom"} + got := engine.Name() + core.AssertEqual(testingT, "fuse", got) +} + +func TestAX7_FUSEEngine_Platforms_Good(testingT *core.T) { + engine := FUSEEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "zx-spectrum") +} + +func TestAX7_FUSEEngine_Platforms_Bad(testingT *core.T) { + engine := FUSEEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "commodore-64") +} + +func TestAX7_FUSEEngine_Platforms_Ugly(testingT *core.T) { + engine := FUSEEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 4) +} + +func TestAX7_FUSEEngine_Acceleration_Good(testingT *core.T) { + engine := FUSEEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_FUSEEngine_Acceleration_Bad(testingT *core.T) { + engine := FUSEEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationRequired, acceleration.Mode) +} + +func TestAX7_FUSEEngine_Acceleration_Ugly(testingT *core.T) { + engine := FUSEEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterCRT) +} + +func TestAX7_FUSEEngine_Verify_Good(testingT *core.T) { + engine := FUSEEngine{Binary: "fuse"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_FUSEEngine_Verify_Bad(testingT *core.T) { + engine := FUSEEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_FUSEEngine_Verify_Ugly(testingT *core.T) { + engine := FUSEEngine{Binary: "bin/fuse"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_FUSEEngine_CodeIdentity_Good(testingT *core.T) { + engine := FUSEEngine{Binary: "fuse", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_FUSEEngine_CodeIdentity_Bad(testingT *core.T) { + engine := FUSEEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "fuse", identity.Path) +} + +func TestAX7_FUSEEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := FUSEEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_FUSEEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (FUSEEngine{Binary: "fuse"}).Run("rom/game.tap", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Profile: "48k", Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_FUSEEngine_Run_Bad(testingT *core.T) { + err := (FUSEEngine{}).Run("rom/game.tap", ax7EngineConfig("48k")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_FUSEEngine_Run_Ugly(testingT *core.T) { + err := (FUSEEngine{Binary: "fuse"}).Run("rom/game.tap", EngineConfig{Profile: "unknown"}) + core.AssertError(testingT, err, "engine/profile-unsupported") + core.AssertNotNil(testingT, err) +} + +func TestAX7_FUSEEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, fuseBundleFS(testingT)) + plan, err := (FUSEEngine{Binary: "fuse"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "fuse", plan.Engine) +} + +func TestAX7_FUSEEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, fuseBundleFS(testingT)) + _, err := (FUSEEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_FUSEEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, fuseBundleFS(testingT)) + bundle.Manifest.Runtime.Profile = "unknown" + _, err := (FUSEEngine{Binary: "fuse"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/profile-unsupported") +} + +func TestAX7_MAMEEngine_Name_Good(testingT *core.T) { + engine := MAMEEngine{} + got := engine.Name() + core.AssertEqual(testingT, "mame", got) +} + +func TestAX7_MAMEEngine_Name_Bad(testingT *core.T) { + engine := MAMEEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "fuse", got) +} + +func TestAX7_MAMEEngine_Name_Ugly(testingT *core.T) { + engine := MAMEEngine{Binary: "custom"} + got := engine.Name() + core.AssertEqual(testingT, "mame", got) +} + +func TestAX7_MAMEEngine_Platforms_Good(testingT *core.T) { + engine := MAMEEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "arcade") +} + +func TestAX7_MAMEEngine_Platforms_Bad(testingT *core.T) { + engine := MAMEEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "dos") +} + +func TestAX7_MAMEEngine_Platforms_Ugly(testingT *core.T) { + engine := MAMEEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 2) +} + +func TestAX7_MAMEEngine_Acceleration_Good(testingT *core.T) { + engine := MAMEEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_MAMEEngine_Acceleration_Bad(testingT *core.T) { + engine := MAMEEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationOff, acceleration.Mode) +} + +func TestAX7_MAMEEngine_Acceleration_Ugly(testingT *core.T) { + engine := MAMEEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterScanline) +} + +func TestAX7_MAMEEngine_Verify_Good(testingT *core.T) { + engine := MAMEEngine{Binary: "mame"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_MAMEEngine_Verify_Bad(testingT *core.T) { + engine := MAMEEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_MAMEEngine_Verify_Ugly(testingT *core.T) { + engine := MAMEEngine{Binary: "bin/mame"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_MAMEEngine_CodeIdentity_Good(testingT *core.T) { + engine := MAMEEngine{Binary: "mame", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_MAMEEngine_CodeIdentity_Bad(testingT *core.T) { + engine := MAMEEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "mame", identity.Path) +} + +func TestAX7_MAMEEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := MAMEEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_MAMEEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (MAMEEngine{Binary: "mame"}).Run("rom/pacman.zip", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Profile: "pacman", Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_MAMEEngine_Run_Bad(testingT *core.T) { + err := (MAMEEngine{}).Run("rom/pacman.zip", ax7EngineConfig("pacman")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_MAMEEngine_Run_Ugly(testingT *core.T) { + err := (MAMEEngine{Binary: "mame"}).Run("rom/pacman.zip", EngineConfig{}) + core.AssertError(testingT, err, "engine/profile-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_MAMEEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, mameBundleFS(testingT)) + plan, err := (MAMEEngine{Binary: "mame"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "mame", plan.Engine) +} + +func TestAX7_MAMEEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, mameBundleFS(testingT)) + _, err := (MAMEEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_MAMEEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, mameBundleFS(testingT)) + bundle.Manifest.Runtime.Profile = "" + _, err := (MAMEEngine{Binary: "mame"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/profile-required") +} + +func TestAX7_RetroArchEngine_Name_Good(testingT *core.T) { + engine := RetroArchEngine{} + got := engine.Name() + core.AssertEqual(testingT, "retroarch", got) +} + +func TestAX7_RetroArchEngine_Name_Bad(testingT *core.T) { + engine := RetroArchEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "snes9x", got) +} + +func TestAX7_RetroArchEngine_Name_Ugly(testingT *core.T) { + engine := RetroArchEngine{Binary: "custom"} + got := engine.Name() + core.AssertEqual(testingT, "retroarch", got) +} + +func TestAX7_RetroArchEngine_Platforms_Good(testingT *core.T) { + engine := RetroArchEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "sega-genesis") +} + +func TestAX7_RetroArchEngine_Platforms_Bad(testingT *core.T) { + engine := RetroArchEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "scummvm") +} + +func TestAX7_RetroArchEngine_Platforms_Ugly(testingT *core.T) { + engine := RetroArchEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "gba") +} + +func TestAX7_RetroArchEngine_Acceleration_Good(testingT *core.T) { + engine := RetroArchEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_RetroArchEngine_Acceleration_Bad(testingT *core.T) { + engine := RetroArchEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationRequired, acceleration.Mode) +} + +func TestAX7_RetroArchEngine_Acceleration_Ugly(testingT *core.T) { + engine := RetroArchEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterCRT) +} + +func TestAX7_RetroArchEngine_Verify_Good(testingT *core.T) { + engine := RetroArchEngine{Binary: "retroarch"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_RetroArchEngine_Verify_Bad(testingT *core.T) { + engine := RetroArchEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_RetroArchEngine_Verify_Ugly(testingT *core.T) { + engine := RetroArchEngine{Binary: "bin/retroarch"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_RetroArchEngine_CodeIdentity_Good(testingT *core.T) { + engine := RetroArchEngine{Binary: "retroarch", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_RetroArchEngine_CodeIdentity_Bad(testingT *core.T) { + engine := RetroArchEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "retroarch", identity.Path) +} + +func TestAX7_RetroArchEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := RetroArchEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_RetroArchEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (RetroArchEngine{Binary: "retroarch"}).Run("rom/game.zip", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Profile: "genesis", Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_RetroArchEngine_Run_Bad(testingT *core.T) { + err := (RetroArchEngine{}).Run("rom/game.zip", ax7EngineConfig("genesis")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_RetroArchEngine_Run_Ugly(testingT *core.T) { + err := (RetroArchEngine{Binary: "retroarch"}).Run("rom/game.zip", EngineConfig{Profile: "unknown"}) + core.AssertError(testingT, err, "engine/profile-unsupported") + core.AssertNotNil(testingT, err) +} + +func TestAX7_RetroArchEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, validBundleFS()) + plan, err := (RetroArchEngine{Binary: "retroarch"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "retroarch", plan.Engine) +} + +func TestAX7_RetroArchEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, validBundleFS()) + _, err := (RetroArchEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_RetroArchEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, validBundleFS()) + bundle.Manifest.Runtime.Profile = "unknown" + _, err := (RetroArchEngine{Binary: "retroarch"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/profile-unsupported") +} + +func TestAX7_ScummVMEngine_Name_Good(testingT *core.T) { + engine := ScummVMEngine{} + got := engine.Name() + core.AssertEqual(testingT, "scummvm", got) +} + +func TestAX7_ScummVMEngine_Name_Bad(testingT *core.T) { + engine := ScummVMEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "retroarch", got) +} + +func TestAX7_ScummVMEngine_Name_Ugly(testingT *core.T) { + engine := ScummVMEngine{Binary: "custom"} + got := engine.Name() + core.AssertEqual(testingT, "scummvm", got) +} + +func TestAX7_ScummVMEngine_Platforms_Good(testingT *core.T) { + engine := ScummVMEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "scummvm") +} + +func TestAX7_ScummVMEngine_Platforms_Bad(testingT *core.T) { + engine := ScummVMEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "arcade") +} + +func TestAX7_ScummVMEngine_Platforms_Ugly(testingT *core.T) { + engine := ScummVMEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 2) +} + +func TestAX7_ScummVMEngine_Acceleration_Good(testingT *core.T) { + engine := ScummVMEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_ScummVMEngine_Acceleration_Bad(testingT *core.T) { + engine := ScummVMEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationRequired, acceleration.Mode) +} + +func TestAX7_ScummVMEngine_Acceleration_Ugly(testingT *core.T) { + engine := ScummVMEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterBilinear) +} + +func TestAX7_ScummVMEngine_Verify_Good(testingT *core.T) { + engine := ScummVMEngine{Binary: "scummvm"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_ScummVMEngine_Verify_Bad(testingT *core.T) { + engine := ScummVMEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_ScummVMEngine_Verify_Ugly(testingT *core.T) { + engine := ScummVMEngine{Binary: "scummvm", Core: scummVMCore("ScummVM 2.6.1")} + err := engine.Verify() + core.AssertError(testingT, err, "engine/version-unsupported") +} + +func TestAX7_ScummVMEngine_CodeIdentity_Good(testingT *core.T) { + engine := ScummVMEngine{Binary: "scummvm", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_ScummVMEngine_CodeIdentity_Bad(testingT *core.T) { + engine := ScummVMEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "scummvm", identity.Path) +} + +func TestAX7_ScummVMEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := ScummVMEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_ScummVMEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (ScummVMEngine{Binary: "scummvm"}).Run("game/BASS/sky.dsk", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Profile: "sky", SaveRoot: "saves/", Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_ScummVMEngine_Run_Bad(testingT *core.T) { + err := (ScummVMEngine{}).Run("game/BASS/sky.dsk", ax7EngineConfig("sky")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_ScummVMEngine_Run_Ugly(testingT *core.T) { + err := (ScummVMEngine{Binary: "scummvm"}).Run("game/BASS/sky.dsk", EngineConfig{}) + core.AssertError(testingT, err, "engine/profile-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_ScummVMEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, scummVMBundleFS()) + plan, err := (ScummVMEngine{Binary: "scummvm"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "scummvm", plan.Engine) +} + +func TestAX7_ScummVMEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, scummVMBundleFS()) + _, err := (ScummVMEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_ScummVMEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, scummVMBundleFS()) + bundle.Manifest.Runtime.Profile = "" + _, err := (ScummVMEngine{Binary: "scummvm"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/profile-required") +} + +func TestAX7_Snes9xEngine_Name_Good(testingT *core.T) { + engine := Snes9xEngine{} + got := engine.Name() + core.AssertEqual(testingT, "snes9x", got) +} + +func TestAX7_Snes9xEngine_Name_Bad(testingT *core.T) { + engine := Snes9xEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "mame", got) +} + +func TestAX7_Snes9xEngine_Name_Ugly(testingT *core.T) { + engine := Snes9xEngine{Binary: "custom"} + got := engine.Name() + core.AssertEqual(testingT, "snes9x", got) +} + +func TestAX7_Snes9xEngine_Platforms_Good(testingT *core.T) { + engine := Snes9xEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "snes") +} + +func TestAX7_Snes9xEngine_Platforms_Bad(testingT *core.T) { + engine := Snes9xEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "nes") +} + +func TestAX7_Snes9xEngine_Platforms_Ugly(testingT *core.T) { + engine := Snes9xEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 2) +} + +func TestAX7_Snes9xEngine_Acceleration_Good(testingT *core.T) { + engine := Snes9xEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_Snes9xEngine_Acceleration_Bad(testingT *core.T) { + engine := Snes9xEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationRequired, acceleration.Mode) +} + +func TestAX7_Snes9xEngine_Acceleration_Ugly(testingT *core.T) { + engine := Snes9xEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterScanline) +} + +func TestAX7_Snes9xEngine_Verify_Good(testingT *core.T) { + engine := Snes9xEngine{Binary: "snes9x"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_Snes9xEngine_Verify_Bad(testingT *core.T) { + engine := Snes9xEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_Snes9xEngine_Verify_Ugly(testingT *core.T) { + engine := Snes9xEngine{Binary: "bin/snes9x"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_Snes9xEngine_CodeIdentity_Good(testingT *core.T) { + engine := Snes9xEngine{Binary: "snes9x", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_Snes9xEngine_CodeIdentity_Bad(testingT *core.T) { + engine := Snes9xEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "snes9x", identity.Path) +} + +func TestAX7_Snes9xEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := Snes9xEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_Snes9xEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (Snes9xEngine{Binary: "snes9x"}).Run("rom/game.sfc", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_Snes9xEngine_Run_Bad(testingT *core.T) { + err := (Snes9xEngine{}).Run("rom/game.sfc", ax7EngineConfig("")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_Snes9xEngine_Run_Ugly(testingT *core.T) { + err := (Snes9xEngine{Binary: "snes9x"}).Run("rom/game.sfc", EngineConfig{}) + core.AssertError(testingT, err, "engine/process-unavailable") + core.AssertNotNil(testingT, err) +} + +func TestAX7_Snes9xEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, snes9xBundleFS(testingT)) + plan, err := (Snes9xEngine{Binary: "snes9x"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "snes9x", plan.Engine) +} + +func TestAX7_Snes9xEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, snes9xBundleFS(testingT)) + _, err := (Snes9xEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_Snes9xEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, snes9xBundleFS(testingT)) + bundle.Manifest.Platform = "dos" + _, err := (Snes9xEngine{Binary: "snes9x"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/platform-unsupported") +} + +func TestAX7_SyntheticEngine_Name_Good(testingT *core.T) { + engine := SyntheticEngine{} + got := engine.Name() + core.AssertEqual(testingT, "synthetic", got) +} + +func TestAX7_SyntheticEngine_Name_Bad(testingT *core.T) { + engine := SyntheticEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "retroarch", got) +} + +func TestAX7_SyntheticEngine_Name_Ugly(testingT *core.T) { + engine := SyntheticEngine{} + first := engine.Name() + second := engine.Name() + core.AssertEqual(testingT, first, second) +} + +func TestAX7_SyntheticEngine_Platforms_Good(testingT *core.T) { + engine := SyntheticEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "synthetic") +} + +func TestAX7_SyntheticEngine_Platforms_Bad(testingT *core.T) { + engine := SyntheticEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "dos") +} + +func TestAX7_SyntheticEngine_Platforms_Ugly(testingT *core.T) { + engine := SyntheticEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 1) +} + +func TestAX7_SyntheticEngine_Verify_Good(testingT *core.T) { + engine := SyntheticEngine{} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_SyntheticEngine_Verify_Bad(testingT *core.T) { + engine := SyntheticEngine{} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_SyntheticEngine_Verify_Ugly(testingT *core.T) { + engine := SyntheticEngine{} + err := engine.Verify() + core.AssertNil(testingT, err) +} + +func TestAX7_SyntheticEngine_CodeIdentity_Good(testingT *core.T) { + engine := SyntheticEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "synthetic", identity.Name) +} + +func TestAX7_SyntheticEngine_CodeIdentity_Bad(testingT *core.T) { + engine := SyntheticEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "synthetic", identity.Path) +} + +func TestAX7_SyntheticEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := SyntheticEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_SyntheticEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := SyntheticEngine{}.Run("rom/game.bin", EngineConfig{Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "SYNTHETIC ENGINE OK") +} + +func TestAX7_SyntheticEngine_Run_Bad(testingT *core.T) { + err := SyntheticEngine{}.Run("rom/game.bin", EngineConfig{}) + core.AssertNoError(testingT, err) + core.AssertNil(testingT, err) +} + +func TestAX7_SyntheticEngine_Run_Ugly(testingT *core.T) { + output := core.NewBuffer() + err := SyntheticEngine{}.Run("", EngineConfig{Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "SYNTHETIC ENGINE OK") +} + +func TestAX7_SyntheticEngine_PlanLaunch_Good(testingT *core.T) { + bundle := shieldBundle(testingT, []byte("rom")) + plan, err := SyntheticEngine{}.PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "synthetic", plan.Engine) +} + +func TestAX7_SyntheticEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := shieldBundle(testingT, []byte("rom")) + bundle.Manifest.Runtime.Engine = "retroarch" + _, err := SyntheticEngine{}.PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/runtime-mismatch") +} + +func TestAX7_SyntheticEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := shieldBundle(testingT, []byte("rom")) + bundle.Manifest.Platform = "dos" + _, err := SyntheticEngine{}.PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/platform-unsupported") +} + +func TestAX7_VICEEngine_Name_Good(testingT *core.T) { + engine := VICEEngine{} + got := engine.Name() + core.AssertEqual(testingT, "vice", got) +} + +func TestAX7_VICEEngine_Name_Bad(testingT *core.T) { + engine := VICEEngine{} + got := engine.Name() + core.AssertNotEqual(testingT, "fuse", got) +} + +func TestAX7_VICEEngine_Name_Ugly(testingT *core.T) { + engine := VICEEngine{Binary: "x64sc"} + got := engine.Name() + core.AssertEqual(testingT, "vice", got) +} + +func TestAX7_VICEEngine_Platforms_Good(testingT *core.T) { + engine := VICEEngine{} + platforms := engine.Platforms() + core.AssertContains(testingT, platforms, "commodore-64") +} + +func TestAX7_VICEEngine_Platforms_Bad(testingT *core.T) { + engine := VICEEngine{} + platforms := engine.Platforms() + core.AssertNotContains(testingT, platforms, "zx-spectrum") +} + +func TestAX7_VICEEngine_Platforms_Ugly(testingT *core.T) { + engine := VICEEngine{} + platforms := engine.Platforms() + core.AssertLen(testingT, platforms, 5) +} + +func TestAX7_VICEEngine_Acceleration_Good(testingT *core.T) { + engine := VICEEngine{} + acceleration := engine.Acceleration() + core.AssertEqual(testingT, AccelerationAuto, acceleration.Mode) +} + +func TestAX7_VICEEngine_Acceleration_Bad(testingT *core.T) { + engine := VICEEngine{} + acceleration := engine.Acceleration() + core.AssertNotEqual(testingT, AccelerationRequired, acceleration.Mode) +} + +func TestAX7_VICEEngine_Acceleration_Ugly(testingT *core.T) { + engine := VICEEngine{} + acceleration := engine.Acceleration() + core.AssertContains(testingT, acceleration.PreferredFilters, FrameFilterCRT) +} + +func TestAX7_VICEEngine_Verify_Good(testingT *core.T) { + engine := VICEEngine{Binary: "x64sc"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_VICEEngine_Verify_Bad(testingT *core.T) { + engine := VICEEngine{} + err := engine.Verify() + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_VICEEngine_Verify_Ugly(testingT *core.T) { + engine := VICEEngine{Binary: "bin/x64sc"} + err := engine.Verify() + core.AssertNoError(testingT, err) +} + +func TestAX7_VICEEngine_CodeIdentity_Good(testingT *core.T) { + engine := VICEEngine{Binary: "x64sc", BinarySHA256: validArtefactSHA256} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, validArtefactSHA256, identity.SHA256) +} + +func TestAX7_VICEEngine_CodeIdentity_Bad(testingT *core.T) { + engine := VICEEngine{} + identity := engine.CodeIdentity() + core.AssertEqual(testingT, "vice", identity.Path) +} + +func TestAX7_VICEEngine_CodeIdentity_Ugly(testingT *core.T) { + engine := VICEEngine{} + identity := engine.CodeIdentity() + core.AssertLen(testingT, identity.SHA256, 64) +} + +func TestAX7_VICEEngine_Run_Good(testingT *core.T) { + output := core.NewBuffer() + err := (VICEEngine{Binary: "x64sc"}).Run("rom/game.d64", EngineConfig{Core: ax7ProcessCore("ran"), Context: core.Background(), Profile: "c64", Output: output}) + core.AssertNoError(testingT, err) + core.AssertContains(testingT, output.String(), "ran") +} + +func TestAX7_VICEEngine_Run_Bad(testingT *core.T) { + err := (VICEEngine{}).Run("rom/game.d64", ax7EngineConfig("c64")) + core.AssertError(testingT, err, "engine/binary-required") + core.AssertNotNil(testingT, err) +} + +func TestAX7_VICEEngine_Run_Ugly(testingT *core.T) { + err := (VICEEngine{Binary: "x64sc"}).Run("rom/game.d64", EngineConfig{Profile: "unknown"}) + core.AssertError(testingT, err, "engine/profile-unsupported") + core.AssertNotNil(testingT, err) +} + +func TestAX7_VICEEngine_PlanLaunch_Good(testingT *core.T) { + bundle := ax7Bundle(testingT, viceBundleFS(testingT)) + plan, err := (VICEEngine{Binary: "x64sc"}).PlanLaunch(bundle) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "vice", plan.Engine) +} + +func TestAX7_VICEEngine_PlanLaunch_Bad(testingT *core.T) { + bundle := ax7Bundle(testingT, viceBundleFS(testingT)) + _, err := (VICEEngine{}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/binary-required") +} + +func TestAX7_VICEEngine_PlanLaunch_Ugly(testingT *core.T) { + bundle := ax7Bundle(testingT, viceBundleFS(testingT)) + bundle.Manifest.Runtime.Profile = "unknown" + _, err := (VICEEngine{Binary: "x64sc"}).PlanLaunch(bundle) + core.AssertError(testingT, err, "engine/profile-unsupported") +} diff --git a/catalogue.go b/catalogue.go index 7b03617..e115658 100644 --- a/catalogue.go +++ b/catalogue.go @@ -6,7 +6,7 @@ import ( "path" "sort" - "dappco.re/go/core" + core "dappco.re/go" ) // BundleSummary describes a bundle for list and verify surfaces. @@ -141,7 +141,7 @@ func (catalogue Catalogue) registry() *Registry { func (catalogue Catalogue) bundleSize(bundlePath string) int64 { var size int64 - _ = fs.WalkDir(catalogue.Bundles, bundlePath, func(_ string, entry fs.DirEntry, walkErr error) error { + if err := fs.WalkDir(catalogue.Bundles, bundlePath, func(_ string, entry fs.DirEntry, walkErr error) error { if walkErr != nil || entry.IsDir() { return nil } @@ -152,7 +152,9 @@ func (catalogue Catalogue) bundleSize(bundlePath string) int64 { size += info.Size() return nil - }) + }); err != nil { + return 0 + } return size } diff --git a/cmd/core-play/ax7_bundle_test.go b/cmd/core-play/ax7_bundle_test.go new file mode 100644 index 0000000..5fe9450 --- /dev/null +++ b/cmd/core-play/ax7_bundle_test.go @@ -0,0 +1,52 @@ +package main + +import core "dappco.re/go" + +func TestAX7_BundleWriter_EnsureDirectory_Good(testingT *core.T) { + root := testingT.TempDir() + writer := localBundleWriter{Root: root} + err := writer.EnsureDirectory("bundle/rom") + core.AssertNoError(testingT, err) + core.AssertTrue(testingT, core.Stat(core.Path(root, "bundle/rom")).OK) +} + +func TestAX7_BundleWriter_EnsureDirectory_Bad(testingT *core.T) { + root := testingT.TempDir() + writer := localBundleWriter{Root: root} + err := writer.EnsureDirectory("bad\x00path") + core.AssertError(testingT, err) + core.AssertNotNil(testingT, err) +} + +func TestAX7_BundleWriter_EnsureDirectory_Ugly(testingT *core.T) { + root := testingT.TempDir() + writer := localBundleWriter{Root: root} + err := writer.EnsureDirectory("") + core.AssertNoError(testingT, err) + core.AssertTrue(testingT, core.Stat(root).OK) +} + +func TestAX7_BundleWriter_WriteFile_Good(testingT *core.T) { + root := testingT.TempDir() + writer := localBundleWriter{Root: root} + core.RequireNoError(testingT, writer.EnsureDirectory("bundle/rom")) + err := writer.WriteFile("bundle/rom/game.bin", []byte("rom")) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "rom", string(core.ReadFile(core.Path(root, "bundle/rom/game.bin")).Value.([]byte))) +} + +func TestAX7_BundleWriter_WriteFile_Bad(testingT *core.T) { + root := testingT.TempDir() + writer := localBundleWriter{Root: root} + err := writer.WriteFile("missing/game.bin", []byte("rom")) + core.AssertError(testingT, err) + core.AssertNotNil(testingT, err) +} + +func TestAX7_BundleWriter_WriteFile_Ugly(testingT *core.T) { + root := testingT.TempDir() + writer := localBundleWriter{Root: root} + err := writer.WriteFile("empty.bin", nil) + core.AssertNoError(testingT, err) + core.AssertEqual(testingT, "", string(core.ReadFile(core.Path(root, "empty.bin")).Value.([]byte))) +} diff --git a/cmd/core-play/bundle.go b/cmd/core-play/bundle.go index 2d4d05e..f0c72ca 100644 --- a/cmd/core-play/bundle.go +++ b/cmd/core-play/bundle.go @@ -7,7 +7,7 @@ import ( "os" "path" - "dappco.re/go/core" + core "dappco.re/go" "dappco.re/go/play" ) diff --git a/cmd/core-play/main.go b/cmd/core-play/main.go index f5968cc..573f200 100644 --- a/cmd/core-play/main.go +++ b/cmd/core-play/main.go @@ -7,7 +7,7 @@ import ( "os" "path" - "dappco.re/go/core" + core "dappco.re/go" "dappco.re/go/play" ) diff --git a/cmd/core-play/report.go b/cmd/core-play/report.go index 506b1c6..044d79c 100644 --- a/cmd/core-play/report.go +++ b/cmd/core-play/report.go @@ -5,7 +5,7 @@ import ( "os" "path" - "dappco.re/go/core" + core "dappco.re/go" "dappco.re/go/play" ) diff --git a/command.go b/command.go index 7723118..b9c4a63 100644 --- a/command.go +++ b/command.go @@ -1,6 +1,6 @@ package play -import "dappco.re/go/core" +import core "dappco.re/go" // Core Play command names. const ( @@ -62,10 +62,7 @@ func cmdPlay(opts core.Options) core.Result { bundlePath = "." } - return core.Result{ - Value: PlayRequest{BundlePath: bundlePath}, - OK: true, - } + return core.Ok(PlayRequest{BundlePath: bundlePath}) } func cmdPlayList(opts core.Options) core.Result { @@ -74,13 +71,10 @@ func cmdPlayList(opts core.Options) core.Result { root = "." } - return core.Result{ - Value: ListRequest{ - Root: root, - JSON: opts.Bool("json"), - }, - OK: true, - } + return core.Ok(ListRequest{ + Root: root, + JSON: opts.Bool("json"), + }) } func cmdPlayVerify(opts core.Options) core.Result { @@ -89,61 +83,52 @@ func cmdPlayVerify(opts core.Options) core.Result { bundlePath = "." } - return core.Result{ - Value: VerifyRequest{BundlePath: bundlePath}, - OK: true, - } + return core.Ok(VerifyRequest{BundlePath: bundlePath}) } func cmdPlayBundle(opts core.Options) core.Result { - return core.Result{ - Value: BundleRequest{ - Name: opts.String("name"), - Title: opts.String("title"), - Author: opts.String("author"), - Year: opts.Int("year"), - Platform: opts.String("platform"), - Genre: opts.String("genre"), - Licence: opts.String("licence"), - Engine: opts.String("engine"), - Profile: opts.String("profile"), - Acceleration: AccelerationMode(firstOptionString(opts, "acceleration")), - Filter: FrameFilter(firstOptionString(opts, "filter")), - ArtefactPath: firstOptionString(opts, "rom", "artefact", "artefact_path"), - ArtefactData: optionBytes(opts, "artefact_data", "rom_data"), - ArtefactSHA256: firstOptionString(opts, "artefact_sha256", "sha256"), - ArtefactSize: firstOptionInt64(opts, "artefact_size", "size"), - ArtefactMediaType: firstOptionString(opts, "artefact_media_type", "media_type"), - ArtefactSource: opts.String("source"), - EngineBinaryPath: firstOptionString(opts, "engine_binary", "engine-binary"), - EngineBinaryData: optionBytes(opts, "engine_binary_data"), - EngineBinarySHA256: firstOptionString( - opts, - "engine_binary_sha256", - "engine-sha256", - ), - ResourceLimits: ResourceLimits{ - CPUPercent: firstOptionInt(opts, "cpu_percent", "cpu-percent"), - MemoryBytes: firstOptionInt64(opts, "memory_bytes", "memory-bytes"), - }, - DistributionMode: firstOptionString(opts, "distribution_mode", "distribution"), - BYOROM: opts.Bool("byorom"), - Entrypoint: opts.String("entrypoint"), - RuntimeConfigPath: opts.String("config"), - VerificationChain: opts.String("chain"), - SBOMPath: opts.String("sbom"), - SavePath: firstOptionString(opts, "save_path", "save"), - ScreenshotPath: firstOptionString(opts, "screenshot_path", "screenshots"), + return core.Ok(BundleRequest{ + Name: opts.String("name"), + Title: opts.String("title"), + Author: opts.String("author"), + Year: opts.Int("year"), + Platform: opts.String("platform"), + Genre: opts.String("genre"), + Licence: opts.String("licence"), + Engine: opts.String("engine"), + Profile: opts.String("profile"), + Acceleration: AccelerationMode(firstOptionString(opts, "acceleration")), + Filter: FrameFilter(firstOptionString(opts, "filter")), + ArtefactPath: firstOptionString(opts, "rom", "artefact", "artefact_path"), + ArtefactData: optionBytes(opts, "artefact_data", "rom_data"), + ArtefactSHA256: firstOptionString(opts, "artefact_sha256", "sha256"), + ArtefactSize: firstOptionInt64(opts, "artefact_size", "size"), + ArtefactMediaType: firstOptionString(opts, "artefact_media_type", "media_type"), + ArtefactSource: opts.String("source"), + EngineBinaryPath: firstOptionString(opts, "engine_binary", "engine-binary"), + EngineBinaryData: optionBytes(opts, "engine_binary_data"), + EngineBinarySHA256: firstOptionString( + opts, + "engine_binary_sha256", + "engine-sha256", + ), + ResourceLimits: ResourceLimits{ + CPUPercent: firstOptionInt(opts, "cpu_percent", "cpu-percent"), + MemoryBytes: firstOptionInt64(opts, "memory_bytes", "memory-bytes"), }, - OK: true, - } + DistributionMode: firstOptionString(opts, "distribution_mode", "distribution"), + BYOROM: opts.Bool("byorom"), + Entrypoint: opts.String("entrypoint"), + RuntimeConfigPath: opts.String("config"), + VerificationChain: opts.String("chain"), + SBOMPath: opts.String("sbom"), + SavePath: firstOptionString(opts, "save_path", "save"), + ScreenshotPath: firstOptionString(opts, "screenshot_path", "screenshots"), + }) } func cmdPlayEngines(core.Options) core.Result { - return core.Result{ - Value: RegisteredEngines(), - OK: true, - } + return core.Ok(RegisteredEngines()) } // PlayRequest describes a request to prepare a bundle for launch. diff --git a/command_test.go b/command_test.go index 23861db..ad643f6 100644 --- a/command_test.go +++ b/command_test.go @@ -3,7 +3,7 @@ package play import ( "testing" - "dappco.re/go/core" + core "dappco.re/go" ) func TestCommand_Commands_Good(testingT *testing.T) { @@ -60,6 +60,9 @@ func TestCommand_Register_Bad(testingT *testing.T) { testingT.Parallel() Register(nil) + if Commands()[0] != CommandPlay { + testingT.Fatalf("Register(nil) altered command ordering") + } } func TestCommand_Register_Ugly(testingT *testing.T) { diff --git a/dosbox_init.go b/dosbox_init.go index 5ac1e67..ba99cfb 100644 --- a/dosbox_init.go +++ b/dosbox_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(DOSBoxEngine{Binary: "dosbox"}) + if err := RegisterEngine(DOSBoxEngine{Binary: "dosbox"}); err != nil { + panic(err) + } } diff --git a/dosboxx_init.go b/dosboxx_init.go index 3e9d4c6..0933fd4 100644 --- a/dosboxx_init.go +++ b/dosboxx_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(DOSBoxXEngine{Binary: "dosbox-x"}) + if err := RegisterEngine(DOSBoxXEngine{Binary: "dosbox-x"}); err != nil { + panic(err) + } } diff --git a/engine.go b/engine.go index b593a74..59beb78 100644 --- a/engine.go +++ b/engine.go @@ -5,7 +5,7 @@ import ( "io" "sort" - "dappco.re/go/core" + core "dappco.re/go" ) // Engine defines a runnable STIM runtime adapter. diff --git a/fuse_init.go b/fuse_init.go index 967fe6b..8c17dbf 100644 --- a/fuse_init.go +++ b/fuse_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(FUSEEngine{Binary: "fuse"}) + if err := RegisterEngine(FUSEEngine{Binary: "fuse"}); err != nil { + panic(err) + } } diff --git a/go.mod b/go.mod index 2d389cc..ce2256d 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,10 @@ go 1.26.0 require gopkg.in/yaml.v3 v3.0.1 -require dappco.re/go/core v0.8.0-alpha.1 +require dappco.re/go v0.9.0 -require github.com/kr/text v0.2.0 // indirect +require ( + github.com/kr/pretty v0.3.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect +) diff --git a/go.sum b/go.sum index 5092fed..54e1e07 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,17 @@ -dappco.re/go/core v0.8.0-alpha.1 h1:gj7+Scv+L63Z7wMxbJYHhaRFkHJo2u4MMPuUSv/Dhtk= -dappco.re/go/core v0.8.0-alpha.1/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A= +dappco.re/go v0.9.0 h1:4ruZRNqKDDva8o6g65tYggjGVe42E6/lMZfVKXtr3p0= +dappco.re/go v0.9.0/go.mod h1:xapr7fLK4/9Pu2iSCr4qZuIuatmtx1j56zS/oPDbGyQ= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/mame_init.go b/mame_init.go index 048ca82..4cab092 100644 --- a/mame_init.go +++ b/mame_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(MAMEEngine{Binary: "mame"}) + if err := RegisterEngine(MAMEEngine{Binary: "mame"}); err != nil { + panic(err) + } } diff --git a/retroarch_init.go b/retroarch_init.go index e451d98..2fb24a7 100644 --- a/retroarch_init.go +++ b/retroarch_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(RetroArchEngine{Binary: "retroarch"}) + if err := RegisterEngine(RetroArchEngine{Binary: "retroarch"}); err != nil { + panic(err) + } } diff --git a/sbom.go b/sbom.go index 6566fed..8c3e60a 100644 --- a/sbom.go +++ b/sbom.go @@ -3,7 +3,7 @@ package play import ( "bytes" - "dappco.re/go/core" + core "dappco.re/go" ) const ( diff --git a/scummvm.go b/scummvm.go index 0ecf74c..e293753 100644 --- a/scummvm.go +++ b/scummvm.go @@ -5,7 +5,7 @@ import ( "path" "strconv" - "dappco.re/go/core" + core "dappco.re/go" ) // ScummVMEngine is a first-pass ScummVM adapter scaffold. diff --git a/scummvm_init.go b/scummvm_init.go index ef61007..3154c39 100644 --- a/scummvm_init.go +++ b/scummvm_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(ScummVMEngine{Binary: "scummvm"}) + if err := RegisterEngine(ScummVMEngine{Binary: "scummvm"}); err != nil { + panic(err) + } } diff --git a/scummvm_test.go b/scummvm_test.go index 6bac519..63ed5fe 100644 --- a/scummvm_test.go +++ b/scummvm_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "dappco.re/go/core" + core "dappco.re/go" ) func TestScummvm_Verify_Good(testingT *testing.T) { @@ -77,7 +77,7 @@ func TestScummvm_ProcessVerify_Ugly(testingT *testing.T) { c := core.New() c.Action("process.run", func(context.Context, core.Options) core.Result { - return core.Result{Value: "missing scummvm", OK: false} + return core.Fail(core.NewError("missing scummvm")) }) err := (ScummVMEngine{Binary: "scummvm", Core: c}).Verify() @@ -119,7 +119,7 @@ func TestScummvm_PlanLaunch_Good(testingT *testing.T) { func scummVMCore(versionOutput string) *core.Core { c := core.New() c.Action("process.run", func(context.Context, core.Options) core.Result { - return core.Result{Value: versionOutput, OK: true} + return core.Ok(versionOutput) }) return c diff --git a/snes9x_init.go b/snes9x_init.go index 8923645..e803fbf 100644 --- a/snes9x_init.go +++ b/snes9x_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(Snes9xEngine{Binary: "snes9x"}) + if err := RegisterEngine(Snes9xEngine{Binary: "snes9x"}); err != nil { + panic(err) + } } diff --git a/synthetic.go b/synthetic.go index 1065c65..35500ae 100644 --- a/synthetic.go +++ b/synthetic.go @@ -1,6 +1,6 @@ package play -import "dappco.re/go/core" +import core "dappco.re/go" // SyntheticEngine is a smoke-test engine that runs no external emulator. type SyntheticEngine struct{} diff --git a/synthetic_init.go b/synthetic_init.go index 7509747..0e9f2da 100644 --- a/synthetic_init.go +++ b/synthetic_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(SyntheticEngine{}) + if err := RegisterEngine(SyntheticEngine{}); err != nil { + panic(err) + } } diff --git a/threat.go b/threat.go index c88ba7e..6b5b021 100644 --- a/threat.go +++ b/threat.go @@ -6,7 +6,7 @@ import ( "io/fs" "path" - "dappco.re/go/core" + core "dappco.re/go" ) const ( diff --git a/verify.go b/verify.go index cce8fae..82f0607 100644 --- a/verify.go +++ b/verify.go @@ -234,7 +234,7 @@ func verifyChecksumCoverage(filesystem fs.FS, manifest Manifest, recordedPaths m var issues ValidationErrors chainPath := normaliseBundlePath(manifest.Verification.Chain) - _ = fs.WalkDir(filesystem, ".", func(candidatePath string, entry fs.DirEntry, walkErr error) error { + if err := fs.WalkDir(filesystem, ".", func(candidatePath string, entry fs.DirEntry, walkErr error) error { if walkErr != nil || entry.IsDir() { return nil } @@ -254,7 +254,13 @@ func verifyChecksumCoverage(filesystem fs.FS, manifest Manifest, recordedPaths m }) return nil - }) + }); err != nil { + issues = append(issues, ValidationIssue{ + Code: "hash/coverage-failed", + Field: "verification.chain", + Message: "bundle file coverage could not be checked", + }) + } return issues } diff --git a/vice_init.go b/vice_init.go index 5f48df9..f8778d8 100644 --- a/vice_init.go +++ b/vice_init.go @@ -3,5 +3,7 @@ package play func init() { - _ = RegisterEngine(VICEEngine{Binary: "x64sc"}) + if err := RegisterEngine(VICEEngine{Binary: "x64sc"}); err != nil { + panic(err) + } }