Fix Home action completion and test isolation
This commit is contained in:
@@ -21,12 +21,17 @@ Singleton {
|
|||||||
property string lastError: ""
|
property string lastError: ""
|
||||||
property bool fixtureMode: false
|
property bool fixtureMode: false
|
||||||
property var fixtureFavorites: []
|
property var fixtureFavorites: []
|
||||||
|
property string fixtureProcessMode: ""
|
||||||
|
|
||||||
property var actionQueue: []
|
property var actionQueue: []
|
||||||
property var activeAction: null
|
property var activeAction: null
|
||||||
property var busyEntityIds: []
|
property var busyEntityIds: []
|
||||||
property var pendingBrightness: ({})
|
property var pendingBrightness: ({})
|
||||||
property var entityErrors: ({})
|
property var entityErrors: ({})
|
||||||
|
property string actionResponseText: ""
|
||||||
|
property bool actionStreamFinished: false
|
||||||
|
property bool actionExited: false
|
||||||
|
property int actionExitCode: 0
|
||||||
|
|
||||||
readonly property var visibleEntities: root.selectedEntities.slice(0, 4)
|
readonly property var visibleEntities: root.selectedEntities.slice(0, 4)
|
||||||
readonly property int discoveredCount: root.catalog.length
|
readonly property int discoveredCount: root.catalog.length
|
||||||
@@ -177,7 +182,7 @@ Singleton {
|
|||||||
root.pendingBrightness = nextPending;
|
root.pendingBrightness = nextPending;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.fixtureMode) {
|
if (root.fixtureMode && root.fixtureProcessMode === "") {
|
||||||
root.applyFixtureAction(action);
|
root.applyFixtureAction(action);
|
||||||
root.finishActionState(action.entityId);
|
root.finishActionState(action.entityId);
|
||||||
return;
|
return;
|
||||||
@@ -192,27 +197,73 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
root.activeAction = root.actionQueue[0];
|
root.activeAction = root.actionQueue[0];
|
||||||
actionProc.command = root.activeAction.kind === "brightness"
|
root.actionResponseText = "";
|
||||||
? [root.helperPath, "brightness", root.activeAction.entityId, String(root.activeAction.percent)]
|
root.actionStreamFinished = false;
|
||||||
: [root.helperPath, "toggle", root.activeAction.entityId];
|
root.actionExited = false;
|
||||||
|
root.actionExitCode = 0;
|
||||||
|
if (root.fixtureProcessMode !== "") {
|
||||||
|
actionProc.command = root.fixtureActionCommand(root.activeAction);
|
||||||
|
} else {
|
||||||
|
actionProc.command = root.activeAction.kind === "brightness"
|
||||||
|
? [root.helperPath, "brightness", root.activeAction.entityId, String(root.activeAction.percent)]
|
||||||
|
: [root.helperPath, "toggle", root.activeAction.entityId];
|
||||||
|
}
|
||||||
actionProc.running = true;
|
actionProc.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function consumeAction(text: string): void {
|
function fixtureActionCommand(action: var): var {
|
||||||
|
if (root.fixtureProcessMode === "no-output"
|
||||||
|
&& action.entityId === "light.fixture_kitchen") {
|
||||||
|
return ["/usr/bin/sh", "-c", "sleep 0.5; exit 7"];
|
||||||
|
}
|
||||||
|
return ["/usr/bin/sh", "-c", "sleep 0.5; printf '%s' '{\"ok\":true}'"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleActionStreamFinished(text: string): void {
|
||||||
|
if (root.activeAction === null || root.actionStreamFinished)
|
||||||
|
return;
|
||||||
|
root.actionResponseText = text;
|
||||||
|
root.actionStreamFinished = true;
|
||||||
|
actionCompletionTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleActionExited(exitCode: int): void {
|
||||||
|
if (root.activeAction === null || root.actionExited)
|
||||||
|
return;
|
||||||
|
root.actionExited = true;
|
||||||
|
root.actionExitCode = exitCode;
|
||||||
|
actionCompletionTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryCompleteAction(): void {
|
||||||
|
if (root.activeAction === null)
|
||||||
|
return;
|
||||||
|
if (actionProc.running) {
|
||||||
|
actionCompletionTimer.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!root.actionExited || !root.actionStreamFinished)
|
||||||
|
return;
|
||||||
|
root.consumeAction(root.actionResponseText, root.actionExitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function consumeAction(text: string, exitCode: int): void {
|
||||||
if (root.activeAction === null)
|
if (root.activeAction === null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const completedAction = root.activeAction;
|
const completedAction = root.activeAction;
|
||||||
let ok = false;
|
let ok = false;
|
||||||
let errorCode = "action-failed";
|
let errorCode = text === "" ? "action-failed" : "invalid-response";
|
||||||
try {
|
try {
|
||||||
const result = JSON.parse(text);
|
const result = JSON.parse(text);
|
||||||
ok = result.ok === true;
|
ok = exitCode === 0 && result.ok === true;
|
||||||
errorCode = String(result.error || errorCode);
|
errorCode = String(result.error || "action-failed");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorCode = "invalid-response";
|
// Empty output from a failed process is an action failure, while
|
||||||
|
// malformed non-empty output remains an invalid response.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actionCompletionTimer.stop();
|
||||||
root.actionQueue = root.actionQueue.slice(1);
|
root.actionQueue = root.actionQueue.slice(1);
|
||||||
root.activeAction = null;
|
root.activeAction = null;
|
||||||
root.finishActionState(completedAction.entityId);
|
root.finishActionState(completedAction.entityId);
|
||||||
@@ -220,12 +271,15 @@ Singleton {
|
|||||||
const nextErrors = Object.assign({}, root.entityErrors);
|
const nextErrors = Object.assign({}, root.entityErrors);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
delete nextErrors[completedAction.entityId];
|
delete nextErrors[completedAction.entityId];
|
||||||
refreshDelay.restart();
|
if (root.fixtureMode && root.fixtureProcessMode !== "")
|
||||||
|
root.applyFixtureAction(completedAction);
|
||||||
|
else
|
||||||
|
refreshDelay.restart();
|
||||||
} else {
|
} else {
|
||||||
nextErrors[completedAction.entityId] = errorCode;
|
nextErrors[completedAction.entityId] = errorCode;
|
||||||
}
|
}
|
||||||
root.entityErrors = nextErrors;
|
root.entityErrors = nextErrors;
|
||||||
root.startNextAction();
|
queueAdvanceTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishActionState(entityId: string): void {
|
function finishActionState(entityId: string): void {
|
||||||
@@ -283,19 +337,29 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetActionState(): void {
|
function resetActionState(): void {
|
||||||
|
actionCompletionTimer.stop();
|
||||||
|
queueAdvanceTimer.stop();
|
||||||
root.actionQueue = [];
|
root.actionQueue = [];
|
||||||
root.activeAction = null;
|
root.activeAction = null;
|
||||||
root.busyEntityIds = [];
|
root.busyEntityIds = [];
|
||||||
root.pendingBrightness = {};
|
root.pendingBrightness = {};
|
||||||
root.entityErrors = {};
|
root.entityErrors = {};
|
||||||
|
root.actionResponseText = "";
|
||||||
|
root.actionStreamFinished = false;
|
||||||
|
root.actionExited = false;
|
||||||
|
root.actionExitCode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyFixture(name: string): void {
|
function applyFixture(name: string): void {
|
||||||
if (["ready", "stale", "unavailable", "missing-selected", "action-error"].indexOf(name) < 0)
|
if (["ready", "stale", "unavailable", "missing-selected", "action-error",
|
||||||
|
"process-actions", "process-no-output"].indexOf(name) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
root.fixtureMode = true;
|
root.fixtureMode = true;
|
||||||
root.resetActionState();
|
root.resetActionState();
|
||||||
|
root.fixtureProcessMode = name === "process-actions"
|
||||||
|
? "success"
|
||||||
|
: (name === "process-no-output" ? "no-output" : "");
|
||||||
if (name === "unavailable") {
|
if (name === "unavailable") {
|
||||||
root.catalog = [];
|
root.catalog = [];
|
||||||
root.fixtureFavorites = [];
|
root.fixtureFavorites = [];
|
||||||
@@ -326,6 +390,7 @@ Singleton {
|
|||||||
|
|
||||||
function clearFixture(): void {
|
function clearFixture(): void {
|
||||||
root.fixtureMode = false;
|
root.fixtureMode = false;
|
||||||
|
root.fixtureProcessMode = "";
|
||||||
root.phase = "loading";
|
root.phase = "loading";
|
||||||
root.catalog = [];
|
root.catalog = [];
|
||||||
root.selectedEntities = [];
|
root.selectedEntities = [];
|
||||||
@@ -347,9 +412,27 @@ Singleton {
|
|||||||
Process {
|
Process {
|
||||||
id: actionProc
|
id: actionProc
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
id: actionOutput
|
onStreamFinished: root.handleActionStreamFinished(this.text)
|
||||||
|
}
|
||||||
|
onExited: (code, status) => root.handleActionExited(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: actionCompletionTimer
|
||||||
|
interval: 0
|
||||||
|
onTriggered: root.tryCompleteAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: queueAdvanceTimer
|
||||||
|
interval: 0
|
||||||
|
onTriggered: {
|
||||||
|
if (actionProc.running) {
|
||||||
|
queueAdvanceTimer.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.startNextAction();
|
||||||
}
|
}
|
||||||
onExited: (code, status) => root.consumeAction(actionOutput.text)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ ShellRoot {
|
|||||||
selectedIds: HomeAssistant.selectedEntities.map(entity => entity.id),
|
selectedIds: HomeAssistant.selectedEntities.map(entity => entity.id),
|
||||||
entities: HomeAssistant.selectedEntities,
|
entities: HomeAssistant.selectedEntities,
|
||||||
stale: HomeAssistant.stale,
|
stale: HomeAssistant.stale,
|
||||||
|
busy: HomeAssistant.busyEntityIds.length > 0,
|
||||||
busyEntityIds: HomeAssistant.busyEntityIds,
|
busyEntityIds: HomeAssistant.busyEntityIds,
|
||||||
pendingBrightness: HomeAssistant.pendingBrightness,
|
pendingBrightness: HomeAssistant.pendingBrightness,
|
||||||
entityErrors: HomeAssistant.entityErrors,
|
entityErrors: HomeAssistant.entityErrors,
|
||||||
|
|||||||
@@ -2,25 +2,84 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
config_path="$repo_dir/config/dot/quickshell"
|
||||||
|
state_home="$(mktemp -d /tmp/panama-control-center-state.XXXXXX)"
|
||||||
|
shell_log="$state_home/quickshell.log"
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
printf 'Control Center services contract: %s\n' "$1" >&2
|
printf 'Control Center services contract: %s\n' "$1" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qs_for_test() {
|
||||||
|
QS_CONFIG_PATH="$config_path" XDG_STATE_HOME="$state_home" \
|
||||||
|
qs -p "$config_path" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_test_shell() {
|
||||||
|
qs_for_test kill >/dev/null 2>&1 || true
|
||||||
|
for _ in $(seq 1 80); do
|
||||||
|
if ! qs_for_test list 2>/dev/null | rg '^Instance ' >/dev/null \
|
||||||
|
&& ! qs_for_test ipc show >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
qs ipc call kdeconnect reset >/dev/null 2>&1 || true
|
qs_for_test ipc call kdeconnect reset >/dev/null 2>&1 || true
|
||||||
qs ipc call home-assistant reset >/dev/null 2>&1 || true
|
qs_for_test ipc call home-assistant reset >/dev/null 2>&1 || true
|
||||||
qs ipc call status-events reset >/dev/null 2>&1 || true
|
qs_for_test ipc call status-events reset >/dev/null 2>&1 || true
|
||||||
qs ipc call quicksettings close >/dev/null 2>&1 || true
|
qs_for_test ipc call quicksettings close >/dev/null 2>&1 || true
|
||||||
|
if stop_test_shell; then
|
||||||
|
rm -rf "$state_home"
|
||||||
|
else
|
||||||
|
printf 'Control Center services contract: branch shell did not stop; retained %s\n' \
|
||||||
|
"$state_home" >&2
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
qs ipc show | rg -q '^target kdeconnect$' \
|
start_test_shell() {
|
||||||
|
stop_test_shell || fail 'pre-existing branch shell did not stop cleanly'
|
||||||
|
qs_for_test --daemonize >"$shell_log" 2>&1
|
||||||
|
for _ in $(seq 1 80); do
|
||||||
|
if qs_for_test ipc show 2>/dev/null | rg '^target home-assistant$' >/dev/null; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
sed -n '1,200p' "$shell_log" >&2
|
||||||
|
fail 'isolated branch shell did not start'
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_home_status() {
|
||||||
|
local filter="$1"
|
||||||
|
local message="$2"
|
||||||
|
local status=""
|
||||||
|
|
||||||
|
for _ in $(seq 1 80); do
|
||||||
|
status="$(qs_for_test ipc call home-assistant status)"
|
||||||
|
if jq -e "$filter" <<<"$status" >/dev/null; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
printf 'Last Home status: %s\n' "$status" >&2
|
||||||
|
fail "$message"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_test_shell
|
||||||
|
|
||||||
|
qs_for_test ipc show | rg '^target kdeconnect$' >/dev/null \
|
||||||
|| fail 'KDE Connect IPC target is missing'
|
|| fail 'KDE Connect IPC target is missing'
|
||||||
qs ipc show | rg -q '^target home-assistant$' \
|
qs_for_test ipc show | rg '^target home-assistant$' >/dev/null \
|
||||||
|| fail 'Home Assistant IPC target is missing'
|
|| fail 'Home Assistant IPC target is missing'
|
||||||
|
|
||||||
qs ipc call kdeconnect fixture reachable >/dev/null
|
qs_for_test ipc call kdeconnect fixture reachable >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.available == true and
|
.available == true and
|
||||||
@@ -28,35 +87,35 @@ jq -e '
|
|||||||
.actionCount == 4 and
|
.actionCount == 4 and
|
||||||
.transferActive == false and
|
.transferActive == false and
|
||||||
.ongoingCount == 0
|
.ongoingCount == 0
|
||||||
' <<<"$(qs ipc call kdeconnect status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call kdeconnect status)" >/dev/null \
|
||||||
|| fail 'reachable phone fixture is malformed'
|
|| fail 'reachable phone fixture is malformed'
|
||||||
|
|
||||||
qs ipc call kdeconnect fixture offline >/dev/null
|
qs_for_test ipc call kdeconnect fixture offline >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.available == true and
|
.available == true and
|
||||||
.reachable == false and
|
.reachable == false and
|
||||||
.pairedCount == 1 and
|
.pairedCount == 1 and
|
||||||
.ongoingCount == 0
|
.ongoingCount == 0
|
||||||
' <<<"$(qs ipc call kdeconnect status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call kdeconnect status)" >/dev/null \
|
||||||
|| fail 'offline phone fixture is malformed'
|
|| fail 'offline phone fixture is malformed'
|
||||||
|
|
||||||
qs ipc call kdeconnect fixture transfer >/dev/null
|
qs_for_test ipc call kdeconnect fixture transfer >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.transferActive == true and
|
.transferActive == true and
|
||||||
.transferFileName == "Fixture document.pdf" and
|
.transferFileName == "Fixture document.pdf" and
|
||||||
.ongoingCount == 1
|
.ongoingCount == 1
|
||||||
' <<<"$(qs ipc call kdeconnect status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call kdeconnect status)" >/dev/null \
|
||||||
|| fail 'phone transfer did not enter Ongoing'
|
|| fail 'phone transfer did not enter Ongoing'
|
||||||
|
|
||||||
qs ipc call kdeconnect cancel >/dev/null
|
qs_for_test ipc call kdeconnect cancel >/dev/null
|
||||||
jq -e '.transferActive == false and .ongoingCount == 0' \
|
jq -e '.transferActive == false and .ongoingCount == 0' \
|
||||||
<<<"$(qs ipc call kdeconnect status)" >/dev/null \
|
<<<"$(qs_for_test ipc call kdeconnect status)" >/dev/null \
|
||||||
|| fail 'phone transfer did not leave Ongoing'
|
|| fail 'phone transfer did not leave Ongoing'
|
||||||
|
|
||||||
qs ipc call home-assistant fixture ready >/dev/null
|
qs_for_test ipc call home-assistant fixture ready >/dev/null
|
||||||
home_ready="$(qs ipc call home-assistant status)"
|
home_ready="$(qs_for_test ipc call home-assistant status)"
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.phase == "ready" and
|
.phase == "ready" and
|
||||||
@@ -76,27 +135,57 @@ jq -e '
|
|||||||
"Bedroom"
|
"Bedroom"
|
||||||
] and
|
] and
|
||||||
.stale == false and
|
.stale == false and
|
||||||
|
.busy == false and
|
||||||
.lastError == ""
|
.lastError == ""
|
||||||
' <<<"$home_ready" >/dev/null \
|
' <<<"$home_ready" >/dev/null \
|
||||||
|| fail 'ready Home fixture is malformed'
|
|| fail 'ready Home fixture is malformed'
|
||||||
|
|
||||||
qs ipc call home-assistant brightness light.fixture_living 64 >/dev/null
|
qs_for_test ipc call home-assistant brightness light.fixture_living 64 >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.entities[] |
|
.entities[] |
|
||||||
select(.id == "light.fixture_living") |
|
select(.id == "light.fixture_living") |
|
||||||
.active == true and .state == "on" and .brightnessPct == 64
|
.active == true and .state == "on" and .brightnessPct == 64
|
||||||
' <<<"$(qs ipc call home-assistant status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call home-assistant status)" >/dev/null \
|
||||||
|| fail 'Home brightness fixture did not update local catalog state'
|
|| fail 'Home brightness fixture did not update local catalog state'
|
||||||
|
|
||||||
qs ipc call home-assistant toggle light.fixture_living >/dev/null
|
qs_for_test ipc call home-assistant toggle light.fixture_living >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.entities[] |
|
.entities[] |
|
||||||
select(.id == "light.fixture_living") |
|
select(.id == "light.fixture_living") |
|
||||||
.active == false and .state == "off"
|
.active == false and .state == "off"
|
||||||
' <<<"$(qs ipc call home-assistant status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call home-assistant status)" >/dev/null \
|
||||||
|| fail 'Home toggle fixture did not update local catalog state'
|
|| fail 'Home toggle fixture did not update local catalog state'
|
||||||
|
|
||||||
qs ipc call home-assistant fixture missing-selected >/dev/null
|
qs_for_test ipc call home-assistant fixture process-actions >/dev/null
|
||||||
|
qs_for_test ipc call home-assistant brightness light.fixture_living 64 >/dev/null
|
||||||
|
qs_for_test ipc call home-assistant toggle light.fixture_hall >/dev/null
|
||||||
|
jq -e '
|
||||||
|
.busy == true and
|
||||||
|
.busyEntityIds == ["light.fixture_living", "light.fixture_hall"] and
|
||||||
|
.pendingBrightness["light.fixture_living"] == 64
|
||||||
|
' <<<"$(qs_for_test ipc call home-assistant status)" >/dev/null \
|
||||||
|
|| fail 'process-backed Home actions did not remain per-entity busy'
|
||||||
|
wait_for_home_status '
|
||||||
|
.busy == false and
|
||||||
|
.busyEntityIds == [] and
|
||||||
|
.pendingBrightness == {} and
|
||||||
|
(.entities[] | select(.id == "light.fixture_living") | .active == true and .brightnessPct == 64) and
|
||||||
|
(.entities[] | select(.id == "light.fixture_hall") | .active == true)
|
||||||
|
' 'process-backed Home actions did not complete in queue order'
|
||||||
|
|
||||||
|
qs_for_test ipc call home-assistant fixture process-no-output >/dev/null
|
||||||
|
qs_for_test ipc call home-assistant toggle light.fixture_kitchen >/dev/null
|
||||||
|
qs_for_test ipc call home-assistant toggle light.fixture_hall >/dev/null
|
||||||
|
wait_for_home_status '
|
||||||
|
.phase == "ready" and
|
||||||
|
.busy == false and
|
||||||
|
.busyEntityIds == [] and
|
||||||
|
.entityErrors["light.fixture_kitchen"] == "action-failed" and
|
||||||
|
(.entityErrors["light.fixture_hall"] // "") == "" and
|
||||||
|
(.entities[] | select(.id == "light.fixture_hall") | .active == true)
|
||||||
|
' 'nonzero no-output Home action did not fail safely and continue the queue'
|
||||||
|
|
||||||
|
qs_for_test ipc call home-assistant fixture missing-selected >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.phase == "ready" and
|
.phase == "ready" and
|
||||||
@@ -111,10 +200,10 @@ jq -e '
|
|||||||
.active == false and
|
.active == false and
|
||||||
.dimmable == false and
|
.dimmable == false and
|
||||||
.brightnessPct == 0)
|
.brightnessPct == 0)
|
||||||
' <<<"$(qs ipc call home-assistant status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call home-assistant status)" >/dev/null \
|
||||||
|| fail 'missing Home selection was not retained as unavailable'
|
|| fail 'missing Home selection was not retained as unavailable'
|
||||||
|
|
||||||
qs ipc call home-assistant fixture action-error >/dev/null
|
qs_for_test ipc call home-assistant fixture action-error >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.phase == "ready" and
|
.phase == "ready" and
|
||||||
@@ -122,10 +211,10 @@ jq -e '
|
|||||||
.entityErrors["light.fixture_kitchen"] == "request-failed" and
|
.entityErrors["light.fixture_kitchen"] == "request-failed" and
|
||||||
(.entityErrors["light.fixture_hall"] // "") == "" and
|
(.entityErrors["light.fixture_hall"] // "") == "" and
|
||||||
.lastError == ""
|
.lastError == ""
|
||||||
' <<<"$(qs ipc call home-assistant status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call home-assistant status)" >/dev/null \
|
||||||
|| fail 'Home action error was not isolated to one entity'
|
|| fail 'Home action error was not isolated to one entity'
|
||||||
|
|
||||||
qs ipc call home-assistant fixture stale >/dev/null
|
qs_for_test ipc call home-assistant fixture stale >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.phase == "degraded" and
|
.phase == "degraded" and
|
||||||
@@ -134,10 +223,10 @@ jq -e '
|
|||||||
.visibleCount == 4 and
|
.visibleCount == 4 and
|
||||||
.stale == true and
|
.stale == true and
|
||||||
.lastError == "unreachable"
|
.lastError == "unreachable"
|
||||||
' <<<"$(qs ipc call home-assistant status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call home-assistant status)" >/dev/null \
|
||||||
|| fail 'stale Home fixture did not retain entities'
|
|| fail 'stale Home fixture did not retain entities'
|
||||||
|
|
||||||
qs ipc call home-assistant fixture unavailable >/dev/null
|
qs_for_test ipc call home-assistant fixture unavailable >/dev/null
|
||||||
jq -e '
|
jq -e '
|
||||||
.fixture == true and
|
.fixture == true and
|
||||||
.phase == "unavailable" and
|
.phase == "unavailable" and
|
||||||
@@ -145,9 +234,11 @@ jq -e '
|
|||||||
.configuredCount == 0 and
|
.configuredCount == 0 and
|
||||||
.visibleCount == 0 and
|
.visibleCount == 0 and
|
||||||
.lastError == "not-configured"
|
.lastError == "not-configured"
|
||||||
' <<<"$(qs ipc call home-assistant status)" >/dev/null \
|
' <<<"$(qs_for_test ipc call home-assistant status)" >/dev/null \
|
||||||
|| fail 'unavailable Home fixture is malformed'
|
|| fail 'unavailable Home fixture is malformed'
|
||||||
|
|
||||||
trap - EXIT
|
trap - EXIT
|
||||||
cleanup
|
cleanup
|
||||||
|
[[ ! -e "$state_home" ]] \
|
||||||
|
|| fail 'temporary Home preferences state was not removed after shell exit'
|
||||||
printf 'Control Center services contract: PASS\n'
|
printf 'Control Center services contract: PASS\n'
|
||||||
|
|||||||
Reference in New Issue
Block a user