Skip to content

[huesync] Scheduled runnable ended with an exception#20512

Draft
pgfeller wants to merge 5 commits intoopenhab:mainfrom
pgfeller:pgfeller/huesync/19079
Draft

[huesync] Scheduled runnable ended with an exception#20512
pgfeller wants to merge 5 commits intoopenhab:mainfrom
pgfeller:pgfeller/huesync/19079

Conversation

@pgfeller
Copy link
Copy Markdown
Contributor

@pgfeller pgfeller commented Apr 3, 2026

Summary

Fixes #19079NoSuchElementException: No value present thrown from the scheduled update task when Optional fields (connection, deviceInfo) become empty during handler lifecycle transitions (e.g. dispose() called while a scheduled task is mid-execution).

…ssion tests for safe connection disposal

Signed-off-by: Patrik Gfeller <patrik.gfeller@proton.me>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes Issue #19079 by preventing NoSuchElementException during scheduled Hue Sync status updates when handler lifecycle transitions cause incomplete/invalid update data, and adds regression tests around the new exception-handling behavior.

Changes:

  • Update polling task now only invokes the update consumer on fully successful fetches and routes failures to the exception handler.
  • Handler update processing no longer throws on missing update fields and dispose() no longer throws when connection is absent.
  • Add regression tests for update-task exception contract and handler dispose/initialize safety.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java Stop calling the consumer from finally; call it only on successful fetch; adjust logging/exception flow.
bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java Update consumer wiring, safer update handling, and dispose() no longer throws on empty connection.
bundles/org.openhab.binding.huesync/src/test/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTaskTest.java New regression tests validating consumer/exception-handler invocation rules.
bundles/org.openhab.binding.huesync/src/test/java/org/openhab/binding/huesync/internal/handler/HueSyncHandlerTest.java New regression tests ensuring dispose() and basic lifecycle calls don’t throw.
bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/ResourceHelper.java Javadoc author note updated.
bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java Javadoc author note updated.
bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java Javadoc author note updated.
bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java Javadoc author note updated.
bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java Javadoc author note updated.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +58 to +68
@Test
@DisplayName("dispose() should not throw NoSuchElementException when connection is empty")
void disposeWithEmptyConnection() throws Exception {
// Given: handler with empty connection (default state)
// When: dispose is called
// Then: should not throw NoSuchElementException
handler.dispose();

// Test passes if no exception was thrown
assertThat(true, is(true));
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Test assertions like assertThat(true, is(true)) don’t validate behavior and will pass even if the method under test is never executed meaningfully. Replace these with JUnit assertions that directly express intent (e.g., assertDoesNotThrow(handler::dispose) / assertDoesNotThrow(() -> { ... })).

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +80
@Test
@DisplayName("Multiple successive dispose() calls should be safe")
void multipleSideDisposeCalls() throws Exception {
// Given: handler initialized
// When: dispose is called multiple times
handler.dispose();
handler.dispose();

// Then: should not throw exception
assertThat(true, is(true));
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Typo in test method name: multipleSideDisposeCalls reads like “side” rather than “successive”. Rename to something like multipleSuccessiveDisposeCalls to match the display name and intent.

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +53

private HueSyncDeviceConnection connection = mock(HueSyncDeviceConnection.class);
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

This test class is @NonNullByDefault, but Mockito mock(...) fields are not annotated/suppressed for null analysis. In this codebase, tests commonly use @SuppressWarnings("null") on mocked fields to satisfy Eclipse/JDT null analysis (e.g., bundles/org.openhab.binding.evcc/src/test/java/.../EvccBaseThingHandlerTest.java:55-60). Add the appropriate suppression/annotations here to avoid null-analysis build failures.

Suggested change
private HueSyncDeviceConnection connection = mock(HueSyncDeviceConnection.class);
@SuppressWarnings("null")
private HueSyncDeviceConnection connection = mock(HueSyncDeviceConnection.class);
@SuppressWarnings("null")

Copilot uses AI. Check for mistakes.
pgfeller added 4 commits April 4, 2026 18:50
…mprove logging

Route failures from the update task to the exception handler instead of propagating Optional-related exceptions.
Include device uniqueId and the exception in log calls for better diagnostics.
Make dispose() null-safe when the connection is absent.
Add regression tests for update-task exception handling and handler lifecycle safety.

Signed-off-by: Patrik Gfeller <patrik.gfeller@proton.me>
Replaced log calls that only used e.getMessage() (losing stack traces) with SLF4J calls that pass the exception object and provide contextual messages (initialization, disposal, registration parsing, unregistering).

Signed-off-by: Patrik Gfeller <patrik.gfeller@proton.me>
Replace duplicated uniqueId in the trace with the device IP and pass the exception object to the logger (instead of e.getMessage()) so the full stacktrace is recorded.

Signed-off-by: Patrik Gfeller <patrik.gfeller@proton.me>
Replace manual Mockito.mock(...) usage with field-level @mock injection using MockitoExtension.
Add a class-level ScheduledExecutorService @mock and use it to inject BaseThingHandler.scheduler in the test.
Remove redundant manual mock creation in @beforeeach.

Signed-off-by: Patrik Gfeller <patrik.gfeller@proton.me>
@pgfeller pgfeller added the work in progress A PR that is not yet ready to be merged label Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

work in progress A PR that is not yet ready to be merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[huesync] Scheduled runnable ended with an exception

2 participants