nighty night

This commit is contained in:
firestar5683
2026-07-14 01:15:35 -05:00
parent fcd39be875
commit 45eead8dcd
10 changed files with 235 additions and 37 deletions
Binary file not shown.
+6 -6
View File
@@ -323,7 +323,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"FLMActiveProfileId", {PERSISTENT, STRING, "", "", 2}},
{"FLMTrialBaseline", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"FLMTrialApplied", {PERSISTENT, BOOL, "0", "0", 2}},
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
{"FPSCounter", {PERSISTENT, BOOL, "0", "0", 3}},
{"GalaxyDashboardStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"StarPilotApiToken", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"StarPilotCarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES, "", ""}},
@@ -448,7 +448,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2}},
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0}},
{"NudgelessLaneChangeOnlyWhenEngaged", {PERSISTENT, BOOL, "0", "0", 1}},
{"NumericalTemp", {PERSISTENT, BOOL, "1", "0", 3}},
{"NumericalTemp", {PERSISTENT, BOOL, "0", "0", 3}},
{"Offset1", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
{"Offset2", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
{"Offset3", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
@@ -520,11 +520,11 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShowCCMStatus", {PERSISTENT, BOOL, "0", "0", 2}},
{"ShowCPU", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowCPU", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowIP", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowMemoryUsage", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowMemoryUsage", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShownToggleDescriptions", {PERSISTENT, JSON, "{}", "{}"}},
{"ShowSLCOffset", {PERSISTENT, BOOL, "1", "0", 0}},
@@ -534,7 +534,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ShowStoppingPointMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowStorageLeft", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowStorageUsed", {PERSISTENT, BOOL, "0", "0", 3}},
{"SidebarMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"SidebarMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
{"SidebarOpen", {PERSISTENT, BOOL, "0", "0", 0}},
{"SignalAnimation", {PERSISTENT, STRING, "stock", "stock", 0}},
{"SignalMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
@@ -567,7 +567,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SpeedLimitSources", {PERSISTENT, BOOL, "0", "0", 3}},
{"VisionSpeedLimitAutoBookmark", {PERSISTENT, BOOL, "0", "0", 0}},
{"VisionSpeedLimitAutoPreserveSegment", {PERSISTENT, BOOL, "0", "0", 0}},
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "0", "0", 0}},
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "1", "0", 0}},
{"VisionSpeedLimitTrainingCollector", {PERSISTENT, BOOL, "1", "1", 0}},
{"StandardFollow", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.2", "1.2", 2}},
Binary file not shown.
@@ -52,12 +52,18 @@ LOCK_CMD = b"\x40\x05\x30\x11\x00\x80\x00\x00"
UNLOCK_CMD = b"\x40\x05\x30\x11\x00\x40\x00\x00"
def is_ths_hybrid(CP) -> bool:
return CP.carFingerprint == CAR.TOYOTA_PRIUS or (
CP.carFingerprint == CAR.TOYOTA_CAMRY and bool(CP.flags & ToyotaFlags.HYBRID.value)
)
def get_long_tune(CP, params):
kiBP = [2., 5.]
kiV = [0.5, 0.25]
k_f = 1.0
if CP.carFingerprint == CAR.TOYOTA_PRIUS:
if is_ths_hybrid(CP):
k_f = 0.8
elif CP.carFingerprint not in TSS2_CAR:
kiBP = [0., 5., 35.]
@@ -440,7 +446,7 @@ class CarController(CarControllerBase):
else:
# constantly slowly unwind integral to recover from large temporary errors
unwind_rate = ACCEL_PID_UNWIND
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS and pcm_accel_cmd * self.long_pid.i < 0.0:
if is_ths_hybrid(self.CP) and pcm_accel_cmd * self.long_pid.i < 0.0:
unwind_rate *= PRIUS_INTEGRAL_MISMATCH_UNWIND
self.long_pid.i -= unwind_rate * float(np.sign(self.long_pid.i))
@@ -158,6 +158,8 @@ class CarInterface(CarInterfaceBase):
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
prius_long_defaults = candidate == CAR.TOYOTA_PRIUS and ret.openpilotLongitudinalControl
camry_hybrid_long_defaults = (candidate == CAR.TOYOTA_CAMRY and ret.openpilotLongitudinalControl and
bool(ret.flags & ToyotaFlags.HYBRID.value))
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED or prius_long_defaults:
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
@@ -170,6 +172,13 @@ class CarInterface(CarInterfaceBase):
if ret.flags & ToyotaFlags.HYBRID.value:
ret.longitudinalActuatorDelay = 0.05
if camry_hybrid_long_defaults:
# The THS eCVT responds much faster than the legacy non-TSS2 ICE tune.
ret.longitudinalActuatorDelay = 0.05
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
ret.stoppingDecelRate = 0.3
if ret.enableGasInterceptorDEPRECATED:
# Pedal/SDSU Toyotas feel best with a softer final stop clamp.
ret.longitudinalActuatorDelay = max(ret.longitudinalActuatorDelay, 0.2)
@@ -8,7 +8,7 @@ from opendbc.can import CANPacker, CANParser
from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.toyota import toyotacan
from opendbc.car.toyota.carcontroller import CarController, get_prius_positive_feedforward_scale, limit_interceptor_pcm_accel, \
from opendbc.car.toyota.carcontroller import CarController, get_long_tune, get_prius_positive_feedforward_scale, limit_interceptor_pcm_accel, \
limit_interceptor_stopping_accel, limit_no_lead_cruise_sign_flip, \
limit_prius_stopping_accel, update_permit_braking
from opendbc.car.toyota.carstate import calculate_interceptor_gas_pressed
@@ -82,7 +82,7 @@ class TestToyotaInterfaces:
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
def test_camry_continental_radar_keeps_standard_longitudinal_tune(self):
def test_camry_hybrid_continental_radar_uses_ths_longitudinal_tune(self):
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
hybrid_fw = [CarParams.CarFw(ecu=Ecu.hybrid, address=0x7D2, fwVersion=b"test")]
car_params = CarInterface.get_params(
@@ -98,17 +98,44 @@ class TestToyotaInterfaces:
assert car_params.openpilotLongitudinalControl
assert not car_params.radarUnavailable
assert abs(car_params.radarTimeStepDEPRECATED - 0.1) < 1e-6
assert abs(car_params.longitudinalActuatorDelay - 0.15) < 1e-6
assert abs(car_params.vEgoStopping - 0.5) < 1e-6
assert abs(car_params.vEgoStarting - 0.5) < 1e-6
assert abs(car_params.stoppingDecelRate - 0.8) < 1e-6
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
assert abs(car_params.stoppingDecelRate - 0.3) < 1e-6
assert not car_params.flags & ToyotaFlags.NO_STOP_TIMER.value
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
controller.speed = 0.0
assert controller.k_i == pytest.approx(0.5)
assert controller.k_f == pytest.approx(0.8)
radar_interface = RadarInterface(car_params)
assert radar_interface.radar_acc_tssp
assert radar_interface.rcp is not None
assert radar_interface.pt_cp is not None
def test_camry_ice_keeps_legacy_longitudinal_tune(self):
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
car_params = CarInterface.get_params(
CAR.TOYOTA_CAMRY,
fingerprint,
[],
alpha_long=True,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert not car_params.flags & ToyotaFlags.HYBRID.value
assert car_params.longitudinalActuatorDelay == pytest.approx(0.15)
assert car_params.vEgoStopping == pytest.approx(0.5)
assert car_params.stoppingDecelRate == pytest.approx(0.8)
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
controller.speed = 0.0
assert controller.k_i == pytest.approx(3.6)
assert controller.k_f == pytest.approx(1.0)
def test_camry_continental_radar_converts_absolute_target_speed(self):
radar_interface = RadarInterface.__new__(RadarInterface)
radar_interface.CP = SimpleNamespace(wheelSpeedFactor=1.0)
+27 -11
View File
@@ -10,6 +10,7 @@ from collections import defaultdict, deque
from pathlib import Path
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
MAPD_DIR = Path(BASEDIR) / "starpilot/navigation"
@@ -19,6 +20,8 @@ RESTART_DELAY_S = 0.25
MISSING_TILE_BACKOFF_S = 30.0
FAILURE_WINDOW_S = 3.0
FAILURE_THRESHOLD = 3
MISSING_COVERAGE_EXIT_CODE = 3
ROAD_STATE_POLL_S = 1.0
def extract_bounds_filename(line: str) -> str | None:
@@ -75,25 +78,19 @@ class CorruptTileMonitor:
def quarantine_offline_tile(filename: str) -> Path | None:
tile_path = Path(filename)
try:
relative_path = tile_path.relative_to(OFFLINE_ROOT)
tile_path.relative_to(OFFLINE_ROOT)
except ValueError:
cloudlog.warning(f"mapd_wrapper refusing to quarantine unexpected path: {filename}")
return None
quarantine_path = tile_path if tile_path.is_file() else None
if quarantine_path is None and len(relative_path.parts) >= 2:
archive_path = OFFLINE_ROOT / relative_path.parts[0] / f"{relative_path.parts[1]}.tar.gz"
if archive_path.is_file():
quarantine_path = archive_path
if quarantine_path is None:
if not tile_path.is_file():
return None
quarantined = quarantine_path.with_name(f"{quarantine_path.name}.corrupt.{int(time.time())}")
quarantined = tile_path.with_name(f"{tile_path.name}.corrupt.{int(time.time())}")
try:
quarantine_path.rename(quarantined)
tile_path.rename(quarantined)
except OSError:
cloudlog.exception(f"mapd_wrapper failed to quarantine offline data: {quarantine_path}")
cloudlog.exception(f"mapd_wrapper failed to quarantine offline data: {tile_path}")
return None
return quarantined
@@ -145,6 +142,15 @@ def run_mapd_once() -> int:
for line in proc.stdout:
print(line, end="")
bad_tile = monitor.observe(line)
# mapd reports an unmarshal failure even when no offline tile is installed.
# Stop its resulting hot loop until the next onroad process cycle.
missing_tile = monitor.current_filename
if is_offline_read_error(line) and missing_tile is not None and not Path(missing_tile).is_file():
cloudlog.info(f"mapd_wrapper has no offline tile for {missing_tile}; stopping mapd until the next drive")
terminate_child(proc)
return MISSING_COVERAGE_EXIT_CODE
if bad_tile is None:
continue
@@ -170,7 +176,14 @@ def run_mapd_once() -> int:
return proc.wait()
def wait_for_road_state_change(params: Params) -> None:
initial_onroad = params.get_bool("IsOnroad")
while params.get_bool("IsOnroad") == initial_onroad:
time.sleep(ROAD_STATE_POLL_S)
def main() -> None:
params = Params()
while True:
exit_code = run_mapd_once()
if exit_code == 1:
@@ -179,6 +192,9 @@ def main() -> None:
if exit_code == 2:
time.sleep(MISSING_TILE_BACKOFF_S)
continue
if exit_code == MISSING_COVERAGE_EXIT_CODE:
wait_for_road_state_change(params)
continue
raise SystemExit(exit_code)
+50 -11
View File
@@ -4,7 +4,7 @@ import subprocess
from pathlib import Path
from openpilot.starpilot.navigation.mapd_wrapper import CorruptTileMonitor, quarantine_offline_tile, terminate_child
from openpilot.starpilot.navigation.mapd_wrapper import CorruptTileMonitor, quarantine_offline_tile, run_mapd_once, terminate_child, wait_for_road_state_change
def _loading_line(filename: str) -> str:
@@ -43,21 +43,60 @@ def test_quarantine_offline_tile_renames_file(tmp_path, monkeypatch):
assert Path(quarantined).name.startswith(f"{tile.name}.corrupt.")
def test_quarantine_offline_tile_renames_backing_archive(tmp_path, monkeypatch):
def test_quarantine_offline_tile_ignores_missing_file(tmp_path, monkeypatch):
offline_root = tmp_path / "offline"
archive = offline_root / "34/-88.tar.gz"
archive.parent.mkdir(parents=True)
archive.write_text("bad archive")
virtual_tile = offline_root / "34/-88/34.750000_-87.750000_35.000000_-87.500000"
missing_tile = offline_root / "34/-88/34.750000_-87.750000_35.000000_-87.500000"
monkeypatch.setitem(quarantine_offline_tile.__globals__, "OFFLINE_ROOT", offline_root)
quarantined = quarantine_offline_tile(virtual_tile.as_posix())
assert quarantine_offline_tile(missing_tile.as_posix()) is None
assert quarantined is not None
assert not archive.exists()
assert Path(quarantined).exists()
assert Path(quarantined).name.startswith(f"{archive.name}.corrupt.")
def test_run_mapd_once_stops_for_missing_offline_coverage(tmp_path, monkeypatch):
missing_tile = tmp_path / "offline/36/-98/37.500000_-98.000000_37.750000_-97.750000"
output = []
for _ in range(4):
output.extend((_loading_line(missing_tile.as_posix()), _error_line()))
class CompletedProcess:
pid = 123
def __init__(self):
self.stdout = iter(f"{line}\n" for line in output)
self.terminated = False
def poll(self):
return 0 if self.terminated else None
def terminate(self):
self.terminated = True
def wait(self, timeout=None):
return 0
proc = CompletedProcess()
monkeypatch.setitem(run_mapd_once.__globals__, "OFFLINE_ROOT", tmp_path / "offline")
monkeypatch.setattr(subprocess, "Popen", lambda *args, **kwargs: proc)
monkeypatch.setattr("signal.signal", lambda *args: None)
assert run_mapd_once() == 3
assert proc.terminated
def test_missing_coverage_waits_for_road_state_change(monkeypatch):
class Params:
states = iter((True, True, False))
def get_bool(self, key):
assert key == "IsOnroad"
return next(self.states)
sleeps = []
monkeypatch.setattr("time.sleep", sleeps.append)
wait_for_road_state_change(Params())
assert sleeps == [1.0]
def test_terminate_child_tolerates_wedged_process():
+51 -1
View File
@@ -10,6 +10,15 @@ LONG_PITCH_KEY = "LongPitch"
STEER_KP_KEY = "SteerKP"
STEER_KP_STOCK_KEY = "SteerKPStock"
USE_OLD_UI_KEY = "UseOldUI"
VISION_SPEED_LIMIT_DETECTION_KEY = "VisionSpeedLimitDetection"
DEVELOPER_METRIC_DISPLAY_KEYS = (
"FPSCounter",
"ShowCPU",
"ShowGPU",
"NumericalTemp",
"ShowMemoryUsage",
"SidebarMetrics",
)
DEFAULT_STEER_KP = 0.6
LEGACY_STEER_KP = 0.7
@@ -20,6 +29,8 @@ BRANCH_DEFAULTS_MIGRATION_MARKER = ".starpilot_branch_defaults_migrations_v1"
ACCELERATION_PROFILE_MIGRATION_MARKER = ".starpilot_acceleration_profile_default_v1"
USE_OLD_UI_MIGRATION_MARKER = ".starpilot_use_old_ui_migration_v2"
LATERAL_METHOD_REBRAND_MIGRATION_MARKER = ".starpilot_lateral_method_rebrand_v1"
VISION_SPEED_LIMIT_DETECTION_MIGRATION_MARKER = ".starpilot_vision_speed_limit_detection_v1"
DEVELOPER_METRIC_DISPLAY_MIGRATION_MARKER = ".starpilot_developer_metric_display_off_v1"
MARKER_DIRNAME = ".starpilot_param_migrations"
LATERAL_METHOD_PARAM_SUFFIXES = (
@@ -99,6 +110,14 @@ def _lateral_method_rebrand_marker_path(params: ParamsLike) -> Path:
return _marker_dir_path(params) / LATERAL_METHOD_REBRAND_MIGRATION_MARKER
def _vision_speed_limit_detection_marker_path(params: ParamsLike) -> Path:
return _marker_dir_path(params) / VISION_SPEED_LIMIT_DETECTION_MIGRATION_MARKER
def _developer_metric_display_marker_path(params: ParamsLike) -> Path:
return _marker_dir_path(params) / DEVELOPER_METRIC_DISPLAY_MIGRATION_MARKER
def _marker_dir_path(params: ParamsLike) -> Path:
params_path = Path(params.get_param_path())
# Params.clear_all() removes unknown files inside the params directory, so
@@ -208,11 +227,36 @@ def _apply_lateral_method_rebrand_migration(params: ParamsLike, marker: Path) ->
marker.touch()
def _apply_vision_speed_limit_detection_migration(params: ParamsLike, marker: Path) -> None:
if marker.exists():
return
marker.parent.mkdir(parents=True, exist_ok=True)
params.put_bool(VISION_SPEED_LIMIT_DETECTION_KEY, True)
marker.touch()
def _apply_developer_metric_display_migration(params: ParamsLike, marker: Path) -> None:
if marker.exists():
return
marker.parent.mkdir(parents=True, exist_ok=True)
for key in DEVELOPER_METRIC_DISPLAY_KEYS:
params.put_bool(key, False)
marker.touch()
def apply_launch_param_migrations(params: ParamsLike, marker_path: Path | None = None,
branch_defaults_marker_path: Path | None = None,
acceleration_profile_marker_path: Path | None = None,
use_old_ui_marker_path: Path | None = None,
lateral_method_rebrand_marker_path: Path | None = None) -> None:
lateral_method_rebrand_marker_path: Path | None = None,
vision_speed_limit_detection_marker_path: Path | None = None,
developer_metric_display_marker_path: Path | None = None) -> None:
_apply_legacy_launch_param_migrations(params, marker_path or _default_marker_path(params))
# Keep branch-default rollout on its own marker so older installs that already
# have the legacy marker still receive this one-time param reset.
@@ -224,6 +268,12 @@ def apply_launch_param_migrations(params: ParamsLike, marker_path: Path | None =
_apply_lateral_method_rebrand_migration(
params, lateral_method_rebrand_marker_path or _lateral_method_rebrand_marker_path(params)
)
_apply_vision_speed_limit_detection_migration(
params, vision_speed_limit_detection_marker_path or _vision_speed_limit_detection_marker_path(params)
)
_apply_developer_metric_display_migration(
params, developer_metric_display_marker_path or _developer_metric_display_marker_path(params)
)
def main() -> int:
@@ -3,12 +3,15 @@ from pathlib import Path
from openpilot.system.manager.launch_param_migrations import (
ACCELERATION_PROFILE_MIGRATION_MARKER,
BRANCH_DEFAULTS_MIGRATION_MARKER,
DEVELOPER_METRIC_DISPLAY_KEYS,
DEVELOPER_METRIC_DISPLAY_MIGRATION_MARKER,
DEFAULT_STEER_KP,
LAUNCH_PARAM_MIGRATION_MARKER,
LATERAL_METHOD_REBRAND_MIGRATION_MARKER,
MARKER_DIRNAME,
STANDARD_ACCELERATION_PROFILE,
USE_OLD_UI_MIGRATION_MARKER,
VISION_SPEED_LIMIT_DETECTION_MIGRATION_MARKER,
apply_launch_param_migrations,
)
@@ -258,3 +261,51 @@ def test_apply_launch_param_migrations_preserves_active_lateral_method_trial(tmp
assert params.get(f"FLM{suffix}") == value
assert not Path(params.get_param_path(f"{legacy_prefix}{suffix}")).exists()
assert marker_path(tmp_path, LATERAL_METHOD_REBRAND_MIGRATION_MARKER).is_file()
def test_apply_launch_param_migrations_enables_vision_speed_limit_detection_once(tmp_path):
params = FileBackedFakeParams(tmp_path / "params")
params.put_bool("VisionSpeedLimitDetection", False)
params.put("SLCPriority1", "Dashboard")
params.put("SLCPriority2", "Map Data")
apply_launch_param_migrations(params)
assert params.get_bool("VisionSpeedLimitDetection")
assert params.get("SLCPriority1") == "Dashboard"
assert params.get("SLCPriority2") == "Map Data"
assert marker_path(tmp_path, VISION_SPEED_LIMIT_DETECTION_MIGRATION_MARKER).is_file()
def test_apply_launch_param_migrations_does_not_reenable_vision_speed_limit_detection_after_marker(tmp_path):
params = FileBackedFakeParams(tmp_path / "params")
params.put_bool("VisionSpeedLimitDetection", False)
marker_path(tmp_path, VISION_SPEED_LIMIT_DETECTION_MIGRATION_MARKER).touch()
apply_launch_param_migrations(params)
assert not params.get_bool("VisionSpeedLimitDetection")
def test_apply_launch_param_migrations_disables_developer_metric_display_once(tmp_path):
params = FileBackedFakeParams(tmp_path / "params")
for key in DEVELOPER_METRIC_DISPLAY_KEYS:
params.put_bool(key, True)
apply_launch_param_migrations(params)
for key in DEVELOPER_METRIC_DISPLAY_KEYS:
assert not params.get_bool(key)
assert marker_path(tmp_path, DEVELOPER_METRIC_DISPLAY_MIGRATION_MARKER).is_file()
def test_apply_launch_param_migrations_preserves_developer_metric_display_after_marker(tmp_path):
params = FileBackedFakeParams(tmp_path / "params")
for key in DEVELOPER_METRIC_DISPLAY_KEYS:
params.put_bool(key, True)
marker_path(tmp_path, DEVELOPER_METRIC_DISPLAY_MIGRATION_MARKER).touch()
apply_launch_param_migrations(params)
for key in DEVELOPER_METRIC_DISPLAY_KEYS:
assert params.get_bool(key)