Skip to content

Commit 8a4aa01

Browse files
committed
tests: add test coverage
Signed-off-by: deadprogram <ron@hybridgroup.com>
1 parent cd94fd2 commit 8a4aa01

File tree

6 files changed

+1560
-0
lines changed

6 files changed

+1560
-0
lines changed

chip_test.go

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
package espflash
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestChipTypeString(t *testing.T) {
8+
tests := []struct {
9+
chip ChipType
10+
expected string
11+
}{
12+
{ChipESP8266, "ESP8266"},
13+
{ChipESP32, "ESP32"},
14+
{ChipESP32S2, "ESP32-S2"},
15+
{ChipESP32S3, "ESP32-S3"},
16+
{ChipESP32C2, "ESP32-C2"},
17+
{ChipESP32C3, "ESP32-C3"},
18+
{ChipESP32C6, "ESP32-C6"},
19+
{ChipESP32H2, "ESP32-H2"},
20+
{ChipAuto, "Auto"},
21+
{ChipType(99), "Unknown(99)"},
22+
}
23+
24+
for _, tt := range tests {
25+
t.Run(tt.expected, func(t *testing.T) {
26+
got := tt.chip.String()
27+
if got != tt.expected {
28+
t.Errorf("ChipType(%d).String() = %q, want %q", int(tt.chip), got, tt.expected)
29+
}
30+
})
31+
}
32+
}
33+
34+
func TestChipDefsCompleteness(t *testing.T) {
35+
// Every chip type (except ChipAuto) should have a definition.
36+
expectedChips := []ChipType{
37+
ChipESP8266,
38+
ChipESP32,
39+
ChipESP32S2,
40+
ChipESP32S3,
41+
ChipESP32C2,
42+
ChipESP32C3,
43+
ChipESP32C6,
44+
ChipESP32H2,
45+
}
46+
47+
for _, ct := range expectedChips {
48+
def, ok := chipDefs[ct]
49+
if !ok {
50+
t.Errorf("chipDefs missing definition for %s", ct)
51+
continue
52+
}
53+
if def.ChipType != ct {
54+
t.Errorf("chipDefs[%s].ChipType = %s, want %s", ct, def.ChipType, ct)
55+
}
56+
if def.Name == "" {
57+
t.Errorf("chipDefs[%s].Name is empty", ct)
58+
}
59+
if def.FlashSizes == nil || len(def.FlashSizes) == 0 {
60+
t.Errorf("chipDefs[%s].FlashSizes is empty", ct)
61+
}
62+
if def.FlashFrequency == nil || len(def.FlashFrequency) == 0 {
63+
t.Errorf("chipDefs[%s].FlashFrequency is empty", ct)
64+
}
65+
}
66+
}
67+
68+
func TestDetectChipByMagic(t *testing.T) {
69+
tests := []struct {
70+
name string
71+
magic uint32
72+
expected ChipType
73+
found bool
74+
}{
75+
{"ESP8266", 0xFFF0C101, ChipESP8266, true},
76+
{"ESP32", 0x00F01D83, ChipESP32, true},
77+
{"ESP32-S2", 0x000007C6, ChipESP32S2, true},
78+
{"unknown magic", 0xDEADBEEF, 0, false},
79+
{"zero magic", 0x00000000, 0, false},
80+
}
81+
82+
for _, tt := range tests {
83+
t.Run(tt.name, func(t *testing.T) {
84+
def := detectChipByMagic(tt.magic)
85+
if tt.found {
86+
if def == nil {
87+
t.Fatalf("detectChipByMagic(0x%08X) = nil, want %s", tt.magic, tt.expected)
88+
}
89+
if def.ChipType != tt.expected {
90+
t.Errorf("detectChipByMagic(0x%08X).ChipType = %s, want %s",
91+
tt.magic, def.ChipType, tt.expected)
92+
}
93+
} else {
94+
if def != nil {
95+
t.Errorf("detectChipByMagic(0x%08X) = %s, want nil", tt.magic, def.ChipType)
96+
}
97+
}
98+
})
99+
}
100+
}
101+
102+
func TestDefaultFlashSizes(t *testing.T) {
103+
sizes := defaultFlashSizes()
104+
105+
expected := map[string]byte{
106+
"1MB": 0x00,
107+
"2MB": 0x10,
108+
"4MB": 0x20,
109+
"8MB": 0x30,
110+
"16MB": 0x40,
111+
"32MB": 0x50,
112+
"64MB": 0x60,
113+
"128MB": 0x70,
114+
}
115+
116+
for name, val := range expected {
117+
got, ok := sizes[name]
118+
if !ok {
119+
t.Errorf("defaultFlashSizes() missing %q", name)
120+
continue
121+
}
122+
if got != val {
123+
t.Errorf("defaultFlashSizes()[%q] = 0x%02X, want 0x%02X", name, got, val)
124+
}
125+
}
126+
}
127+
128+
func TestDefaultFlashSizesUpperNibble(t *testing.T) {
129+
// Flash size values should only occupy the upper nibble (bits 4-7).
130+
sizes := defaultFlashSizes()
131+
for name, val := range sizes {
132+
if val&0x0F != 0 {
133+
t.Errorf("defaultFlashSizes()[%q] = 0x%02X has non-zero lower nibble", name, val)
134+
}
135+
}
136+
}
137+
138+
func TestESP8266FlashSizes(t *testing.T) {
139+
// ESP8266 uses a different encoding than ESP32+.
140+
sizes := defESP8266.FlashSizes
141+
expected := map[string]byte{
142+
"512KB": 0x00,
143+
"256KB": 0x10,
144+
"1MB": 0x20,
145+
"2MB": 0x30,
146+
"4MB": 0x40,
147+
}
148+
for name, val := range expected {
149+
got, ok := sizes[name]
150+
if !ok {
151+
t.Errorf("ESP8266 FlashSizes missing %q", name)
152+
continue
153+
}
154+
if got != val {
155+
t.Errorf("ESP8266 FlashSizes[%q] = 0x%02X, want 0x%02X", name, got, val)
156+
}
157+
}
158+
}
159+
160+
func TestChipCapabilities(t *testing.T) {
161+
// ESP8266: no encrypted flash, no ROM compressed flash, no ROM change baud
162+
if defESP8266.SupportsEncryptedFlash {
163+
t.Error("ESP8266 should not support encrypted flash")
164+
}
165+
if defESP8266.ROMHasCompressedFlash {
166+
t.Error("ESP8266 should not have ROM compressed flash")
167+
}
168+
if defESP8266.ROMHasChangeBaud {
169+
t.Error("ESP8266 should not have ROM change baud")
170+
}
171+
172+
// ESP32: no encrypted flash support, but has ROM compressed flash and change baud
173+
if defESP32.SupportsEncryptedFlash {
174+
t.Error("ESP32 should not support encrypted flash")
175+
}
176+
if !defESP32.ROMHasCompressedFlash {
177+
t.Error("ESP32 should have ROM compressed flash")
178+
}
179+
if !defESP32.ROMHasChangeBaud {
180+
t.Error("ESP32 should have ROM change baud")
181+
}
182+
183+
// ESP32-S2 and newer: all capabilities
184+
newerChips := []*chipDef{defESP32S2, defESP32S3, defESP32C2, defESP32C3, defESP32C6, defESP32H2}
185+
for _, def := range newerChips {
186+
if !def.SupportsEncryptedFlash {
187+
t.Errorf("%s should support encrypted flash", def.Name)
188+
}
189+
if !def.ROMHasCompressedFlash {
190+
t.Errorf("%s should have ROM compressed flash", def.Name)
191+
}
192+
if !def.ROMHasChangeBaud {
193+
t.Errorf("%s should have ROM change baud", def.Name)
194+
}
195+
}
196+
}
197+
198+
func TestMagicValueChips(t *testing.T) {
199+
// ESP8266, ESP32, ESP32-S2 use magic value detection
200+
magicChips := []*chipDef{defESP8266, defESP32, defESP32S2}
201+
for _, def := range magicChips {
202+
if !def.UsesMagicValue {
203+
t.Errorf("%s should use magic value detection", def.Name)
204+
}
205+
if def.MagicValue == 0 {
206+
t.Errorf("%s magic value should not be 0", def.Name)
207+
}
208+
}
209+
210+
// Newer chips don't use magic value detection
211+
nonMagicChips := []*chipDef{defESP32C3, defESP32C6, defESP32H2}
212+
for _, def := range nonMagicChips {
213+
if def.UsesMagicValue {
214+
t.Errorf("%s should not use magic value detection", def.Name)
215+
}
216+
}
217+
}
218+
219+
func TestChipDetectMagicRegAddr(t *testing.T) {
220+
if chipDetectMagicRegAddr != 0x40001000 {
221+
t.Errorf("chipDetectMagicRegAddr = 0x%08X, want 0x40001000", chipDetectMagicRegAddr)
222+
}
223+
}
224+
225+
func TestFlashFrequencyNonEmpty(t *testing.T) {
226+
// All chips should have at least one flash frequency entry.
227+
for ct, def := range chipDefs {
228+
if len(def.FlashFrequency) == 0 {
229+
t.Errorf("%s (ChipType=%d) has no flash frequencies", def.Name, ct)
230+
}
231+
}
232+
233+
// ESP32-family chips (not C2/H2) should support "40m"
234+
for _, def := range []*chipDef{defESP8266, defESP32, defESP32S2, defESP32S3, defESP32C3, defESP32C6} {
235+
if _, ok := def.FlashFrequency["40m"]; !ok {
236+
t.Errorf("%s missing 40m flash frequency", def.Name)
237+
}
238+
}
239+
240+
// ESP32-C2 uses different freq names
241+
if _, ok := defESP32C2.FlashFrequency["60m"]; !ok {
242+
t.Error("ESP32-C2 missing 60m flash frequency")
243+
}
244+
245+
// ESP32-H2 uses different freq names
246+
if _, ok := defESP32H2.FlashFrequency["48m"]; !ok {
247+
t.Error("ESP32-H2 missing 48m flash frequency")
248+
}
249+
}
250+
251+
func TestBootloaderFlashOffset(t *testing.T) {
252+
// ESP8266 and some RISC-V chips use offset 0x0; ESP32/S2/S3 use 0x1000.
253+
if defESP8266.BootloaderFlashOffset != 0x0 {
254+
t.Errorf("ESP8266 bootloader offset = 0x%X, want 0x0", defESP8266.BootloaderFlashOffset)
255+
}
256+
if defESP32.BootloaderFlashOffset != 0x1000 {
257+
t.Errorf("ESP32 bootloader offset = 0x%X, want 0x1000", defESP32.BootloaderFlashOffset)
258+
}
259+
}

