laundry: cover a device that advertises cloud cycles it has never loaded
A survey of every laundry diagnostics dump attached to an issue turned up 14 devices, 4 of which carry cloud-course tokens. Two were already known; the two new ones are both useful, and one contradicts something the investigation write-up asserted. A DW5000C dishwasher (issues #113/#123) advertises four downloaded programs and carries no payload token for any of them. That is a shape the corpus didn't have: the feature is not washer-only (DA_DW, not DA_WM), and a device can name programs whose payloads have never been observed. The existing code already handles it correctly -- nothing learnable, nothing offered, gap still counted for the Repairs issue -- so this adds the fixture, golden, and tests that keep it that way. A second WW5000C (issues #259/#343, firmware _B048) holds the same saved program as the first one's captured "Towels", and the two payloads differ at exactly one byte: byte 3, 04 against 06. Everything else -- id, slot, all four varying tag values, the whole tail -- is identical. So byte 3 is neither a per-board constant nor a property of the program, and the doc's claim that it is always 04 on this board was wrong. That is also the strongest argument yet for learning payloads per device: a catalog keyed on program id would have shipped one unit's byte 3 to the other. Nothing changes in the implementation as a result -- it never had a catalog -- but the reasoning is now backed by evidence rather than caution. Also recorded: both WW5000C units advertise the byte-identical slot list despite different firmware, so the program set looks factory- or region-assigned rather than user-curated; and a sentinel's byte 2 equals the selected course on one dump but not the other, so it stays unused.
This commit is contained in:
1 parent
22b4508f95
commit
9d28b088cb
5 files changed
+472
-14
No files matched your search
@@ -122,6 +122,59 @@ class TestRealDumps:
|
||||
store.observe(rep)
|
||||
assert store.download_candidates() == []
|
||||
|
||||
def test_a_dishwasher_advertises_programs_it_has_never_loaded(self):
|
||||
"""DW5000C (issues #113/#123): CloudExtraCourse_ names four slots
|
||||
with no CloudCourse_/OneTimeCloudCourse_ token anywhere in the array.
|
||||
|
||||
Two things at once -- the feature is not washer-only (DA_DW, not
|
||||
DA_WM), and a device can advertise programs whose payloads have never
|
||||
been observed. Nothing is learnable here, so nothing is offerable,
|
||||
but the gap is still countable and still worth telling the user
|
||||
about."""
|
||||
rep = _load_device("dishwasher_dw5000c_cloud")["/course/vs/0"]
|
||||
assert cloudcourse.advertised_slots(rep) == ["8E", "8D", "8F", "02"]
|
||||
assert cloudcourse.supports_cloud_courses(rep) is True
|
||||
|
||||
store = cloudcourse.CloudCourses()
|
||||
assert store.observe(rep) is False
|
||||
assert store.view() == {}
|
||||
assert cloudcourse.undiscovered(rep, store.snapshot()) == ["8E", "8D", "8F", "02"]
|
||||
|
||||
def test_byte_three_is_not_part_of_a_programs_identity(self):
|
||||
"""Two WW5000C units on different firmware (issue #342's _B06C and
|
||||
issues #259/#343's _B048) report the same program -- same id, same
|
||||
slot, same field values, same tail -- differing only at byte 3.
|
||||
|
||||
This is the evidence against a shipped catalog of payloads: keyed on
|
||||
program id, one unit's capture would have carried the other unit's
|
||||
byte 3. Both are recorded and replayed per device instead, so
|
||||
whatever byte 3 tracks never has to be understood."""
|
||||
b06c = "00040A0449404D134AB84C0035F004F005F0AC00"
|
||||
b048 = "00040A0649404D134AB84C0035F004F005F0AC00"
|
||||
assert b06c != b048
|
||||
# Same program by every identifier the device gives us.
|
||||
assert cloudcourse.slot_of(b06c) == cloudcourse.slot_of(b048) == "0A"
|
||||
assert b06c[:6] == b048[:6]
|
||||
assert b06c[8:] == b048[8:]
|
||||
# Learned separately per device, and each replays what it saw.
|
||||
for blob in (b06c, b048):
|
||||
store = cloudcourse.CloudCourses()
|
||||
store.observe(_rep(["CloudExtraCourse_0A", f"CloudCourse_{blob}"]))
|
||||
assert store.blob("0A") == blob
|
||||
|
||||
def test_a_sentinel_slot_byte_is_not_the_current_course(self):
|
||||
"""The two sentinels in the corpus disagree about this -- WA55's byte
|
||||
2 happens to equal its selected course, the WW5000C _B048's does not
|
||||
(1B against Course_1C). Nothing keys off it: a sentinel is rejected
|
||||
on its FFFF prefix, and its byte 2 is not an advertised slot either
|
||||
way."""
|
||||
wa55 = "FFFF010049004D004A804C0037F0AC00"
|
||||
b048 = "FFFF1B0049004D004A804C0035F004F005F0AC00"
|
||||
assert cloudcourse.slot_of(wa55) is None
|
||||
assert cloudcourse.slot_of(b048) is None
|
||||
# Different board widths, same sentinel rule.
|
||||
assert len(wa55) != len(b048)
|
||||
|
||||
def test_undiscovered_counts_against_what_the_device_advertises(self):
|
||||
rep = _load_device("washer_ww5000c_cloud")["/course/vs/0"]
|
||||
store = cloudcourse.CloudCourses()
|
||||
|
||||
Reference in new issue
Block a user