Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions pkg/espflasher/connect_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,56 @@ func TestConnectStatusNilCallbackNoBehaviorChange(t *testing.T) {
}
}

// TestConnectSkipsStubWhenSkipStubSet verifies that with SkipStub=true,
// connect() does not upload the stub loader even for a chip that has one.
func TestConnectSkipsStubWhenSkipStubSet(t *testing.T) {
var loadStubCalled bool
mc := esp32DetectMockConnection()
mc.loadStubFunc = func(s *stub) error {
loadStubCalled = true
return nil
}

f := &Flasher{
port: &mockPort{},
conn: mc,
opts: &FlasherOptions{
ChipType: ChipAuto,
ResetMode: ResetNoReset,
ConnectAttempts: 7,
SkipStub: true,
},
}

if err := f.connect(); err != nil {
t.Fatalf("connect() error: %v", err)
}
if loadStubCalled {
t.Error("loadStub should not be called when SkipStub is true")
}
}

// TestConnectLoadsStubByDefault verifies that with SkipStub left at its
// zero value (false), connect() still uploads the stub loader for a chip
// that has one — i.e. today's behavior is unchanged.
func TestConnectLoadsStubByDefault(t *testing.T) {
var loadStubCalled bool
mc := esp32DetectMockConnection()
mc.loadStubFunc = func(s *stub) error {
loadStubCalled = true
return nil
}

f := newConnectTestFlasher(mc, 7, nil)

if err := f.connect(); err != nil {
t.Fatalf("connect() error: %v", err)
}
if !loadStubCalled {
t.Error("loadStub should be called by default (SkipStub=false)")
}
}

// Note: a test asserting the attempt counter increments across a
// failed-then-succeeding retry sequence was considered but is infeasible
// with the existing mock infra. When every sync try in an attempt fails,
Expand Down
14 changes: 13 additions & 1 deletion pkg/espflasher/flasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ type FlasherOptions struct {
// port opens (initial and reopen-after-USB-reenumeration). Useful for
// callers that multiplex port access across a monitor and the flasher.
SerialOpener func(name string, mode *serial.Mode) (serial.Port, error)

// SkipStub, when true, skips uploading the stub loader during connect.
// Stub-dependent features (whole-chip erase, compression, etc.) are
// unavailable, but ROM operations (register read/write, flash read/write
// via ROM, chip detect) still work. Useful for register-only workflows
// (e.g. GPIO probing) that don't need the stub, and for avoiding a
// resident stub that can cause a subsequent no-reset reconnect to
// mis-detect the chip.
// Default: false (stub is loaded as today).
SkipStub bool
}

// Logger is the interface for receiving progress and status messages.
Expand Down Expand Up @@ -447,7 +457,9 @@ synced:
}

// Upload the stub loader to enable advanced features (erase, compression, etc.).
if s, ok := stubFor(f.chip.ChipType); ok {
if f.opts.SkipStub {
f.logf("Skipping stub loader (SkipStub set); ROM bootloader only.")
} else if s, ok := stubFor(f.chip.ChipType); ok {
f.logf("Loading stub loader...")
f.connectStatus(ConnectPhaseLoadStub, 0, 0, "loading stub")
if err := f.conn.loadStub(s); err != nil {
Expand Down
Loading