"""Run the AI01 lab tests one lab step at a time. python check.py every step (1-8) python check.py 3 steps 1-3 only; use this while you work python check.py --stretch every step plus the optional stretch testA step "passes" only when all of its tests pass. When one fails, read thetest's name, re-read that lab step in AI01, and climb the step's hint ladderone rung at a time."""importargparseimportosimportsysimportunittestfrompathlibimportPathHERE=Path(__file__).resolve().parentSTEP_NAMES={1:"forward pass",2:"local backward",3:"backward()",4:"accumulation",5:"more operations",6:"gradient checking",7:"neural net",8:"training",}# Which test module holds which steps.MODULE_STEPS={"test_engine":{1,2,3,4,5,6},"test_gradcheck":{6},"test_nn":{7},"test_train":{8},}defstep_of(test):"""Step number for a test, from its class name (Step3Backward -> 3)."""test=getattr(test,"test_case",test)# unwrap a failing subTestname=type(test).__name__ifname.startswith("Step")andname[4:5].isdigit():returnint(name[4])ifname.startswith("Stretch"):return"stretch"returnNonedefflatten(suite):foriteminsuite:ifisinstance(item,unittest.TestSuite):yield fromflatten(item)else:yielditemdefmain():parser=argparse.ArgumentParser(description="Run the AI01 lab tests by step.")parser.add_argument("step",nargs="?",type=int,default=8,help="run steps 1..STEP (default 8)")parser.add_argument("--stretch",action="store_true",help="also run the stretch test")args=parser.parse_args()ifnot1<=args.step<=8:parser.error("step must be between 1 and 8")ifargs.stretch:os.environ["AILAB_STRETCH"]="1"wanted=set(range(1,args.step+1))sys.path.insert(0,str(HERE/"tests"))loader=unittest.TestLoader()suite=unittest.TestSuite()formodule,stepsinMODULE_STEPS.items():ifnotsteps&wanted:continuefortestinflatten(loader.loadTestsFromName(module)):step=step_of(test)ifstepisNone:# an import error: always show itsuite.addTest(test)elifstep=="stretch"andargs.stretch:suite.addTest(test)elifstepinwanted:suite.addTest(test)print(f"AI01 lab: running steps 1-{args.step}"+(" plus stretch"ifargs.stretchelse"")+"\n")result=unittest.TextTestRunner(verbosity=2,stream=sys.stdout).run(suite)broken={}fortest,traceinresult.failures+result.errors:step=step_of(test)or"import"not_written="NotImplementedError"intracebroken.setdefault(step,[]).append((test,not_written))print("\nSummary")forstepinsorted(wanted):ifstepinbroken:tests=broken[step]ifall(nwfor_,nwintests):status="not written yet"else:first=next(tfort,nwintestsifnotnw)first=getattr(first,"test_case",first)status=f"FAIL first failing test: {type(first).__name__}.{first._testMethodName}"else:status="pass"print(f" step {step} ({STEP_NAMES[step]}): {status}")ifargs.stretch:print(f" stretch: {'FAIL'if'stretch'inbrokenelse'pass'}")if"import"inbroken:print(" A test file could not import your code. Read the traceback above first.")print(f"\n{result.testsRun} tests run, {len(result.failures)} failures, "f"{len(result.errors)} errors, {len(result.skipped)} skipped")sys.exit(0ifresult.wasSuccessful()else1)if__name__=="__main__":main()