| # |
| # QR Code generator batch test (Python 3) |
| # |
| # Runs various versions of the QR Code generator test worker as subprocesses, |
| # feeds each one the same random input, and compares their output for equality. |
| # |
| # Copyright (c) Project Nayuki. (MIT License) |
| # https://www.nayuki.io/page/qr-code-generator-library |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining a copy of |
| # this software and associated documentation files (the "Software"), to deal in |
| # the Software without restriction, including without limitation the rights to |
| # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
| # the Software, and to permit persons to whom the Software is furnished to do so, |
| # subject to the following conditions: |
| # - The above copyright notice and this permission notice shall be included in |
| # all copies or substantial portions of the Software. |
| # - The Software is provided "as is", without warranty of any kind, express or |
| # implied, including but not limited to the warranties of merchantability, |
| # fitness for a particular purpose and noninfringement. In no event shall the |
| # authors or copyright holders be liable for any claim, damages or other |
| # liability, whether in an action of contract, tort or otherwise, arising from, |
| # out of or in connection with the Software or the use or other dealings in the |
| # Software. |
| # |
| |
| from __future__ import print_function |
| import itertools, random, subprocess, sys, time |
| if sys.version_info.major < 3: |
| raise RuntimeError("Requires Python 3+") |
| |
| |
| CHILD_PROGRAMS = [ |
| ["python2", "../python/qrcodegen-worker.py"], # Python 2 program |
| ["python3", "../python/qrcodegen-worker.py"], # Python 3 program |
| ["java", "-cp", "../java", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program |
| ["../c/qrcodegen-worker"], # C program |
| ["../cpp/QrCodeGeneratorWorker"], # C++ program |
| ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program |
| ] |
| |
| |
| subprocs = [] |
| |
| def main(): |
| # Launch workers |
| global subprocs |
| try: |
| for args in CHILD_PROGRAMS: |
| subprocs.append(subprocess.Popen(args, universal_newlines=True, |
| stdin=subprocess.PIPE, stdout=subprocess.PIPE)) |
| except FileNotFoundError: |
| write_all(-1) |
| raise |
| |
| # Check if any died |
| time.sleep(0.3) |
| if any(proc.poll() is not None for proc in subprocs): |
| for proc in subprocs: |
| if proc.poll() is None: |
| print(-1, file=proc.stdin) |
| proc.stdin.flush() |
| sys.exit("Error: One or more workers failed to start") |
| |
| # Do tests |
| for i in itertools.count(): |
| print("Trial {}: ".format(i), end="") |
| do_trial() |
| print() |
| |
| |
| def do_trial(): |
| mode = random.randrange(4) |
| if mode == 0: # Numeric |
| length = round((2 * 7089) ** random.random()) |
| data = [random.randrange(48, 58) for _ in range(length)] |
| elif mode == 1: # Alphanumeric |
| length = round((2 * 4296) ** random.random()) |
| data = [ord(random.choice("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")) for _ in range(length)] |
| elif mode == 2: # ASCII |
| length = round((2 * 2953) ** random.random()) |
| data = [random.randrange(128) for _ in range(length)] |
| elif mode == 3: # Byte |
| length = round((2 * 2953) ** random.random()) |
| data = [random.randrange(256) for _ in range(length)] |
| else: |
| raise AssertionError() |
| |
| write_all(length) |
| for b in data: |
| write_all(b) |
| |
| errcorlvl = random.randrange(4) |
| minversion = random.randint(1, 40) |
| maxversion = random.randint(1, 40) |
| if minversion > maxversion: |
| minversion, maxversion = maxversion, minversion |
| mask = -1 |
| if random.random() < 0.5: |
| mask = random.randrange(8) |
| boostecl = int(random.random() < 0.2) |
| print("mode={} len={} ecl={} minv={} maxv={} mask={} boost={}".format(mode, length, errcorlvl, minversion, maxversion, mask, boostecl), end="") |
| |
| write_all(errcorlvl) |
| write_all(minversion) |
| write_all(maxversion) |
| write_all(mask) |
| write_all(boostecl) |
| flush_all() |
| |
| version = read_verify() |
| print(" version={}".format(version), end="") |
| if version == -1: |
| return |
| size = version * 4 + 17 |
| for _ in range(size**2): |
| read_verify() |
| |
| |
| def write_all(val): |
| for proc in subprocs: |
| print(val, file=proc.stdin) |
| |
| def flush_all(): |
| for proc in subprocs: |
| proc.stdin.flush() |
| |
| def read_verify(): |
| val = subprocs[0].stdout.readline().rstrip("\r\n") |
| for proc in subprocs[1 : ]: |
| if proc.stdout.readline().rstrip("\r\n") != val: |
| raise ValueError("Mismatch") |
| return int(val) |
| |
| |
| if __name__ == "__main__": |
| main() |