package renode import ( "context" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "gitlab.hive-brain.de/MDietermann/Archduino/internal/config" "gitlab.hive-brain.de/MDietermann/Archduino/internal/module" "gitlab.hive-brain.de/MDietermann/Archduino/internal/platform" ) type fakeRunner struct { runCalls, outCalls [][]string installed map[string]bool } func (f *fakeRunner) Run(_ context.Context, name string, args ...string) error { f.runCalls = append(f.runCalls, append([]string{name}, args...)) return nil } func (f *fakeRunner) Output(_ context.Context, name string, args ...string) ([]byte, error) { f.outCalls = append(f.outCalls, append([]string{name}, args...)) if name == "pacman" && len(args) == 2 && args[0] == "-Q" { if f.installed[args[1]] { return []byte("ok"), nil } return nil, errExit1 } return nil, nil } type exitOne struct{} func (exitOne) Error() string { return "exit 1" } var errExit1 = exitOne{} type nopReporter struct{} func (nopReporter) Progress(string, int, int, string) {} func (nopReporter) Log(string, string) {} func sampleCfg(url string) config.RenodeConfig { return config.RenodeConfig{ WokwiVersion: "0.28.1", WokwiURL: url, Platforms: []config.RenodePlatform{ {Name: "stm32f4", ReplPath: "platforms/boards/stm32f4_discovery.repl", Machine: "STM32F4_Discovery"}, {Name: "rp2040", ReplPath: "platforms/cpus/rp2040.repl", Machine: "RP2040"}, }, } } func TestDetectNothingInstalled(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) r := &fakeRunner{installed: map[string]bool{}} m := New(sampleCfg("http://x/wokwi"), platform.NewPacman(r), platform.NewYay(r)) st, err := m.Detect(context.Background()) if err != nil { t.Fatal(err) } s := st.(state) if s.RenodeInstalled || s.GdbInstalled || s.SimScriptCurrent || s.WokwiInstalled { t.Fatalf("want all absent, got %+v", s) } } func TestDetectAllInstalled(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) os.MkdirAll(filepath.Join(home, ".local", "bin"), 0o755) os.WriteFile(filepath.Join(home, ".local", "bin", "wokwi-cli"), []byte("x"), 0o755) r := &fakeRunner{installed: map[string]bool{"renode-bin": true, "arm-none-eabi-gdb": true}} m := New(sampleCfg("http://x/wokwi"), platform.NewPacman(r), platform.NewYay(r)) // Apply once to seed the sim script, then re-detect. st, _ := m.Detect(context.Background()) p, _ := m.Plan(context.Background(), st) _ = m.Apply(context.Background(), p, nopReporter{}) st2, _ := m.Detect(context.Background()) p2, _ := m.Plan(context.Background(), st2) if !p2.IsEmpty() { t.Fatalf("second plan not empty: %+v", p2.Actions) } } func TestApplyRendersSimScriptAndDownloadsWokwi(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Write([]byte("wokwi-binary")) })) defer srv.Close() r := &fakeRunner{installed: map[string]bool{"yay": true}} m := New(sampleCfg(srv.URL), platform.NewPacman(r), platform.NewYay(r)) st, _ := m.Detect(context.Background()) p, _ := m.Plan(context.Background(), st) if err := m.Apply(context.Background(), p, nopReporter{}); err != nil { t.Fatal(err) } // arduino-sim rendered with both platforms sim, err := os.ReadFile(filepath.Join(home, ".local", "bin", "arduino-sim")) if err != nil { t.Fatalf("arduino-sim not written: %v", err) } if !strings.Contains(string(sim), "stm32f4)") { t.Errorf("stm32f4 branch missing: %s", sim) } if !strings.Contains(string(sim), "rp2040)") { t.Errorf("rp2040 branch missing: %s", sim) } info, _ := os.Stat(filepath.Join(home, ".local", "bin", "arduino-sim")) if info.Mode().Perm() != 0o755 { t.Errorf("arduino-sim mode %v", info.Mode().Perm()) } // wokwi-cli downloaded wokwi, err := os.ReadFile(filepath.Join(home, ".local", "bin", "wokwi-cli")) if err != nil { t.Fatalf("wokwi-cli missing: %v", err) } if string(wokwi) != "wokwi-binary" { t.Errorf("wokwi-cli content %q", wokwi) } // yay install renode-bin called sawYay := false for _, c := range r.runCalls { if c[0] == "yay" && len(c) >= 2 && c[1] == "-S" { sawYay = true } } if !sawYay { t.Fatalf("yay install missing; calls=%v", r.runCalls) } } var _ module.Module = Module{}