Skip to content

Commit a94e2d2

Browse files
committed
gd test (android): export the test app with --headless
The android test scheduler runs via the engine's per-frame main loop, which is gated on the GPU. On a CI emulator there's only software GL (SwiftShader), so the frame loop crawls and the suite stalls after a couple of tests. Bake command_line/extra_args="--headless" into the chosen export preset for the test run (no rendering, no GPU dependency, faster everywhere) and restore the config afterwards so a real `gd build` is unaffected. Verified on Waydroid (android/amd64): exit 0, config restored.
1 parent df52a20 commit a94e2d2

1 file changed

Lines changed: 58 additions & 0 deletions

File tree

cmd/gd/internal/builder/android.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,15 @@ func (android Android) Test(args ...string) error {
403403
if err != nil {
404404
return xray.New(err)
405405
}
406+
// Run the test app with --headless so it doesn't depend on a GPU/render loop:
407+
// the test scheduler is driven by the engine's per-frame main loop, which
408+
// crawls on the software GL of a CI emulator. Restore the preset afterwards so
409+
// a real `gd build` for the same project is unaffected.
410+
restoreHeadless, err := bakeAndroidHeadless(presetName)
411+
if err != nil {
412+
return xray.New(err)
413+
}
414+
defer restoreHeadless()
406415
apkPath := filepath.Join(project.GraphicsDirectory, exportPath)
407416
if err := os.MkdirAll(filepath.Dir(apkPath), 0755); err != nil {
408417
return xray.New(err)
@@ -498,6 +507,55 @@ func printAndroidResults(log string) {
498507
}
499508
}
500509

510+
// bakeAndroidHeadless sets command_line/extra_args="--headless" on the named
511+
// export preset so the exported test app runs without rendering, and returns a
512+
// function that restores the original config (so a normal `gd build` is
513+
// unaffected).
514+
func bakeAndroidHeadless(presetName string) (restore func(), err error) {
515+
cfgPath := filepath.Join(project.GraphicsDirectory, "export_presets.cfg")
516+
original, err := os.ReadFile(cfgPath)
517+
if err != nil {
518+
return nil, err
519+
}
520+
// Find the [preset.N] whose name matches, then set the cmdline in its
521+
// [preset.N.options] section.
522+
lines := strings.Split(string(original), "\n")
523+
idx, cur := "", ""
524+
for _, line := range lines {
525+
s := strings.TrimSpace(line)
526+
if strings.HasPrefix(s, "[preset.") && !strings.HasSuffix(s, ".options]") {
527+
cur = strings.TrimSuffix(strings.TrimPrefix(s, "[preset."), "]")
528+
} else if name, ok := strings.CutPrefix(s, "name="); ok && strings.Trim(name, `"`) == presetName {
529+
idx = cur
530+
break
531+
}
532+
}
533+
if idx == "" {
534+
return nil, fmt.Errorf("preset %q not found in %s", presetName, cfgPath)
535+
}
536+
optionsHeader := "[preset." + idx + ".options]"
537+
inOptions, set := false, false
538+
for i, line := range lines {
539+
s := strings.TrimSpace(line)
540+
if strings.HasPrefix(s, "[") {
541+
inOptions = s == optionsHeader
542+
continue
543+
}
544+
if inOptions && strings.HasPrefix(s, "command_line/extra_args=") {
545+
lines[i] = `command_line/extra_args="--headless"`
546+
set = true
547+
break
548+
}
549+
}
550+
if !set {
551+
return nil, fmt.Errorf("command_line/extra_args not found for preset %q", presetName)
552+
}
553+
if err := os.WriteFile(cfgPath, []byte(strings.Join(lines, "\n")), 0o644); err != nil {
554+
return nil, err
555+
}
556+
return func() { _ = os.WriteFile(cfgPath, original, 0o644) }, nil
557+
}
558+
501559
// lastSentinel returns the exit code from the last "GDTEST_DONE <code>" line
502560
// emitted by the test binary's TestMain (see internal/main_android_test.go).
503561
func lastSentinel(s string) (code int, ok bool) {

0 commit comments

Comments
 (0)