Skip to content

Commit 9ef2f11

Browse files
committed
wip: adapt fuzzer to stdcm v2
1 parent 575380a commit 9ef2f11

File tree

1 file changed

+43
-74
lines changed

1 file changed

+43
-74
lines changed

tests/fuzzer/fuzzer_v2.py

+43-74
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import datetime
22
import json
3+
import math
34
import random
45
import time
56
from collections import defaultdict
67
from dataclasses import dataclass, field
78
from enum import Enum, IntEnum
89
from functools import cache
910
from pathlib import Path
10-
from typing import Dict, Iterable, List, Optional, Set, Tuple
11+
from typing import Dict, Iterable, List, Optional, Tuple
1112

1213
import requests
1314
from osrd_schemas.switch_type import builtin_node_types
@@ -119,9 +120,9 @@ def run_test(
119120
if rolling_stock_name is None
120121
else get_rolling_stock(editoast_url, rolling_stock_name)
121122
)
122-
path, path_length = make_valid_path(infra)
123-
if random.randint(0, 1) == 0:
124-
test_new_train(editoast_url, scenario, rolling_stock.name, path_length, infra_name, path, prelude)
123+
path = make_valid_path(infra)
124+
if random.randint(0, 0) == 1:
125+
test_new_train(editoast_url, scenario, rolling_stock.name, infra_name, path, prelude)
125126
else:
126127
test_stdcm(editoast_url, scenario, rolling_stock.id, infra_name, path, prelude)
127128

