From 7685c996375fffa56188d29147cd646432c69cd7 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 18 Feb 2026 22:30:22 +0100 Subject: [PATCH 1/3] implement equals check tests and fix color equals check. --- .../provider/ColorStateProviderService.java | 91 ++++- .../ColorStateProviderServiceTest.java | 56 --- .../provider/ColorStateProviderServiceTest.kt | 346 ++++++++++++++++++ 3 files changed, 428 insertions(+), 65 deletions(-) delete mode 100644 module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.java create mode 100644 module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.kt diff --git a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderService.java b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderService.java index 792fa1832b..ee1ddb2c4c 100644 --- a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderService.java +++ b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderService.java @@ -169,21 +169,94 @@ static Boolean equalServiceStates(final ColorState colorStateA, final ColorState final HSBColor hsbColorA = colorStateA.getColor().getHsbColor(); final HSBColor hsbColorB = colorStateB.getColor().getHsbColor(); + // Helper: compare hues with wrap-around (0 == 360) + // margin in degrees + final double HUE_MARGIN = 1.0; + final double SATURATION_MARGIN = 0.01; + final double BRIGHTNESS_MARGIN = 0.01; - boolean hueEquals = true; - boolean saturationEquals = true; - boolean brightnessEquals = true; + // normalize angle to [0,360) + java.util.function.DoubleUnaryOperator normalize = (v) -> { + double r = v % 360.0; + if (r < 0) r += 360.0; + return r; + }; - if(hsbColorA.hasHue() && hsbColorB.hasHue()) { - hueEquals = OperationService.equals(hsbColorA.getHue(), hsbColorB.getHue(), 1.0); + java.util.function.BiPredicate hueEqualsWithWrap = (ha, hb) -> { + double aNorm = normalize.applyAsDouble(ha); + double bNorm = normalize.applyAsDouble(hb); + double diff = Math.abs(aNorm - bNorm); + if (diff > 180.0) { + diff = 360.0 - diff; // shortest distance on circle + } + return diff <= HUE_MARGIN; + }; + + boolean hueEquals; + if (hsbColorA.hasHue() && hsbColorB.hasHue()) { + hueEquals = hueEqualsWithWrap.test(hsbColorA.getHue(), hsbColorB.getHue()); + } else if (!hsbColorA.hasHue() && !hsbColorB.hasHue()) { + // both undefined -> treat as equal + hueEquals = true; + } else { + // one missing: if the present color is 'neutral' (saturation == 0 or brightness == 0 or both undefined) the hue is irrelevant + final HSBColor present = hsbColorA.hasHue() ? hsbColorA : hsbColorB; + boolean presentIsNeutral = false; + // If both saturation and brightness are missing, treat as neutral (no chroma information) + if (!present.hasSaturation() && !present.hasBrightness()) { + presentIsNeutral = true; + } + // If saturation is present and effectively 0 -> neutral + if (!presentIsNeutral && present.hasSaturation()) { + presentIsNeutral = OperationService.equals(present.getSaturation(), 0d, SATURATION_MARGIN); + } + // If not neutral yet and brightness is present and effectively 0 -> neutral + if (!presentIsNeutral && present.hasBrightness()) { + presentIsNeutral = OperationService.equals(present.getBrightness(), 0d, BRIGHTNESS_MARGIN); + } + hueEquals = presentIsNeutral; } - if(hsbColorA.hasSaturation() && hsbColorB.hasSaturation()) { - saturationEquals = OperationService.equals(hsbColorA.getSaturation(), hsbColorB.getSaturation(), 0.01); + boolean saturationEquals; + if (hsbColorA.hasSaturation() && hsbColorB.hasSaturation()) { + saturationEquals = OperationService.equals(hsbColorA.getSaturation(), hsbColorB.getSaturation(), SATURATION_MARGIN); + } else if (!hsbColorA.hasSaturation() && !hsbColorB.hasSaturation()) { + saturationEquals = true; + } else { + // one missing: consider equal if the present saturation is effectively 0 (neutral) + // or if the present has no saturation but brightness is present and 0 -> neutral + final HSBColor present = hsbColorA.hasSaturation() ? hsbColorA : hsbColorB; + boolean presentIsNeutral = false; + if (present.hasSaturation()) { + presentIsNeutral = OperationService.equals(present.getSaturation(), 0d, SATURATION_MARGIN); + } else if (present.hasBrightness()) { + presentIsNeutral = OperationService.equals(present.getBrightness(), 0d, BRIGHTNESS_MARGIN); + } else { + // no saturation and no brightness information -> treat as neutral + presentIsNeutral = true; + } + saturationEquals = presentIsNeutral; } - if(hsbColorA.hasBrightness() && hsbColorB.hasBrightness()) { - brightnessEquals = OperationService.equals(hsbColorA.getBrightness(), hsbColorB.getBrightness(), 0.01); + boolean brightnessEquals; + if (hsbColorA.hasBrightness() && hsbColorB.hasBrightness()) { + brightnessEquals = OperationService.equals(hsbColorA.getBrightness(), hsbColorB.getBrightness(), BRIGHTNESS_MARGIN); + } else if (!hsbColorA.hasBrightness() && !hsbColorB.hasBrightness()) { + brightnessEquals = true; + } else { + // one missing: consider equal if the present brightness is effectively 0 (off/neutral) + // or if the present has no brightness but saturation is present and 0 -> neutral + final HSBColor present = hsbColorA.hasBrightness() ? hsbColorA : hsbColorB; + boolean presentIsNeutral = false; + if (present.hasBrightness()) { + presentIsNeutral = OperationService.equals(present.getBrightness(), 0d, BRIGHTNESS_MARGIN); + } else if (present.hasSaturation()) { + presentIsNeutral = OperationService.equals(present.getSaturation(), 0d, SATURATION_MARGIN); + } else { + // no brightness and no saturation information -> treat as neutral + presentIsNeutral = true; + } + brightnessEquals = presentIsNeutral; } return hueEquals && saturationEquals && brightnessEquals; diff --git a/module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.java b/module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.java deleted file mode 100644 index c7a9b9d05d..0000000000 --- a/module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.openbase.bco.dal.lib.layer.service.provider; - -/*- - * #%L - * BCO DAL Library - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.openbase.jps.core.JPService; -import org.openbase.jps.exception.JPServiceException; -import org.openbase.jul.exception.VerificationFailedException; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.type.domotic.state.ColorStateType.ColorState; -import org.openbase.type.domotic.state.ColorStateType.ColorState.Builder; - -public class ColorStateProviderServiceTest { - - @Test - @Timeout(10) - public void verifyColorState() throws VerificationFailedException, JPServiceException { - - JPService.setupJUnitTestMode(); - - final Builder builder = ColorState.newBuilder(); - builder.getColorBuilder().getHsbColorBuilder().setHue(240); - builder.getColorBuilder().getHsbColorBuilder().setSaturation(100); - builder.getColorBuilder().getHsbColorBuilder().setBrightness(50); - - ExceptionPrinter.setBeQuit(true); - final ColorState verifiedColorState = ColorStateProviderService.verifyColorState(builder.build()); - ExceptionPrinter.setBeQuit(false); - - assertEquals(builder.getColorBuilder().getHsbColorBuilder().getHue(), verifiedColorState.getColor().getHsbColor().getHue(), 0.00001, "Hue value invalid!"); - assertEquals(1d, verifiedColorState.getColor().getHsbColor().getSaturation(), 0.00001, "Hue value invalid!"); - assertEquals(0.5d, verifiedColorState.getColor().getHsbColor().getBrightness(), 0.00001, "Hue value invalid!"); - } -} diff --git a/module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.kt b/module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.kt new file mode 100644 index 0000000000..f2a1e9a584 --- /dev/null +++ b/module/dal/lib/src/test/java/org/openbase/bco/dal/lib/layer/service/provider/ColorStateProviderServiceTest.kt @@ -0,0 +1,346 @@ +package org.openbase.bco.dal.lib.layer.service.provider + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Timeout +import org.openbase.jps.core.JPService +import org.openbase.jps.exception.JPServiceException +import org.openbase.jul.exception.VerificationFailedException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.type.domotic.state.ColorStateType.ColorState + +/*- +* #%L +* BCO DAL Library +* %% +* Copyright (C) 2014 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ + +class ColorStateProviderServiceTest { + @Test + @Timeout(10) + @Throws(VerificationFailedException::class, JPServiceException::class) + fun verifyColorState() { + JPService.setupJUnitTestMode() + + val builder = ColorState.newBuilder() + builder.getColorBuilder().getHsbColorBuilder().setHue(240.0) + builder.getColorBuilder().getHsbColorBuilder().setSaturation(100.0) + builder.getColorBuilder().getHsbColorBuilder().setBrightness(50.0) + + ExceptionPrinter.setBeQuit(true) + val verifiedColorState = ColorStateProviderService.verifyColorState(builder.build()) + ExceptionPrinter.setBeQuit(false) + + Assertions.assertEquals( + builder.getColorBuilder().getHsbColorBuilder().getHue(), + verifiedColorState.getColor().getHsbColor().getHue(), + 0.00001, + "Hue value invalid!" + ) + Assertions.assertEquals( + 1.0, + verifiedColorState.getColor().getHsbColor().getSaturation(), + 0.00001, + "Hue value invalid!" + ) + Assertions.assertEquals( + 0.5, + verifiedColorState.getColor().getHsbColor().getBrightness(), + 0.00001, + "Hue value invalid!" + ) + } + + @Test + @Timeout(10) + @Throws(VerificationFailedException::class, JPServiceException::class) + fun `should handle color state comparison with equal state`() { + JPService.setupJUnitTestMode() + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe true + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(0.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(360.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe true + } + + @Test + @Timeout(10) + @Throws(VerificationFailedException::class, JPServiceException::class) + fun `should handle color state comparison with non equal state`() { + JPService.setupJUnitTestMode() + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(50.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(100.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(30.0) + setSaturation(0.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(50.0) + setBrightness(1.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(10.0) + setSaturation(100.0) + setBrightness(20.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(233.0) + setSaturation(23.0) + setBrightness(12.0) + } + }.build(), + ) shouldBe false + + } + + @Test + @Timeout(10) + @Throws(VerificationFailedException::class, JPServiceException::class) + fun `should handle color state comparison with neutral state`() { + JPService.setupJUnitTestMode() + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setBrightness(50.0) + } + }.build(), + ) shouldBe false + + ColorStateProviderService.equalServiceStates( + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + setBrightness(50.0) + } + }.build(), + ColorState.newBuilder().apply { + getColorBuilder().getHsbColorBuilder().apply { + setHue(240.0) + setSaturation(100.0) + } + }.build(), + ) shouldBe false + } +} From fb0e868e71eebc068e08bbb5c1d00ca0e289efb1 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 18 Feb 2026 22:34:44 +0100 Subject: [PATCH 2/3] upgrade jul --- lib/jul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jul b/lib/jul index 9d9befebe1..624961115a 160000 --- a/lib/jul +++ b/lib/jul @@ -1 +1 @@ -Subproject commit 9d9befebe197fd170f73518483ce213bef661653 +Subproject commit 624961115af6d25863de263b70c857320d0fc7a4 From 3ad5f9f677f33844385333fc8791001bf335999c Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 18 Feb 2026 23:07:39 +0100 Subject: [PATCH 3/3] fix addon commit message contains no addon name. --- .github/workflows/update-addon-version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-addon-version.yaml b/.github/workflows/update-addon-version.yaml index 988d4f2fb2..129d0be867 100644 --- a/.github/workflows/update-addon-version.yaml +++ b/.github/workflows/update-addon-version.yaml @@ -95,7 +95,7 @@ jobs: git config user.name "$GIT_USERNAME" git config user.email "$GIT_EMAIL" git add config.yaml - git commit -m "Update add-on $ADDONS_DIR version to $VERSION" || { + git commit -m "Update add-on $ADDON_DIR version to $VERSION" || { echo "No changes to commit" exit 0 }