-
Notifications
You must be signed in to change notification settings - Fork 47
fix(esp32): restore emulator float output and add regression coverage #1699
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
2f68148
fe2cb9f
6d61f9f
fdb7c48
e5411f2
158fd2c
250f41e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| package main | ||
|
|
||
| type point struct { | ||
| x float64 | ||
| y float64 | ||
| } | ||
|
|
||
| type myPoint = point | ||
|
|
||
| func (p *point) scale(factor float64) { | ||
| p.x *= factor | ||
| p.y *= factor | ||
| } | ||
|
|
||
| func (p *myPoint) move(dx, dy float64) { | ||
| p.x += dx | ||
| p.y += dy | ||
| } | ||
|
|
||
| func pair(f float64) (int, float64) { | ||
| return 1, f | ||
| } | ||
|
|
||
| type bar struct { | ||
| pb *byte | ||
| f float32 | ||
| } | ||
|
|
||
| type foo struct { | ||
| pb *byte | ||
| f float32 | ||
| } | ||
|
|
||
| func xadd(a, b int) int { | ||
| return a + b | ||
| } | ||
|
|
||
| func double(v float64) float64 { | ||
| return v * 2 | ||
| } | ||
|
|
||
| func main() { | ||
| pt := &myPoint{1, 2} | ||
| pt.scale(2) | ||
| pt.move(3, 4) | ||
| println(pt.x, pt.y) | ||
|
|
||
| i, f := pair(2.0) | ||
| println(i, f) | ||
|
|
||
| // Keep this case on the float-format path without triggering | ||
| // esp32 type-assert timeout cases tracked separately. | ||
| ret, ok := bar{}, false | ||
| println(ret.pb, ret.f, "notOk:", !ok) | ||
|
|
||
| ret2, ok2 := foo{}, true | ||
| println(ret2.pb, ret2.f, ok2) | ||
|
|
||
| println(xadd(1, 2), double(3.14)) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,58 @@ run_emulator_smoke() { | |
| fi | ||
| } | ||
|
|
||
| extract_last_nonempty_lines() { | ||
| local n="$1" | ||
| awk -v n="$n" ' | ||
| NF { out[++count] = $0 } | ||
| END { | ||
| if (n <= 0 || count == 0) { | ||
| exit | ||
| } | ||
| start = count - n + 1 | ||
| if (start < 1) { | ||
| start = 1 | ||
| } | ||
| for (i = start; i <= count; i++) { | ||
| print out[i] | ||
| } | ||
| }' | ||
| } | ||
|
|
||
| run_case_and_compare() { | ||
| local target="$1" | ||
| local case_dir="$2" | ||
| local expected="$3" | ||
| local raw_output | ||
| local actual | ||
| local expected_lines | ||
|
|
||
| echo "Running: llgo run -a -target=${target} -emulator ${case_dir}" | ||
| if ! raw_output=$(llgo run -a -target="${target}" -emulator "${case_dir}" 2>&1); then | ||
|
||
| echo "✗ FAIL: command failed for ${case_dir}" | ||
| echo "$raw_output" | ||
| return 1 | ||
| fi | ||
|
|
||
| expected_lines=$(printf "%s\n" "$expected" | awk 'NF { n++ } END { print n + 0 }') | ||
| actual=$(printf "%s\n" "$raw_output" | tr -d '\r' | extract_last_nonempty_lines "$expected_lines") | ||
| if [ "$actual" = "$expected" ]; then | ||
| echo "✓ PASS: $case_dir" | ||
| return 0 | ||
| fi | ||
|
|
||
| echo "✗ FAIL: output mismatch for $case_dir" | ||
| echo "Expected:" | ||
| printf "%s\n" "$expected" | ||
| echo "" | ||
| echo "Got:" | ||
| printf "%s\n" "$actual" | ||
| echo "" | ||
| echo "Diff:" | ||
| diff -u <(printf "%s\n" "$expected") <(printf "%s\n" "$actual") || true | ||
| return 1 | ||
| } | ||
|
|
||
| mkdir -p "$TEMP_DIR" | ||
|
|
||
| echo "==> Creating minimal test program..." | ||
|
|
@@ -98,7 +150,14 @@ run_emulator_smoke "esp32c3-basic" "ESP32-C3" "Hello World" | |
| build_target "esp32" "$ESP32_PREFIX" "ESP32" | ||
| run_emulator_smoke "esp32" "ESP32" "Hello World" | ||
|
|
||
| echo "" | ||
| echo "=== Regression: ESP32 float output (temporary) ===" | ||
|
||
| pushd "$SCRIPT_DIR" > /dev/null | ||
| run_case_and_compare "esp32" "./esp32/float-1685" $'+5.000000e+00 +8.000000e+00\n1 +2.000000e+00\n0x0 +0.000000e+00 notOk: true\n0x0 +0.000000e+00 true\n3 +6.280000e+00' | ||
| popd > /dev/null | ||
|
|
||
| echo "" | ||
| echo "=== Smoke Tests Passed ===" | ||
| echo "✓ ESP32-C3 build + emulator run passed" | ||
| echo "✓ ESP32 build + emulator run passed" | ||
| echo "✓ ESP32 float output regression cases match expected output" | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -558,8 +558,8 @@ func TestGetNewlibESP32ConfigXtensa(t *testing.T) { | |||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // Test Groups configuration | ||||||||||||||
| if len(config.Groups) != 3 { | ||||||||||||||
| t.Errorf("Expected 2 groups, got %d", len(config.Groups)) | ||||||||||||||
| if len(config.Groups) != 4 { | ||||||||||||||
| t.Errorf("Expected 4 groups, got %d", len(config.Groups)) | ||||||||||||||
| } else { | ||||||||||||||
| // Group 0: libcrt0 | ||||||||||||||
| group0 := config.Groups[0] | ||||||||||||||
|
|
@@ -631,6 +631,28 @@ func TestGetNewlibESP32ConfigXtensa(t *testing.T) { | |||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // Group 3: libm fpclassify symbols for _printf_float path. | ||||||||||||||
| group3 := config.Groups[3] | ||||||||||||||
| expectedOutput3 := "libm-fpclassify-" + target + ".a" | ||||||||||||||
| if group3.OutputFileName != expectedOutput3 { | ||||||||||||||
| t.Errorf("Group3 OutputFileName expected '%s', got '%s'", expectedOutput3, group3.OutputFileName) | ||||||||||||||
| } | ||||||||||||||
| for _, sample := range []string{ | ||||||||||||||
| filepath.Join(baseDir, "newlib", "libm", "common", "s_fpclassify.c"), | ||||||||||||||
| filepath.Join(baseDir, "newlib", "libm", "common", "sf_fpclassify.c"), | ||||||||||||||
| } { | ||||||||||||||
| found := false | ||||||||||||||
| for _, file := range group3.Files { | ||||||||||||||
| if file == sample { | ||||||||||||||
| found = true | ||||||||||||||
| break | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| if !found { | ||||||||||||||
| t.Errorf("Expected file '%s' not found in group3 files", sample) | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // Test LDFlags and CCFlags | ||||||||||||||
| if len(group0.LDFlags) == 0 { | ||||||||||||||
| t.Error("Expected non-empty LDFlags in group0") | ||||||||||||||
|
|
@@ -698,8 +720,8 @@ func TestGroupConfiguration(t *testing.T) { | |||||||||||||
|
|
||||||||||||||
| t.Run("Xtensa_GroupCount", func(t *testing.T) { | ||||||||||||||
| config := getNewlibESP32ConfigXtensa(baseDir, target) | ||||||||||||||
| if len(config.Groups) != 3 { | ||||||||||||||
| t.Errorf("Expected 2 groups for Xtensa, got %d", len(config.Groups)) | ||||||||||||||
| if len(config.Groups) != 4 { | ||||||||||||||
| t.Errorf("Expected 4 groups for Xtensa, got %d", len(config.Groups)) | ||||||||||||||
| } | ||||||||||||||
| }) | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -726,12 +748,11 @@ func TestGroupConfiguration(t *testing.T) { | |||||||||||||
| expectedNames := []string{ | ||||||||||||||
| "libcrt0-" + target + ".a", | ||||||||||||||
| "libgloss-" + target + ".a", | ||||||||||||||
| "libc-" + target + ".a", | ||||||||||||||
| "libm-fpclassify-" + target + ".a", | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| for i, group := range config.Groups { | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old guard Consider bounding the loop or adding a length check:
Suggested change
|
||||||||||||||
| if i >= len(expectedNames) { | ||||||||||||||
| return | ||||||||||||||
| } | ||||||||||||||
| if group.OutputFileName != expectedNames[i] { | ||||||||||||||
| t.Errorf("Group %d expected name '%s', got '%s'", i, expectedNames[i], group.OutputFileName) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
extract_last_nonempty_linesfunction is more complex than necessary and can be inefficient for large inputs as it buffers all non-empty lines in memory. You can achieve the same result more idiomatically and efficiently using a combination of standard shell utilities likegrepandtail.