3030import io
3131import inspect
3232import time
33+ import random
3334from pathlib import Path
3435import asyncio
3536from pyscript import RUNNING_IN_WORKER
@@ -120,6 +121,28 @@ def parse_traceback_from_exception(ex):
120121 return "\n " .join (result )
121122
122123
124+ def shuffle (a_list ):
125+ """
126+ Shuffle a list, in place.
127+
128+ This function is needed because MicroPython does not have a random.shuffle
129+ function.
130+
131+ It falls back to random.shuffle if using CPython, otherwise it uses a
132+ simple implementation of the Fisher-Yates in-place shuffle algorithm.
133+
134+ Context:
135+
136+ https://stackoverflow.com/questions/73143243/are-there-any-alternatives-for-the-python-module-randoms-shuffle-function-in
137+ """
138+ if hasattr (random , "shuffle" ):
139+ random .shuffle (a_list )
140+ else :
141+ for i in range (len (a_list ) - 1 , 0 , - 1 ):
142+ j = random .randrange (i + 1 )
143+ a_list [i ], a_list [j ] = a_list [j ], a_list [i ]
144+
145+
123146class TestCase :
124147 """
125148 Represents an individual test to run.
@@ -274,7 +297,7 @@ async def print(self, text):
274297 await asyncio .sleep (0 )
275298 print (text , end = "" , flush = True )
276299
277- async def run (self ):
300+ async def run (self , randomize = False ):
278301 """
279302 Run each TestCase instance for this module. If a setup or teardown
280303 exists, these will be evaluated immediately before and after the
@@ -284,6 +307,8 @@ async def run(self):
284307 for each skipped test.
285308 """
286309 print (f"\n { self .path } : " , end = "" )
310+ if randomize :
311+ shuffle (self ._tests )
287312 for test_case in self .tests :
288313 if self .setup :
289314 if is_awaitable (self .setup ):
@@ -466,12 +491,16 @@ async def run(*args, **kwargs):
466491 print ("Running in worker: \033 [1m" , RUNNING_IN_WORKER , "\033 [0m" )
467492 targets = []
468493 pattern = kwargs .get ("pattern" , "test_*.py" )
494+ randomize = kwargs .get ("random" , False )
495+ print ("Randomize test order: \033 [1m" , randomize , "\033 [0m" )
469496 for arg in args :
470497 if isinstance (arg , str ):
471498 targets .append (arg )
472499 else :
473500 raise ValueError (f"Unexpected argument: { arg } " )
474501 test_modules = discover (targets , pattern )
502+ if randomize :
503+ shuffle (test_modules )
475504 module_count = len (test_modules )
476505 test_count = sum ([len (module .tests ) for module in test_modules ])
477506 print (
@@ -483,7 +512,7 @@ async def run(*args, **kwargs):
483512 passed_tests = []
484513 start = time .time ()
485514 for module in test_modules :
486- await module .run ()
515+ await module .run (randomize )
487516 for test in module .tests :
488517 if test .status == FAIL :
489518 failed_tests .append (test )
@@ -542,6 +571,7 @@ async def run(*args, **kwargs):
542571 "platform" : sys .platform ,
543572 "version" : sys .version ,
544573 "running_in_worker" : RUNNING_IN_WORKER ,
574+ "randomize" : randomize ,
545575 "passes" : [test .as_dict for test in passed_tests ],
546576 "fails" : [test .as_dict for test in failed_tests ],
547577 "skipped" : [test .as_dict for test in skipped_tests ],
0 commit comments