Files
RASCSI/go/piscsi-ctrlboard/attach_menu_test.go
Daniel Markstedt 485298ffe5 restore X68000 Host Bridge support
Restore the SCBR device as an X68000 Host Bridge, including the Human68k-compatible host file-system service and the bridge-specific SCSI message handlers. Back its Ethernet path with the TAP driver, derive a stable locally administered MAC address, preserve the driver minimum-frame/FCS expectations, and restrict received traffic to its own unicast frames and broadcasts.

Match the RASCTL/RASETHER/RASDRV bridge protocol that originated in the parent RaSCSI project: dispatch its non-standard message format on READ(10)/WRITE(10) (0x28/0x2a), with type, function, phase, and a three-byte byte count carried in the CDB. Advertise the legacy control channel required by RASCTL, translate its list and device-management commands to PiSCSI operations, and defer stop or system-shutdown requests until after the response is sent, while gating this insecure interface behind a new '-c disabled|media|full' runtime option. Preserve the type-1 Ethernet exchange used by RASETHER and the type-2, phased filesystem exchange used by RASDRV.

Register the device in the factory, protobuf API, controller transfer path, command-line tooling, device listings, and web UI. Share the explicit bridge and proxy-ARP topology workflow with DaynaPort so both adapters validate and use the same mode/interface pair.

Add build support for iconv where needed, extend unit coverage for device creation, RAS* protocol handling, CLI parsing, web attachment, and topology selection, and update the man pages and network-profile documentation for Host Bridge operation and its networking limits.
2026-09-01 00:52:00 +02:00

117 lines
4.3 KiB
Go

// Copyright 2026 Daniel Markstedt. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
package ctrlboard
import (
"testing"
pb "github.com/piscsi/piscsi/go/proto"
)
func TestDeviceTypeMenuUsesDaemonTypesAndRetainsSlot(t *testing.T) {
slot := SCSISlot{ID: 6}
menu, err := NewDeviceTypeMenu(slot, []*pb.PbDeviceTypeProperties{
{Type: pb.PbDeviceType_SAHD},
{Type: pb.PbDeviceType_SCLP, Properties: &pb.PbDeviceProperties{DefaultParams: map[string]string{"cmd": "custom"}}},
{Type: pb.PbDeviceType_SCHD, Properties: &pb.PbDeviceProperties{SupportsFile: true}},
}, 4)
if err != nil {
t.Fatal(err)
}
if got, want := menu.Items[1].Label, "SCSI Hard Disk [SCHD]"; got != want {
t.Fatalf("first device = %q, want %q", got, want)
}
if got, want := menu.Items[3].Label, "SASI Hard Disk [SAHD]"; got != want {
t.Fatalf("last device = %q, want %q", got, want)
}
selection, ok := menu.Items[2].Data.(DeviceTypeSelection)
if !ok || selection.Slot.ID != 6 || selection.Type != pb.PbDeviceType_SCLP {
t.Fatalf("printer selection = %#v", menu.Items[2].Data)
}
}
func TestNetworkTopologyMenuOnlyOffersReadySupportedProfiles(t *testing.T) {
menu, err := NewNetworkTopologyMenu(SCSISlot{ID: 2}, pb.PbDeviceType_SCDP, []*pb.PbNetworkInterface{
{Name: "wlan0", Up: true, SupportedMode: []string{"proxyarp", "ignored"}},
{Name: "eth0", Up: true, SupportedMode: []string{"bridge"}},
{Name: "eth1", Up: false, SupportedMode: []string{"bridge"}},
}, 4)
if err != nil {
t.Fatal(err)
}
if got, want := menu.Items[1].Label, "Wired bridge (eth0)"; got != want {
t.Fatalf("first topology = %q, want %q", got, want)
}
if got, want := menu.Items[2].Label, "Wi-Fi proxy ARP (wlan0)"; got != want {
t.Fatalf("second topology = %q, want %q", got, want)
}
selection, ok := menu.Items[1].Data.(NetworkTopologySelection)
if !ok || selection.Type != pb.PbDeviceType_SCDP || selection.Mode != "bridge" || selection.Interface != "eth0" {
t.Fatalf("topology selection = %#v", menu.Items[1].Data)
}
}
func TestNetworkTopologyMenuRetainsHostBridgeType(t *testing.T) {
menu, err := NewNetworkTopologyMenu(SCSISlot{ID: 2}, pb.PbDeviceType_SCBR, []*pb.PbNetworkInterface{
{Name: "eth0", Up: true, SupportedMode: []string{"bridge"}},
}, 4)
if err != nil {
t.Fatal(err)
}
selection, ok := menu.Items[1].Data.(NetworkTopologySelection)
if !ok || selection.Type != pb.PbDeviceType_SCBR {
t.Fatalf("topology selection = %#v", menu.Items[1].Data)
}
}
func TestDeviceOptionMenuCopiesDefaultParams(t *testing.T) {
defaults := map[string]string{"cmd": "original"}
menu, err := NewDeviceOptionMenu(DeviceTypeSelection{
Slot: SCSISlot{ID: 1}, Type: pb.PbDeviceType_SCLP,
Properties: &pb.PbDeviceProperties{DefaultParams: defaults},
}, 4)
if err != nil {
t.Fatal(err)
}
selection := menu.Items[1].Data.(DeviceAttachSelection)
selection.Params["cmd"] = "changed"
if got := defaults["cmd"]; got != "original" {
t.Fatalf("source default params mutated to %q", got)
}
}
func TestPrinterOptionShowsEffectiveDefaultCommand(t *testing.T) {
menu, err := NewDeviceOptionMenu(DeviceTypeSelection{Type: pb.PbDeviceType_SCLP}, 4)
if err != nil {
t.Fatal(err)
}
if got, want := menu.Items[1].Label, "Attach Printer (lp -oraw %f)"; got != want {
t.Fatalf("printer option = %q, want %q", got, want)
}
}
func TestAddAttachWithoutMediaOptionAddsOnlyRemovableChoice(t *testing.T) {
menu, err := NewMenu("Select Image", []MenuItem{{ID: "return", Label: "Return"}, {ID: "image:disk", Label: "disk"}}, 4)
if err != nil {
t.Fatal(err)
}
AddAttachWithoutMediaOption(menu, DeviceTypeSelection{
Slot: SCSISlot{ID: 5}, Type: pb.PbDeviceType_SCMO,
Properties: &pb.PbDeviceProperties{Removable: true},
})
if got, want := menu.Items[1].Label, "Attach with no media"; got != want {
t.Fatalf("naked attach option = %q, want %q", got, want)
}
selection, ok := menu.Items[1].Data.(DeviceAttachSelection)
if !ok || selection.Slot.ID != 5 || selection.Type != pb.PbDeviceType_SCMO {
t.Fatalf("naked attach selection = %#v", menu.Items[1].Data)
}
AddAttachWithoutMediaOption(menu, DeviceTypeSelection{Type: pb.PbDeviceType_SCHD})
if got, want := len(menu.Items), 3; got != want {
t.Fatalf("non-removable option count = %d, want %d", got, want)
}
}