1# 2# QR Code generator batch test (Python 3) 3# 4# Runs various versions of the QR Code generator test worker as subprocesses, 5# feeds each one the same random input, and compares their output for equality. 6# 7# Copyright (c) Project Nayuki. (MIT License) 8# https://www.nayuki.io/page/qr-code-generator-library 9# 10# Permission is hereby granted, free of charge, to any person obtaining a copy of 11# this software and associated documentation files (the "Software"), to deal in 12# the Software without restriction, including without limitation the rights to 13# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 14# the Software, and to permit persons to whom the Software is furnished to do so, 15# subject to the following conditions: 16# - The above copyright notice and this permission notice shall be included in 17# all copies or substantial portions of the Software. 18# - The Software is provided "as is", without warranty of any kind, express or 19# implied, including but not limited to the warranties of merchantability, 20# fitness for a particular purpose and noninfringement. In no event shall the 21# authors or copyright holders be liable for any claim, damages or other 22# liability, whether in an action of contract, tort or otherwise, arising from, 23# out of or in connection with the Software or the use or other dealings in the 24# Software. 25# 26 27import itertools, random, subprocess, sys, time 28if sys.version_info.major < 3: 29 raise RuntimeError("Requires Python 3+") 30 31 32CHILD_PROGRAMS = [ 33 ["python2", "-B", "../python/qrcodegen-worker.py"], # Python 2 program 34 ["python3", "-B", "../python/qrcodegen-worker.py"], # Python 3 program 35 ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program 36 ["node", "../typescript-javascript/qrcodegen-worker.js"], # TypeScript program 37 ["../c/qrcodegen-worker"], # C program 38 ["../cpp/QrCodeGeneratorWorker"], # C++ program 39 ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program 40] 41 42 43subprocs = [] 44 45def main(): 46 # Launch workers 47 global subprocs 48 try: 49 for args in CHILD_PROGRAMS: 50 subprocs.append(subprocess.Popen(args, universal_newlines=True, 51 stdin=subprocess.PIPE, stdout=subprocess.PIPE)) 52 except FileNotFoundError: 53 write_all(-1) 54 raise 55 56 # Check if any died 57 time.sleep(0.3) 58 if any(proc.poll() is not None for proc in subprocs): 59 for proc in subprocs: 60 if proc.poll() is None: 61 print(-1, file=proc.stdin) 62 proc.stdin.flush() 63 sys.exit("Error: One or more workers failed to start") 64 65 # Do tests 66 for i in itertools.count(): 67 print("Trial {}: ".format(i), end="") 68 do_trial() 69 print() 70 71 72def do_trial(): 73 mode = random.randrange(4) 74 if mode == 0: # Numeric 75 length = round((2 * 7089) ** random.random()) 76 data = [random.randrange(48, 58) for _ in range(length)] 77 elif mode == 1: # Alphanumeric 78 length = round((2 * 4296) ** random.random()) 79 data = [ord(random.choice("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")) for _ in range(length)] 80 elif mode == 2: # ASCII 81 length = round((2 * 2953) ** random.random()) 82 data = [random.randrange(128) for _ in range(length)] 83 elif mode == 3: # Byte 84 length = round((2 * 2953) ** random.random()) 85 data = [random.randrange(256) for _ in range(length)] 86 else: 87 raise AssertionError() 88 89 write_all(length) 90 for b in data: 91 write_all(b) 92 93 errcorlvl = random.randrange(4) 94 minversion = random.randint(1, 40) 95 maxversion = random.randint(1, 40) 96 if minversion > maxversion: 97 minversion, maxversion = maxversion, minversion 98 mask = -1 99 if random.random() < 0.5: 100 mask = random.randrange(8) 101 boostecl = int(random.random() < 0.2) 102 print("mode={} len={} ecl={} minv={} maxv={} mask={} boost={}".format(mode, length, errcorlvl, minversion, maxversion, mask, boostecl), end="") 103 104 write_all(errcorlvl) 105 write_all(minversion) 106 write_all(maxversion) 107 write_all(mask) 108 write_all(boostecl) 109 flush_all() 110 111 version = read_verify() 112 print(" version={}".format(version), end="") 113 if version == -1: 114 return 115 size = version * 4 + 17 116 for _ in range(size**2): 117 read_verify() 118 119 120def write_all(val): 121 for proc in subprocs: 122 print(val, file=proc.stdin) 123 124def flush_all(): 125 for proc in subprocs: 126 proc.stdin.flush() 127 128def read_verify(): 129 val = subprocs[0].stdout.readline().rstrip("\r\n") 130 for proc in subprocs[1 : ]: 131 if proc.stdout.readline().rstrip("\r\n") != val: 132 raise ValueError("Mismatch") 133 return int(val) 134 135 136if __name__ == "__main__": 137 main() 138