"""A change you cannot measure is a change you cannot verify.""" from tiny_computer_agent import ChangeVerifier, Delta, Element, Observation, diff, element_key def frame(elements, text=""): return Observation(index=0, frame="frame", elements=tuple(elements), text=text) def button(label, x=10, y=10, width=60, height=20, kind="button"): return Element(label, kind=kind, x=x, y=y, width=width, height=height) def test_nothing_changed_is_measured_rather_than_guessed(): picture = frame([button("Save", x=100)]) delta = diff(picture, picture) assert delta.empty and not delta.changed assert delta.summary() == "no change" assert isinstance(delta, Delta) def test_a_dialog_closing_is_a_removed_element_not_a_missing_string(): """The whole point: verification without depending on the dialog's own words.""" before = frame([button("Save As"), button("Cancel", x=80)]) after = frame([button("Save As")]) delta = diff(before, after) assert delta.closed and not delta.opened assert [e.label for e in delta.removed] == ["Cancel"] assert delta.matches(disappeared=["cancel"]) assert not delta.matches(appeared=["cancel"]) def test_a_menu_opening_is_an_added_element(): delta = diff(frame([button("File")]), frame([button("File"), button("Save As", y=60)])) assert delta.opened and not delta.closed assert [e.label for e in delta.added] == ["Save As"] assert delta.matches(appeared=["save as"]) def test_a_redraw_is_not_movement_and_a_real_move_is(): still = diff(frame([button("Save", x=100)]), frame([button("Save", x=101)])) assert still.empty, "a one-pixel re-render is not a change" moved = diff(frame([button("Save", x=100)]), frame([button("Save", x=400)])) assert moved.moved and moved.settled, "nothing appeared or vanished: it moved" assert not moved.changed is False assert moved.to_dict()["moved"] == ["Save"] def test_labels_are_compared_the_way_the_index_compares_them(): """OCR drops spaces and models add punctuation, so identity is the alphanumeric core.""" before = frame([button("Save As")]) after = frame([button("SaveAs.")]) delta = diff(before, after) assert delta.empty, "the same label, differently spelled, is the same element" assert element_key(button("Save As")) == element_key(button("SaveAs.")) def test_added_and_removed_are_told_apart_when_counts_differ(): before = frame([button("Row 1"), button("Row 2")]) after = frame([button("Row 2"), button("Row 3"), button("Row 4")]) delta = diff(before, after) assert sorted(e.label for e in delta.added) == ["Row 3", "Row 4"] assert [e.label for e in delta.removed] == ["Row 1"] assert delta.summary().startswith("+2") def test_the_verifier_remembers_the_previous_frame(): verify = ChangeVerifier(disappeared=["Cancel"]) with_cancel = frame([button("Save"), button("Cancel", x=200)]) without_cancel = frame([button("Save")]) assert verify(with_cancel) is False, "the first frame has nothing to compare against" assert verify(without_cancel) is True assert verify.last is not None and verify.last.closed, "Cancel is gone: the dialog closed" assert verify(without_cancel) is False, "and the comparison advances with the frame" assert verify.last is not None and verify.last.empty def test_the_verifier_can_be_reset_between_episodes(): verify = ChangeVerifier(appeared=["Saved"]) verify(frame([button("Save")])) verify.reset() assert verify.previous is None and verify.last is None assert verify(frame([button("Saved")])) is False