text: #!/usr/bin/env python3
  
  # This file is part of the MicroPython project, http://micropython.org/
  # The MIT License (MIT)
  # Copyright (c) 2019 Damien P. George
  
  import os
  import subprocess
  import sys
  import argparse
  
  sys.path.append('../tools')
  import pyboard
  
  # Paths for host executables
  CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3')
  MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../ports/unix/pycopy-coverage')
  
  NATMOD_EXAMPLE_DIR = '../examples/natmod/'
  
  # Supported tests and their corresponding mpy module
  TEST_MAPPINGS = {
      'btree': 'btree/btree_$(ARCH).mpy',
      'framebuf': 'framebuf/framebuf_$(ARCH).mpy',
      'uheapq': 'uheapq/uheapq_$(ARCH).mpy',
      'urandom': 'urandom/urandom_$(ARCH).mpy',
      'ure': 'ure/ure_$(ARCH).mpy',
      'uzlib': 'uzlib/uzlib_$(ARCH).mpy',
  }
  
  # Code to allow a target MicroPython to import an .mpy from RAM
  injected_import_hook_code = """\
  import sys, uos, uio
  class __File(uio.IOBase):
    def __init__(self):
      self.off = 0
    def ioctl(self, request, arg):
      return 0
    def readinto(self, buf):
      buf[:] = memoryview(__buf)[self.off:self.off + len(buf)]
      self.off += len(buf)
      return len(buf)
  class __FS:
    def mount(self, readonly, mkfs):
      pass
    def chdir(self, path):
      pass
    def stat(self, path):
      if path == '__injected.mpy':
        return tuple(0 for _ in range(10))
      else:
        raise OSError(-2) # ENOENT
    def open(self, path, mode):
      return __File()
  uos.mount(__FS(), '/__remote')
  uos.chdir('/__remote')
  sys.modules['{}'] = __import__('__injected')
  """
  
  class TargetSubprocess:
      def __init__(self, cmd):
          self.cmd = cmd
  
      def close(self):
          pass
  
      def run_script(self, script):
          try:
              p = subprocess.run(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, input=script)
              return p.stdout, None
          except subprocess.CalledProcessError as er:
              return b'', er
  
  class TargetPyboard:
      def __init__(self, pyb):
          self.pyb = pyb
          self.pyb.enter_raw_repl()
  
      def close(self):
          self.pyb.exit_raw_repl()
          self.pyb.close()
  
      def run_script(self, script):
          try:
              self.pyb.enter_raw_repl()
              output = self.pyb.exec_(script)
              output = output.replace(b'\r\n', b'\n')
              return output, None
          except pyboard.PyboardError as er:
              return b'', er
  
  def run_tests(target_truth, target, args, stats):
      for test_file in args.files:
          # Find supported test
          for k, v in TEST_MAPPINGS.items():
              if test_file.find(k) != -1:
                  test_module = k
                  test_mpy = v.replace('$(ARCH)', args.arch)
                  break
          else:
              print('----  {} - no matching mpy'.format(test_file))
              continue
  
          # Read test script
          with open(test_file, 'rb') as f:
              test_file_data = f.read()
  
          # Create full test with embedded .mpy
          try:
              with open(NATMOD_EXAMPLE_DIR + test_mpy, 'rb') as f:
                  test_script = b'__buf=' + bytes(repr(f.read()), 'ascii') + b'\n'
          except OSError:
              print('----  {} - mpy file not compiled'.format(test_file))
              continue
          test_script += bytes(injected_import_hook_code.format(test_module), 'ascii')
          test_script += test_file_data
  
          # Run test under MicroPython
          result_out, error = target.run_script(test_script)
  
          # Work out result of test
          extra = ''
          if error is None and result_out == b'SKIP\n':
              result = 'SKIP'
          elif error is not None:
              result = 'FAIL'
              extra = ' - ' + str(error)
          else:
              # Check result against truth
              try:
                  with open(test_file + '.exp', 'rb') as f:
                      result_exp = f.read()
                  error = None
              except OSError:
                  result_exp, error = target_truth.run_script(test_file_data)
              if error is not None:
                  result = 'TRUTH FAIL'
              elif result_out != result_exp:
                  result = 'FAIL'
                  print(result_out)
              else:
                  result = 'pass'
  
          # Accumulate statistics
          stats['total'] += 1
          if result == 'pass':
              stats['pass'] += 1
          elif result == 'SKIP':
              stats['skip'] += 1
          else:
              stats['fail'] += 1
  
          # Print result
          print('{:4}  {}{}'.format(result, test_file, extra))
  
  def main():
      cmd_parser = argparse.ArgumentParser(description='Run dynamic-native-module tests under MicroPython')
      cmd_parser.add_argument('-p', '--pyboard', action='store_true', help='run tests via pyboard.py')
      cmd_parser.add_argument('-d', '--device', default='/dev/ttyACM0', help='the device for pyboard.py')
      cmd_parser.add_argument('-a', '--arch', default='x64', help='native architecture of the target')
      cmd_parser.add_argument('files', nargs='*', help='input test files')
      args = cmd_parser.parse_args()
  
      target_truth = TargetSubprocess([CPYTHON3])
  
      if args.pyboard:
          target = TargetPyboard(pyboard.Pyboard(args.device))
      else:
          target = TargetSubprocess([MICROPYTHON])
  
      stats = {'total': 0, 'pass': 0, 'fail':0, 'skip': 0}
      run_tests(target_truth, target, args, stats)
  
      target.close()
      target_truth.close()
  
      print('{} tests performed'.format(stats['total']))
      print('{} tests passed'.format(stats['pass']))
      if stats['fail']:
          print('{} tests failed'.format(stats['fail']))
      if stats['skip']:
          print('{} tests skipped'.format(stats['skip']))
  
      if stats['fail']:
          sys.exit(1)
  
  if __name__ == "__main__":
      main()
  