• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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