errors_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package espflash
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestCommandError(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
errCode byte
12+
contains string
13+
}{
14+
{"invalid message", 0x05, "received message is invalid"},
15+
{"failed to act", 0x06, "failed to act on received message"},
16+
{"invalid CRC", 0x07, "invalid CRC in message"},
17+
{"flash write", 0x08, "flash write error"},
18+
{"flash read", 0x09, "flash read error"},
19+
{"flash read length", 0x0A, "flash read length error"},
20+
{"deflate error", 0x0B, "deflate error"},
21+
{"unknown error", 0xFF, "unknown error"},
22+
}
23+
24+
for _, tt := range tests {
25+
t.Run(tt.name, func(t *testing.T) {
26+
err := &CommandError{
27+
OpCode: 0x02,
28+
Status: 0x01,
29+
ErrCode: tt.errCode,
30+
}
31+
msg := err.Error()
32+
if !strings.Contains(msg, tt.contains) {
33+
t.Errorf("CommandError.Error() = %q, want to contain %q", msg, tt.contains)
34+
}
35+
// Should include the opcode hex
36+
if !strings.Contains(msg, "0x02") {
37+
t.Errorf("CommandError.Error() = %q, should contain opcode 0x02", msg)
38+
}
39+
})
40+
}
41+
}
42+
43+
func TestCommandErrorFormat(t *testing.T) {
44+
err := &CommandError{OpCode: 0x10, Status: 0x01, ErrCode: 0x05}
45+
msg := err.Error()
46+
// Should have format: "command 0x10 failed: status=0x01 error=0x05 (received message is invalid)"
47+
if !strings.HasPrefix(msg, "command 0x10 failed:") {
48+
t.Errorf("unexpected format: %q", msg)
49+
}
50+
if !strings.Contains(msg, "status=0x01") {
51+
t.Errorf("missing status in: %q", msg)
52+
}
53+
if !strings.Contains(msg, "error=0x05") {
54+
t.Errorf("missing error code in: %q", msg)
55+
}
56+
}
57+
58+
func TestTimeoutError(t *testing.T) {
59+
err := &TimeoutError{Op: "sync"}
60+
msg := err.Error()
61+
if !strings.Contains(msg, "timeout") {
62+
t.Errorf("TimeoutError.Error() = %q, should contain 'timeout'", msg)
63+
}
64+
if !strings.Contains(msg, "sync") {
65+
t.Errorf("TimeoutError.Error() = %q, should contain op name", msg)
66+
}
67+
}
68+
69+
func TestSyncError(t *testing.T) {
70+
err := &SyncError{Attempts: 7}
71+
msg := err.Error()
72+
if !strings.Contains(msg, "sync") {
73+
t.Errorf("SyncError.Error() = %q, should contain 'sync'", msg)
74+
}
75+
if !strings.Contains(msg, "7") {
76+
t.Errorf("SyncError.Error() = %q, should contain attempt count", msg)
77+
}
78+
}
79+
80+
func TestChipDetectError(t *testing.T) {
81+
err := &ChipDetectError{MagicValue: 0xDEADBEEF}
82+
msg := err.Error()
83+
if !strings.Contains(msg, "detect") {
84+
t.Errorf("ChipDetectError.Error() = %q, should contain 'detect'", msg)
85+
}
86+
if !strings.Contains(msg, "DEADBEEF") {
87+
t.Errorf("ChipDetectError.Error() = %q, should contain magic value hex", msg)
88+
}
89+
}
90+
91+
func TestUnsupportedCommandError(t *testing.T) {
92+
err := &UnsupportedCommandError{Command: "erase flash (requires stub)"}
93+
msg := err.Error()
94+
if !strings.Contains(msg, "not supported") {
95+
t.Errorf("UnsupportedCommandError.Error() = %q, should contain 'not supported'", msg)
96+
}
97+
if !strings.Contains(msg, "erase flash") {
98+
t.Errorf("UnsupportedCommandError.Error() = %q, should contain command name", msg)
99+
}
100+
}
101+
102+
func TestErrorsImplementErrorInterface(t *testing.T) {
103+
// Verify all error types implement the error interface.
104+
var _ error = &CommandError{}
105+
var _ error = &TimeoutError{}
106+
var _ error = &SyncError{}
107+
var _ error = &ChipDetectError{}
108+
var _ error = &UnsupportedCommandError{}
109+
}

0 commit comments

Comments
 (0)