├── README.md ├── checker_Mac ├── checker_linux └── tester.py /README.md: -------------------------------------------------------------------------------- 1 | # Push_Swap Sorting Tester | 42 2 | 3 | ## What is the purpose of this script? 4 | 5 | This little Python script allows you to know if **your program sorts correctly smalls and large stacks**. 6 | 7 | **All features:** 8 | 9 | - it checks a lot of different common parsing mistakes. 10 | - for all the small stacks (3, 4 and 5 numbers), it checks if your program sorts correctly **all possible combinations of numbers**. 11 | - for big stacks (100 and 500 numbers), it generates **10 random stacks** and checks if your program sorts them correctly. 12 | - it displays how many moves your program used to sort the stack, as well as the average, max and min moves. 13 | 14 | ## How to install the script? 15 | - ***For Mac***: 16 | 17 | ``` 18 | git clone git@github.com:julien-ctx/push-swap-tester.git && mv push-swap-tester/tester.py . && mv push-swap-tester/checker_Mac . && rm -rf push-swap-tester && chmod 777 checker_Mac && make && python3 tester.py 19 | ``` 20 | 21 | - ***For Linux***: 22 | 23 | ``` 24 | git clone git@github.com:julien-ctx/push-swap-tester.git && mv push-swap-tester/tester.py . && mv push-swap-tester/checker_linux . && rm -rf push-swap-tester && chmod 777 checker_linux && sed -i -- 's/checker_Mac/checker_linux/g' tester.py && make && python3 tester.py 25 | ``` 26 | 27 | ## How to use the script? 28 | 29 | Atfer installation, all you have to do is execute the Python script at the root of your repository using: `python3 tester.py` 30 | 31 | If the script detects errors (too much moves or KO), a **trace.txt** file is created to help you understand your mistakes. 32 | -------------------------------------------------------------------------------- /checker_Mac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julien-ctx/push-swap-tester/124a2d85220063bd58d116d8d250a2b9fc2fd681/checker_Mac -------------------------------------------------------------------------------- /checker_linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julien-ctx/push-swap-tester/124a2d85220063bd58d116d8d250a2b9fc2fd681/checker_linux -------------------------------------------------------------------------------- /tester.py: -------------------------------------------------------------------------------- 1 | import os, math, subprocess, random, sys 2 | 3 | class color: 4 | PURPLE = '\033[95m' 5 | CYAN = '\033[96;1m' 6 | DARKCYAN = '\033[36m' 7 | BLUE = '\033[94;1m' 8 | GREEN = '\033[92;1m' 9 | YELLOW = '\033[93;1m' 10 | RED = '\033[91;1m' 11 | BOLD = '\033[1m' 12 | UNDERLINE = '\033[4m' 13 | END = '\033[0m' 14 | 15 | def swap(elmt, i, j): 16 | elmt[i], elmt[j] = elmt[j], elmt[i] 17 | 18 | def generate_permutations(a): 19 | n = len(a) 20 | c = [0] * n 21 | A = list(a) 22 | yield A 23 | i = 1 24 | while i < n: 25 | if c[i] < i: 26 | if i % 2 == 0: 27 | temp = A[0] 28 | A[0] = A[i] 29 | A[i] = temp 30 | else: 31 | temp = A[c[i]] 32 | A[c[i]] = A[i] 33 | A[i] = temp 34 | yield A 35 | c[i] += 1 36 | i = 1 37 | else: 38 | c[i] = 0 39 | i += 1 40 | 41 | def permutations(elements): 42 | yield from generate_permutations(elements) 43 | 44 | def calculate_combination(nb): 45 | elmt = [x for x in range(1, nb + 1)] 46 | max_moves = 0 47 | all_moves = [] 48 | all_res = [] 49 | limit = 3 if nb == 3 else 12 if nb == 5 else math.inf 50 | for perm in permutations(elmt): 51 | cmd = ' '.join([str(x) for x in perm]) 52 | 53 | moves_cmd = "./push_swap " + cmd + " | wc -l" 54 | moves_output = int(os.popen(moves_cmd).read()) 55 | if (moves_output > max_moves): 56 | max_moves = moves_output 57 | all_moves.append((int(moves_output), str(cmd))) 58 | 59 | res_cmd = "./push_swap " + cmd + " | ./checker_Mac " + cmd 60 | res_output = os.popen(res_cmd).read() 61 | all_res.append((str(res_output), str(cmd))) 62 | 63 | # Printing max moves and results 64 | print(color.BOLD + "👉 MAX = " + str(max_moves) + color.END, end = '') 65 | if (max_moves > limit): 66 | print(color.RED + "\t❌ Too Much Moves" + color.END) 67 | else: 68 | print(color.GREEN + "\t✅ OK" + color.END) 69 | state = True 70 | for x in all_res: 71 | if x[0] == "KO\n": 72 | print(color.BOLD + "👉 RESULTS\t" + "❌ " + color.RED + "KO" + color.END) 73 | state = False 74 | break 75 | if state: 76 | print(color.BOLD + "👉 RESULTS\t" + "✅ " + color.GREEN + "OK" + color.END) 77 | 78 | # Printing trace 79 | if limit == 3: 80 | all_too_much = [("./push_swap " + item[1] + " | wc -l") for item in all_moves if item[0] > 3] 81 | elif limit == 12: 82 | all_too_much = [("./push_swap " + item[1] + " | wc -l") for item in all_moves if item[0] > 12] 83 | else: 84 | all_too_much = None 85 | all_wrong_values = [("./push_swap " + item[1] + " | ./checker_Mac " + item[1]) for item in all_res if item[0] == "KO\n"] 86 | if all_too_much: 87 | f = open('trace.txt', 'a+') 88 | if limit == 3: 89 | f.write("** TOO MUCH MOVES FOR THESE TESTS (MAX = 3) **\n\n") 90 | elif limit == 12: 91 | f.write("** TOO MUCH MOVES FOR THESE TESTS (MAX = 12) **\n\n") 92 | for item in all_too_much: 93 | f.write(item + "\n\n") 94 | f.write("\n") 95 | f.close() 96 | if all_wrong_values: 97 | f = open('trace.txt', 'a+') 98 | f.write("** KO ON THESE TESTS **\n\n") 99 | for item in all_wrong_values: 100 | f.write(item + "\n\n") 101 | f.close() 102 | 103 | def mean(lst): 104 | return round(sum(lst) / len(lst)) 105 | 106 | def calculate(nb): 107 | moves = [] 108 | wrong_values = [] 109 | for x in range(0, 10): 110 | nbs = ' '.join([str(random.randint(-2147483648, 2147483647)) for x in range(0, nb)]) 111 | 112 | res_cmd = "./push_swap " + nbs + " | ./checker_Mac " + nbs 113 | res_output = os.popen(res_cmd).read() 114 | if res_output == "KO\n": 115 | print(color.BOLD + f"TEST {x + 1} 👉 \t❌ " + color.RED + "KO" + color.END) 116 | wrong_values.append(("./push_swap " + nbs + " | ./checker_Mac " + nbs)) 117 | else: 118 | print(color.BOLD + f"TEST {x + 1} 👉 \t✅ " + color.GREEN + "OK" + color.END) 119 | 120 | moves_cmd = "./push_swap " + nbs + " | wc -l" 121 | moves_output = os.popen(moves_cmd).read() 122 | moves.append(int(moves_output)) 123 | 124 | if wrong_values: 125 | f = open('trace.txt', 'a+') 126 | f.write("** KO ON THESE TESTS **\n\n") 127 | for item in wrong_values: 128 | f.write(item + "\n\n") 129 | f.close() 130 | else: 131 | print(color.BOLD + "\nMEAN 👉\t" + str(round(mean(moves))) + " moves" + color.END, end = '') 132 | print(color.RED + "\nMAX 👉\t" + str(max(moves)) + " moves" + color.END, end = '') 133 | print(color.GREEN + "\nMIN 👉\t" + str(min(moves)) + " moves" + color.END) 134 | 135 | def parsing_error(): 136 | print(color.RED + "❌ KO" + color.END) 137 | with open("trace.txt", "w") as f: 138 | f.write("** PARSING ERRORS DETECTED, PLEASE REFER TO THE TESTS **\n\n") 139 | 140 | def parsing_check(): 141 | res = os.popen("./push_swap").read() 142 | ret_code = subprocess.run(["./push_swap"]).returncode 143 | print(color.BOLD + "./push_swap\t\t\t\t" + color.END, end = '') 144 | if res or ret_code == -11: 145 | parsing_error() 146 | else: 147 | print(color.GREEN + "✅ OK" + color.END) 148 | 149 | print(color.BOLD + './push_swap ""\t\t\t\t' + color.END, end = '') 150 | res = subprocess.run(["./push_swap", ""], stderr=subprocess.PIPE, stdout=subprocess.PIPE) 151 | ret_code = res.returncode 152 | if ret_code != -11 and ("Error" in str(res.stderr) or str(res.stdout) == "b''"): 153 | print(color.GREEN + "✅ OK" + color.END) 154 | elif "Error" in str(res.stdout) and ret_code != -11: 155 | print(color.YELLOW + "❗️ Errors must be displayed on stderr" + color.END) 156 | else: 157 | parsing_error() 158 | 159 | res = subprocess.run(['./push_swap', '3 2 -'], stderr=subprocess.PIPE, stdout=subprocess.PIPE) 160 | print(color.BOLD + './push_swap "3 2 -"\t\t\t' + color.END, end = '') 161 | if "Error" in str(res.stdout): 162 | print(color.YELLOW + "❗️ Errors must be displayed on stderr" + color.END) 163 | elif "Error" in str(res.stderr): 164 | print(color.GREEN + "✅ OK" + color.END) 165 | else: 166 | parsing_error() 167 | 168 | res = subprocess.run(['./push_swap', '3', '+', '1'], stderr=subprocess.PIPE, stdout=subprocess.PIPE) 169 | print(color.BOLD + './push_swap 3 + 1\t\t\t' + color.END, end = '') 170 | if "Error" in str(res.stdout): 171 | print(color.YELLOW + "❗️ Errors must be displayed on stderr" + color.END) 172 | elif "Error" in str(res.stderr): 173 | print(color.GREEN + "✅ OK" + color.END) 174 | else: 175 | parsing_error() 176 | 177 | res = os.popen('./push_swap 1 2 3').read() 178 | print(color.BOLD + './push_swap 1 2 3\t\t\t' + color.END, end = '') 179 | if res: 180 | parsing_error() 181 | else: 182 | print(color.GREEN + "✅ OK" + color.END) 183 | 184 | res = subprocess.run(['./push_swap', '3 2 1a'], stderr=subprocess.PIPE, stdout=subprocess.PIPE) 185 | print(color.BOLD + './push_swap "3 2 1a"\t\t\t' + color.END, end = '') 186 | if "Error" in str(res.stdout): 187 | print(color.YELLOW + "❗️ Errors must be displayed on stderr" + color.END) 188 | elif "Error" in str(res.stderr): 189 | print(color.GREEN + "✅ OK" + color.END) 190 | else: 191 | parsing_error() 192 | 193 | res = subprocess.run(['./push_swap', '0 3 0'], stderr=subprocess.PIPE, stdout=subprocess.PIPE) 194 | print(color.BOLD + './push_swap "0 3 0"\t\t\t' + color.END, end = '') 195 | if "Error" in str(res.stdout): 196 | print(color.YELLOW + "❗️ Errors must be displayed on stderr" + color.END) 197 | elif "Error" in str(res.stderr): 198 | print(color.GREEN + "✅ OK" + color.END) 199 | else: 200 | parsing_error() 201 | 202 | res = os.popen('./push_swap "2147483647 -2147483648"').read() 203 | print(color.BOLD + './push_swap "2147483647 -2147483648"\t' + color.END, end = '') 204 | if res != "sa\n" and res != "ra\n" and res != "rra\n": 205 | parsing_error() 206 | else: 207 | print(color.GREEN + "✅ OK" + color.END) 208 | 209 | if __name__ == "__main__": 210 | if os.path.exists("trace.txt"): 211 | os.system("rm -rf trace.txt") 212 | if not os.path.exists("push_swap"): 213 | os.system("make") 214 | if not os.path.exists("checker_Mac"): 215 | print(color.RED + "\nError: the tester needs checker_Mac at the root of the repository!\n" + color.END) 216 | exit(1) 217 | 218 | print(color.CYAN + "\n** Welcome to push_swap tester by jcauchet **" + color.END) 219 | 220 | print(color.BLUE + "\n>> Testing parsing <<" + color.END) 221 | parsing_check() 222 | 223 | for i in range(3, 6): 224 | print(color.BLUE + f"\n>> Testing for all combinations of {i} numbers <<" + color.END) 225 | calculate_combination(i) 226 | 227 | print(color.BLUE + f"\n>> Testing 10 times for 100 numbers <<" + color.END) 228 | calculate(100) 229 | 230 | print(color.BLUE + f"\n>> Testing 10 times for 500 numbers <<" + color.END) 231 | calculate(500) 232 | 233 | if os.path.exists("trace.txt"): 234 | print(color.RED + "\n-> You can find details of failed tests in trace.txt file <-" + color.END) 235 | else: 236 | print(color.GREEN + "\n-> Congratulations! all the tests are OK! <-" + color.END) 237 | print() 238 | --------------------------------------------------------------------------------