Skip to content

Conversation

@elijahr
Copy link
Contributor

@elijahr elijahr commented Feb 8, 2026

Problem

Tests occasionally fail with:

Uncaught SyntaxError: Got [ERASED] expected EOF

Reported in #4132 by @thyttan: tests fail initially but succeed on re-run.
Example: https://github.com/espruino/BangleApps/actions/runs/21783161416/attempts/1

Cause (suspected)

The test runner sends commands without waiting for the emulator to finish processing. When one test ends and the next begins, factoryReset() can be called while the previous test's commands are still being processed, corrupting the input stream.

Solution

  • Track emulator ready state (has it output a response?)
  • Add waitForResponse() to poll until command completes
  • Add safeFactoryReset() that ensures previous commands finished
  • Wait after each test step (cmd, emit, load, etc.)
  • Wait after app upload and reset in test setup

Testing

  • All functional tests pass locally and in CI
  • Full test suite (all 4 apps): https://github.com/elijahr/BangleApps/actions/runs/21789313739
  • No significant increase in test duration
  • Also detects test infrastructure changes as a condition for running all app tests in CI (bin/runapptests.js .github/workflows/nodejs.yml and core submodule).

## Problem

Tests occasionally fail with:
```
Uncaught SyntaxError: Got [ERASED] expected EOF
```

Reported in espruino#4132 by @thyttan: tests fail initially but succeed on re-run.
Example: https://github.com/espruino/BangleApps/actions/runs/21783161416/attempts/1

## Cause

The test runner sends commands without waiting for the emulator to finish
processing. When one test ends and the next begins, `factoryReset()` can be
called while the previous test's commands are still being processed, corrupting
the input stream.

## Solution

- Track emulator ready state (has it output a response?)
- Add `waitForResponse()` to poll until command completes
- Add `safeFactoryReset()` that ensures previous commands finished
- Wait after each test step (`cmd`, `emit`, `load`, etc.)
- Wait after app upload and reset in test setup

## Testing

- All functional tests pass locally and in CI
- Full test suite (all 4 apps): https://github.com/elijahr/BangleApps/actions/runs/21789313739
- No significant increase in test duration
When bin/runapptests.js, .github/workflows/nodejs.yml, or the core
submodule changes, run all functional tests instead of only tests
for changed apps. This ensures test infrastructure changes are
properly validated against the full test suite.
Copy link
Collaborator

@bobrippling bobrippling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the rapid fix, this is great - I have some small questions, but nothing major about the design etc :)

exit 0
else
# Push to other branches: test only changed apps since master
CHANGED=$(git diff --name-only origin/master...HEAD)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep this comment in, as it's not clear what case the else handles

Suggested change
CHANGED=$(git diff --name-only origin/master...HEAD)
# Push to other branches: test only changed apps since master
CHANGED=$(git diff --name-only origin/master...HEAD)

# Check if test infrastructure changed - if so, run all tests
RUN_ALL=false
for f in $TEST_INFRA_FILES; do
if echo "$CHANGED" | grep -q "^$f$"; then
Copy link
Collaborator

@bobrippling bobrippling Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's minor, but $f is treated as a regex when it isn't. We could:

Suggested change
if echo "$CHANGED" | grep -q "^$f$"; then
if echo "$CHANGED" | grep -qx "$f"; then

Or we pull out the refspec above and one-shot this without needing to loop:

# e.g. refspec is origin/master...HEAD

if git diff --quiet "$refspec" -- $TEST_INFRA_FILES; then
...

fi
done

if [ "$RUN_ALL" = "true" ]; then
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can shorten this:

Suggested change
if [ "$RUN_ALL" = "true" ]; then
if "$RUN_ALL"; then

waitForResponse();
}
if (!emuReady) {
throw new Error(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this is unreachable code as we can't leave waitForResponse() with emuReady === false

Perhaps we need a way of detecting if maxIterations has been reached?

Comment on lines 248 to +249
emu.tx(wrappingCode);
waitForResponse();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would using txAndWait() for this and other emu.tx(...); waitForResponse() be an approach to avoiding the bug sneaking in, in future?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants