2020
2121
2222ROOT = Path (__file__ ).resolve ().parents [1 ]
23+ README_PATHS = (
24+ "README.md" ,
25+ "README.zh-CN.md" ,
26+ "README.zh-TW.md" ,
27+ )
28+ MEMORY_EVIDENCE_PATH = (
29+ ROOT / "tests" / "data" / "memory_validation_evidence.json"
30+ )
31+
32+
33+ def _anchored_readme_section (readme : str , anchor : str ) -> str :
34+ match = re .search (
35+ rf'<a id="{ re .escape (anchor )} "></a>\n\n'
36+ rf"### [^\n]+\n\n"
37+ rf"(?P<body>.*?)"
38+ rf'(?=\n(?:<a id="[^"]+"></a>\n\n)?### )' ,
39+ readme ,
40+ flags = re .DOTALL ,
41+ )
42+ assert match is not None , anchor
43+ return match .group ("body" )
2344
2445
2546def _run_fakegpu (* args : str ) -> subprocess .CompletedProcess [str ]:
@@ -241,6 +262,63 @@ def test_fakecuda_profile_matrix() -> None:
241262 )
242263
243264
265+ def test_calibrate_compare_cli_writes_error_and_safety_data (
266+ tmp_path : Path ,
267+ ) -> None :
268+ prediction_path = tmp_path / "prediction.json"
269+ observation_path = tmp_path / "observation.json"
270+ output_path = tmp_path / "comparison.json"
271+ prediction_path .write_text (
272+ json .dumps (
273+ {
274+ "schema_version" : "test_prediction.v1" ,
275+ "memory_timeline" : {
276+ "phases" : [
277+ {"phase" : "peak" , "peak_bytes" : 900 },
278+ ]
279+ },
280+ }
281+ ),
282+ encoding = "utf-8" ,
283+ )
284+ observation_path .write_text (
285+ json .dumps (
286+ {
287+ "schema_version" : "test_observation.v1" ,
288+ "memory_timeline" : {
289+ "phases" : [
290+ {"phase" : "peak" , "peak_bytes" : 1_000 },
291+ ]
292+ },
293+ }
294+ ),
295+ encoding = "utf-8" ,
296+ )
297+
298+ result = _run_fakegpu (
299+ "calibrate" ,
300+ "compare" ,
301+ str (prediction_path ),
302+ str (observation_path ),
303+ "--json" ,
304+ str (output_path ),
305+ )
306+
307+ assert result .returncode == 0 , result .stderr
308+ comparison = json .loads (output_path .read_text (encoding = "utf-8" ))
309+ assert (
310+ comparison ["schema_version" ]
311+ == "fakegpu.calibration_comparison.v1"
312+ )
313+ assert comparison ["comparisons" ][0 ]["absolute_percentage_error" ] == 0.1
314+ summary = comparison ["summary" ]
315+ assert summary ["underprediction_phase_count" ] == 1
316+ assert summary ["recommended_memory_safety_margin_bytes" ] == 100
317+ assert summary ["recommended_memory_safety_factor" ] == pytest .approx (
318+ 10 / 9
319+ )
320+
321+
244322def test_top_level_help_names_builtin_commands () -> None :
245323 result = _run_fakegpu ("--help" )
246324 assert result .returncode == 0
@@ -252,11 +330,7 @@ def test_top_level_help_names_builtin_commands() -> None:
252330 command_result .stderr ,
253331 )
254332
255- for readme_path in (
256- "README.md" ,
257- "README.zh-CN.md" ,
258- "README.zh-TW.md" ,
259- ):
333+ for readme_path in README_PATHS :
260334 readme = (ROOT / readme_path ).read_text (encoding = "utf-8" )
261335 documented_commands = set (
262336 re .findall (
@@ -266,3 +340,107 @@ def test_top_level_help_names_builtin_commands() -> None:
266340 )
267341 )
268342 assert documented_commands == set (BUILTIN_COMMANDS ), readme_path
343+
344+
345+ def test_readmes_document_supported_use_cases () -> None :
346+ use_case_commands = {
347+ "estimate-llm" ,
348+ "preflight" ,
349+ "demo" ,
350+ "validate" ,
351+ "plan-training" ,
352+ "analyze-repo" ,
353+ "analyze-kernel" ,
354+ "capabilities" ,
355+ "simulate-topology" ,
356+ "replay-trace" ,
357+ "bandwidth" ,
358+ "calibrate" ,
359+ }
360+ assert use_case_commands <= set (BUILTIN_COMMANDS )
361+
362+ for readme_path in README_PATHS :
363+ readme = (ROOT / readme_path ).read_text (encoding = "utf-8" )
364+ for language_path in README_PATHS :
365+ assert f"({ language_path } )" in readme , (
366+ readme_path ,
367+ language_path ,
368+ )
369+
370+ section = _anchored_readme_section (readme , "use-cases" )
371+ table_rows = [
372+ line for line in section .splitlines () if line .startswith ("|" )
373+ ]
374+ assert len (table_rows ) == 8 , readme_path
375+ for command in use_case_commands :
376+ assert re .search (
377+ rf"`{ re .escape (command )} (?:`| )" ,
378+ section ,
379+ ), (readme_path , command )
380+
381+
382+ def test_readmes_match_memory_validation_evidence () -> None :
383+ evidence = json .loads (MEMORY_EVIDENCE_PATH .read_text (encoding = "utf-8" ))
384+ revision = evidence ["source_revision" ]
385+ groups = {
386+ group ["id" ]: group for group in evidence ["evidence_groups" ]
387+ }
388+
389+ controlled = groups ["controlled_aten" ]
390+ controlled_error = float (
391+ controlled [
392+ "published_maximum_absolute_percentage_error_percent"
393+ ]
394+ )
395+ inference = groups ["qwen3_8b_inference" ]["measurements" ]
396+ inference_errors = [
397+ abs (item ["predicted_bytes" ] - item ["observed_bytes" ])
398+ / item ["observed_bytes" ]
399+ * 100
400+ for item in inference
401+ ]
402+ sft_errors = [
403+ item ["published_absolute_percentage_error_percent" ]
404+ for item in groups ["qwen_sft" ]["measurements" ]
405+ ]
406+ qlora_errors = [
407+ item ["published_absolute_percentage_error_percent" ]
408+ for item in groups ["qwen_qlora" ]["measurements" ]
409+ ]
410+ expected_claims = {
411+ str (controlled ["workload_count" ]),
412+ str (controlled ["observation_count" ]),
413+ f"{ controlled_error :.2f} %" ,
414+ f"{ 100 - controlled_error :.2f} %" ,
415+ * (f"{ error :.4f} %" for error in inference_errors ),
416+ * (f"{ 100 - error :.4f} %" for error in inference_errors ),
417+ f"{ min (sft_errors ):.3f} %–{ max (sft_errors ):.3f} %" ,
418+ f"{ 100 - max (sft_errors ):.3f} %–{ 100 - min (sft_errors ):.3f} %" ,
419+ f"{ min (qlora_errors ):.3f} %–{ max (qlora_errors ):.3f} %" ,
420+ (
421+ f"{ 100 - max (qlora_errors ):.3f} %–"
422+ f"{ 100 - min (qlora_errors ):.3f} %"
423+ ),
424+ }
425+
426+ for readme_path in README_PATHS :
427+ readme = (ROOT / readme_path ).read_text (encoding = "utf-8" )
428+ section = _anchored_readme_section (
429+ readme ,
430+ "memory-estimation-evidence" ,
431+ )
432+ evidence_rows = [
433+ line for line in section .splitlines () if line .startswith ("|" )
434+ ]
435+ assert len (evidence_rows ) == 6 , readme_path
436+ for claim in expected_claims :
437+ assert claim in section , (readme_path , claim )
438+ assert "tests/data/memory_validation_evidence.json" in section
439+
440+ for group in groups .values ():
441+ source_url = (
442+ "https://github.com/FanBB2333/FakeGPU/blob/"
443+ f"{ revision } /{ group ['source_path' ]} "
444+ f"#{ group ['source_anchor' ]} "
445+ )
446+ assert source_url in readme , (readme_path , group ["id" ])
0 commit comments