feat(license): wafer license gate, runtime warning, and first-launch wizard
- Gate READ/ERASE/DEBUG behind wafer license check; log hint to load .bin - waferRuntimeExceeded() warns when onboard runtime > 350s - LicenseModel: remove-license (soft hide, .bin stays on disk) via removed_licenses.txt; re-loading un-hides - FirstLaunchWizard: 2-step popup (license + save dir) shown on first run - Per-tab file memory in HomePage (map/graph/data don't bleed state) - Merge test_device_controller_license into test_device_controller
This commit is contained in:
@@ -135,6 +135,63 @@ def test_model_load_license_file_url(tmp_path, qapp):
|
||||
assert model.loadLicenseFile("file://" + src) is True
|
||||
|
||||
|
||||
def test_model_remove_license_keeps_bin_on_disk(tmp_path, qapp):
|
||||
(tmp_path / "licenses").mkdir()
|
||||
write_license(tmp_path / "licenses", serial="A00001", level="01")
|
||||
model = LicenseModel(str(tmp_path))
|
||||
assert len(model.licenses) == 1
|
||||
|
||||
assert model.removeLicense("A00001", "01") is True
|
||||
assert model.licenses == []
|
||||
# app-side removal only — the vendor-issued .bin is never deleted
|
||||
assert (tmp_path / "licenses" / "A00001-01.bin").exists()
|
||||
|
||||
# sticky across a fresh scan (not just until the next refresh)
|
||||
model.refresh()
|
||||
assert model.licenses == []
|
||||
|
||||
# sticky across a real relaunch — brand new instance, no shared state
|
||||
relaunched = LicenseModel(str(tmp_path))
|
||||
assert relaunched.licenses == []
|
||||
|
||||
|
||||
def test_model_remove_license_missing_returns_false(tmp_path, qapp):
|
||||
model = LicenseModel(str(tmp_path))
|
||||
assert model.removeLicense("Z99999", "00") is False
|
||||
|
||||
|
||||
def test_model_reload_after_remove_unhides(tmp_path, qapp):
|
||||
src_dir = tmp_path / "dl"
|
||||
src_dir.mkdir()
|
||||
src = write_license(src_dir, serial="A00001", level="01")
|
||||
model = LicenseModel(str(tmp_path / "appdata"))
|
||||
assert model.loadLicenseFile(src) is True
|
||||
assert len(model.licenses) == 1
|
||||
|
||||
model.removeLicense("A00001", "01")
|
||||
assert model.licenses == []
|
||||
|
||||
# explicit reload of the same file brings it back
|
||||
assert model.loadLicenseFile(src) is True
|
||||
assert len(model.licenses) == 1
|
||||
|
||||
|
||||
def test_model_reload_in_place_unhides(tmp_path, qapp):
|
||||
"""Un-hiding by re-picking the .bin from its own <data_dir>/licenses/ dir —
|
||||
src and dst resolve to the same file, must not error on the copy step."""
|
||||
model = LicenseModel(str(tmp_path / "appdata"))
|
||||
write_license(tmp_path / "appdata" / "licenses", serial="A00001", level="01")
|
||||
model.refresh()
|
||||
assert len(model.licenses) == 1
|
||||
|
||||
model.removeLicense("A00001", "01")
|
||||
assert model.licenses == []
|
||||
|
||||
in_place = tmp_path / "appdata" / "licenses" / "A00001-01.bin"
|
||||
assert model.loadLicenseFile(str(in_place)) is True
|
||||
assert len(model.licenses) == 1
|
||||
|
||||
|
||||
def test_model_load_rejects_invalid(tmp_path, qapp):
|
||||
src_dir = tmp_path / "dl"
|
||||
src_dir.mkdir()
|
||||
@@ -167,6 +224,23 @@ def test_replay_state_none_then_temporary(tmp_path, qapp):
|
||||
assert model.replayTrialExpired() is False
|
||||
|
||||
|
||||
def test_start_replay_trial_does_not_reset_existing_marker(tmp_path, qapp):
|
||||
"""Repeat calls must not push the clock forward — the original
|
||||
touch(exist_ok=True) bumped mtime on every call, which would let anyone
|
||||
re-trigger the trial start (e.g. an over-limit wafer re-detected) reset
|
||||
the 30-day window indefinitely."""
|
||||
model = LicenseModel(str(tmp_path))
|
||||
assert model.startReplayTrial() is True
|
||||
first_mtime = model._trial_marker.stat().st_mtime
|
||||
|
||||
# Simulate the marker being old (as if most of the trial has elapsed).
|
||||
old_time = first_mtime - 25 * 86400
|
||||
os.utime(model._trial_marker, (old_time, old_time))
|
||||
|
||||
assert model.startReplayTrial() is True
|
||||
assert model._trial_marker.stat().st_mtime == old_time
|
||||
|
||||
|
||||
def test_replay_state_permanent_with_level1(tmp_path, qapp):
|
||||
(tmp_path / "licenses").mkdir()
|
||||
write_license(tmp_path / "licenses", level="01")
|
||||
|
||||
Reference in New Issue
Block a user