@@ -130,7 +131,6 @@ def test_new_train(
130131
editoast_url: str,
131132
scenario: Scenario,
132133
rolling_stock: str,
133-
path_length: float,
134134
infra_name: str,
135135
path: List[Tuple[str, float]],
136136
prelude: List,
@@ -179,8 +179,11 @@ def test_stdcm(
179179
Not finding a path isn't considered as an error, we only look for 500 codes here.
180180
"""
181181
print("testing stdcm")
182-
stdcm_payload = make_stdcm_payload(scenario, path, rolling_stock)
183-
r = post_with_timeout(editoast_url + "stdcm/", json=stdcm_payload)
182+
stdcm_payload = make_stdcm_payload(path, rolling_stock)
183+
r = post_with_timeout(
184+
editoast_url + f"v2/timetable/{scenario.timetable}/stdcm/?infra={scenario.infra}",
185+
json=stdcm_payload
186+
)
184187
if r.status_code // 100 != 2:
185188
content = r.content.decode("utf-8")
186189
if r.status_code // 100 == 4 and "No path could be found" in content:
@@ -190,29 +193,24 @@ def test_stdcm(
190193
print("test PASSED")
191194

192195

193-
def make_stdcm_payload(scenario: Scenario, path: List[Tuple[str, float]], rolling_stock: int) -> Dict:
196+
def make_stdcm_payload(path: List[Tuple[str, float]], rolling_stock: int) -> Dict:
194197
"""
195198
Creates a payload for an STDCM request
196199
"""
197200
res = {
198-
"infra_id": scenario.infra,
199201
"rolling_stock_id": rolling_stock,
200-
"timetable_id": scenario.timetable,
201-
"start_time": random.randint(0, 3600 * 24),
202-
"maximum_departure_delay": random.randint(0, 3600 * 4),
203-
"maximum_run_time": random.randint(3600 * 5, 3600 * 10),
204-
"margin_before": random.randint(0, 600),
205-
"margin_after": random.randint(0, 600),
206-
"steps": [convert_stop(stop) for stop in path],
202+
"start_time": make_random_time(),
203+
"maximum_departure_delay": random.randint(0, 3_600_000 * 4),
204+
"maximum_run_time": random.randint(3_600_000 * 5, 3_600_000 * 10),
205+
"time_gap_before": random.randint(0, 600_000),
206+
"time_gap_after": random.randint(0, 600_000),
207+
"steps": [convert_stop_stdcm(stop) for stop in path],
207208
"comfort": "STANDARD",
208-
"standard_allowance": {
209-
"value_type": "percentage",
210-
"percentage": 0,
211-
},
209+
"margin": None,
212210
}
213211
res["steps"][-1]["duration"] = 1 # Force a stop at the end
214-
allowance_value = make_random_allowance_value(0)
215-
if allowance_value["value_type"] != "time" and random.randint(0, 2) == 0:
212+
allowance_value = make_random_margin_value()
213+
if random.randint(0, 2) == 0:
216214
res["standard_allowance"] = allowance_value
217215
return res
218216

@@ -298,15 +296,6 @@ def get_random_rolling_stock(editoast_url: str) -> RollingStock:
298296
return RollingStock(rolling_stock["name"], rolling_stock["id"])
299297

300298

301-
def format_route_node(waypoint_id, direction):
302-
"""
303-
Formats a waypoint + track direction into a string, to be used in dicts
304-
:param waypoint_id: waypoint id
305-
:param direction: direction on the track section the waypoint is placed on
306-
"""
307-
return f"{waypoint_id};{direction}"
308-
309-
310299
def make_graph(editoast_url: str, infra: int) -> InfraGraph:
311300
"""
312301
Makes a graph from the infra
@@ -338,20 +327,6 @@ def random_set_element(s: Iterable):
338327
return random.choice(list(s))
339328

340329

341-
def check_tracks_are_unseen(seen_track_sections: Set[str], route: Dict):
342-
"""
343-
Checks all tracks in the route, returns true if a track is already part of the path, adds them otherwise
344-
:param seen_track_sections: Set of track sections on the path
345-
:param route: Route to check
346-
"""
347-
for track_range in route["path"]:
348-
track_id = track_range["track"]
349-
if track_id in seen_track_sections:
350-
return False
351-
seen_track_sections.add(track_id)
352-
return True
353-
354-
355330
def make_steps_on_track(track: Dict, endpoint: Endpoint, number: float) -> List[float]:
356331
"""
357332
Generates a random list of steps on a route
@@ -420,7 +395,7 @@ def make_path(infra: InfraGraph) -> Tuple[List[Tuple[str, float]], float]:
420395
return res, total_path_length
421396

422397

423-
def make_valid_path(infra: InfraGraph) -> Tuple[List[Tuple[str, float]], float]:
398+
def make_valid_path(infra: InfraGraph) -> List[Tuple[str, float]]:
424399
"""
425400
Generates a path with at least two steps
426401
:param infra: infra graph
@@ -429,29 +404,30 @@ def make_valid_path(infra: InfraGraph) -> Tuple[List[Tuple[str, float]], float]:
429404
while True:
430405
path, path_length = make_path(infra)
431406
if len(path) > 1 and path_length > 0:
432-
return path, path_length
407+
return path
433408

434409

435-
def convert_stop(stop: Tuple[str, float]) -> Dict:
410+
def convert_stop_stdcm(stop: Tuple[str, float]) -> Dict:
436411
"""
437-
Converts a stop to be in the schedule payload
412+
Converts a stop to be in the stdcm payload
438413
:param stop: (track, offset)
439414
"""
440415
track_section, offset = stop
441-
duration = 0 if random.randint(0, 1) == 0 else random.random() * 1000
416+
duration = None if random.randint(0, 1) == 0 else to_ms(random.random() * 1_000)
442417
return {
443418
"duration": duration,
444-
"waypoints": [{"track_section": track_section, "offset": offset}],
419+
"location": {"track": track_section, "offset": to_mm(offset)},
445420
}
446421

447422

448-
def convert_stop_v2(stop: Tuple[str, float], i: int) -> Dict:
423+
def convert_stop(stop: Tuple[str, float], i: int) -> Dict:
449424
"""
450425
Converts a stop to be in the schedule payload
451426
:param stop: (track, offset)
427+
:param i: index of the stop, included as an id
452428
"""
453429
track_section, offset = stop
454-
return {"offset": round(offset * 1_000), "track": track_section, "id": str(i)}
430+
return {"offset": to_mm(offset), "track": track_section, "id": str(i)}
455431

456432

457433
def create_scenario(editoast_url: str, infra_id: int) -> Scenario:
@@ -517,7 +493,7 @@ def reset_scenario(editoast_url: str, scenario: Scenario) -> Scenario:
517493
return Scenario(project_id, study_id, new_id, infra_id, new_timetable_id)
518494

519495

520-
def make_random_allowance_value_v2() -> str:
496+
def make_random_margin_value() -> str:
521497
if random.randint(0, 3) == 0:
522498
return f"{random.randint(3, 20)}%"
523499
if random.randint(0, 3) == 0:
@@ -534,7 +510,7 @@ def make_random_margins(n_steps: int) -> Dict:
534510
i += 2 # Avoids constraints on very short ranges
535511
return {
536512
"boundaries": transitions,
537-
"values": [make_random_allowance_value_v2() for _ in range(len(transitions) + 1)],
513+
"values": [make_random_margin_value() for _ in range(len(transitions) + 1)],
538514
}
539515

540516

@@ -551,7 +527,7 @@ def make_payload_schedule(
551527
return [
552528
{
553529
"comfort": "STANDARD",
554-
"path": [convert_stop_v2(stop, i) for i, stop in enumerate(path)],
530+
"path": [convert_stop(stop, i) for i, stop in enumerate(path)],
555531
"initial_speed": 0,
556532
"labels": [],
557533
"constraint_distribution": random.choice(["STANDARD", "MARECO"]),
@@ -566,24 +542,10 @@ def make_payload_schedule(
566542
]
567543

568544

569-
def make_random_allowance_value(allowance_length) -> Dict:
570-
if random.randint(0, 3) == 0:
571-
return {
572-
"value_type": "percentage",
573-
"percentage": random.randint(3, 20) + random.random(),
574-
}
575-
if random.randint(0, 3) == 0:
576-
return {
577-
"value_type": "time_per_distance",
578-
"minutes": random.randint(3, 7) + random.random(),
579-
}
580-
return {
581-
"value_type": "time",
582-
"seconds": (random.randint(3, 7) + random.random()) * 60 * allowance_length / 100000,
583-
}
584-
585-
586545
def make_random_time():
546+
"""
547+
Generate a random datetime. All values will be within the same 24h
548+
"""
587549
start = datetime.datetime(year=2024, month=1, day=1, tzinfo=datetime.timezone.utc) # Arbitrary date
588550
date = start + datetime.timedelta(seconds=(random.randint(0, 3600 * 24)))
589551
return date.isoformat()
@@ -641,6 +603,13 @@ def get_infra(editoast_url: str, infra_name: str) -> int:
641603
raise ValueError(f"Unable to find infra {infra_name}")
642604

643605

606+
def to_mm(distance: float) -> int:
607+
return math.floor(distance * 1_000)
608+
609+
610+
to_ms = to_mm
611+
612+
644613
if __name__ == "__main__":
645614
infra_id = get_infra(EDITOAST_URL, INFRA_NAME)
646615
new_scenario = create_scenario(EDITOAST_URL, infra_id)
@@ -653,7 +622,7 @@ def get_infra(editoast_url: str, infra_name: str) -> int:
653622
EDITOAST_URL,
654623
new_scenario,
655624
scenario_ttl=20,
656-
n_test=1000,
625+
n_test=10,
657626
log_folder=Path(__file__).parent / "errors",
658627
infra_name=INFRA_NAME,
659628
rolling_stock_name=ROLLING_STOCK_NAME,

0 commit comments

Comments
 (0)