├── example.png ├── brute.cpp ├── main.cpp ├── README.md ├── tc_generator.cpp └── stress_test /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhupixb/Stress-Testing-bash-script/HEAD/example.png -------------------------------------------------------------------------------- /brute.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Takes an array of integers and sort them. 7 | void solve() { 8 | int n; 9 | cin >> n; 10 | vector arr(n); 11 | 12 | int negative = 0; 13 | 14 | for (int &x: arr) { 15 | cin >> x; 16 | } 17 | 18 | sort(arr.begin(), arr.end()); 19 | for (int x: arr) { 20 | cout << x << ' '; 21 | } 22 | } 23 | 24 | signed main() { 25 | ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); 26 | solve(); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Takes an array of integers and sort them with off by 1 error. 7 | void solve() { 8 | int n; 9 | cin >> n; 10 | vector arr(n); 11 | 12 | int negative = 0; 13 | 14 | for (int &x: arr) { 15 | cin >> x; 16 | } 17 | 18 | // Notice + 1 here, we are ignoring first element of array during sort to intentionally 19 | // make the logic incorrect. 20 | sort(arr.begin() + 1, arr.end()); 21 | for (int x: arr) { 22 | cout << x << ' '; 23 | } 24 | } 25 | 26 | signed main() { 27 | ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); 28 | solve(); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Note**: A Codeforces blog can be found [here](https://codeforces.com/blog/entry/78985) on `Codeforces`. 2 | # Stress Testing bash script 3 | 4 | A simple bash script which stress tests an optimized solution & a brute 5 | force solution on randomly generated testcases, to find a possible failing testcase.
6 | 7 | ### Usage 8 | Clone this repository using git or download manually. 9 | 10 | ```bash 11 | # Clone the Repository. 12 | $ git clone https://github.com/bhupixb/Stress-Testing-bash-script 13 | 14 | # Change directory. 15 | $ cd Stress-Testing-bash-script 16 | 17 | # Give execute permission to `stress_test` script to current user. 18 | $ sudo chmod u+x stress_test 19 | 20 | # Assuming your optmized solution is in main.cpp and brute force solution 21 | # in brute.cpp, to run stress test: 22 | $ ./stress_test -b brute.cpp -m main.cpp 23 | 24 | # To print usage, use -h flag 25 | $ ./stress_test -h 26 | ``` 27 | ![Usage Screenshot](example.png "Sample Usage Screenshot") 28 | 29 | ### When to use: 30 | This is useful when your optimized solution is failing on some test case during a contest
31 | and you are unable to come up with a failing test case.
32 | In cases like this if you write a brute force solution that you are sure that will produce 33 | correct output and run these 2 solutions on random tests, it can help 34 | you find a test case which fails on your main solution. 35 | 36 | ### How it works: 37 | 1. This script takes your main solution and brute force solution cpp file as argument. 38 | 2. Compiles the code & generates executable. 39 | 3. Generates testcases(default 10) from `tc_generator.cpp` program and runs your main & brute solution 40 | against it. Then it checks for difference in output of both solutions, if it differs, the 41 | script reports the testcase, output of your main solution and output of your brute force 42 | solution to your terminal STDOUT. 43 | 4. Now you can figure out, what's wrong in your solution given a test case on which your sol 44 | fails. 45 | 46 | ### Testcase Generator: 47 | The file `tc_generator.cpp` has some boilerplate code to generate commonly 48 | required testcases like `tree`, `graph`, `array of integers` & `strings`.
49 | You can modify this file to generate test cases as per your requirements. E.g. 50 | 1) to generate an array calling: 51 | `gen_array(10, -5, 10)`; 52 | it will return an array(vector more specifically) with length 10 and elements in the range [-5, 10]. 53 | 54 | 2) to generate a tree calling: 55 | `gen_tree(10)`: 56 | will return a tree with 10 nodes. 57 | 58 | 3) to generate a simple graph calling: 59 | `gen_simple_graph(10, 12)`; 60 | will return a simple connected graph with 10 nodes and 12 edges. 61 | 62 | You can add things as you need them or use testlib which is the best if you know how to use it. 63 | -------------------------------------------------------------------------------- /tc_generator.cpp: -------------------------------------------------------------------------------- 1 | // #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | using namespace std; 26 | 27 | #define int long long 28 | #define accuracy chrono::steady_clock::now().time_since_epoch().count() 29 | #define rep(i,a,n) for (int i = a; i <= n; ++i) 30 | 31 | const int N = 1e6 + 4; 32 | 33 | int32_t permutation[N]; 34 | 35 | mt19937 rng(accuracy); 36 | 37 | int rand(int l, int r){ 38 | uniform_int_distribution ludo(l, r); return ludo(rng); 39 | } 40 | 41 | const int inf = 1LL << 31; 42 | 43 | using pii = pair; 44 | 45 | namespace generator { 46 | string gen_string(int len = 0, bool upperCase = false, int l = 1, int r = 26) { 47 | assert(len >= 0 && len <= 5e6); 48 | string str(len, (upperCase ? 'A' : 'a')); 49 | for (char &ch: str) { 50 | ch += rand(l, r) - 1; 51 | } 52 | return str; 53 | } 54 | vector gen_array(int len = 0, int minRange = 0, int maxRange = inf){ 55 | assert(len >= 0 and len <= 5e6); 56 | vector a(len); 57 | for (int &x: a) x = rand(minRange, maxRange); 58 | return a; 59 | } 60 | vector> gen_tree(int n = 0){ 61 | assert(n >= 0); 62 | vector res(n ? n - 1 : 0); 63 | // if you like to have bamboo like tree or star like tree uncomment below 8 lines 64 | /*if (rng() % 5 == 0) { // bamboo like tree 65 | for (int i = 1; i < n; ++i) res[i-1] = {i, i + 1}; 66 | return res; 67 | } 68 | if (rng() % 7 == 0) { // star tree 69 | for (int i = 2; i <= n; ++i) res[i-2] = {1, i}; 70 | return res; 71 | }*/ 72 | iota(permutation, permutation + 1 + n, 0); 73 | shuffle(permutation + 1, permutation + 1 + n, rng); 74 | for(int i = 2; i <= n; ++i){ 75 | int u = i, v = rand(1 , i-1); 76 | u = permutation[u], v = permutation[v]; 77 | res[i-2] = minmax(u, v); // u < v, just for convenience while debugging 78 | } 79 | shuffle(res.begin() , res.end() , rng); 80 | return res; 81 | } 82 | vector> simple_graph(int n = 0, int m = 0) { 83 | assert(n > 0 && m >= n); 84 | int max_edges = n * (n - 1) / 2; 85 | assert(m <= max_edges); 86 | vector res = gen_tree(n); 87 | set edge(res.begin(), res.end()); 88 | for (int i = n; i <= m; ++i) { 89 | while (true) { 90 | int u = rand(1, n), v = rand(1, n); 91 | if (u == v) continue; 92 | auto it = edge.insert(minmax(u, v)); 93 | if (it.second) break; 94 | } 95 | } 96 | res.assign(edge.begin(), edge.end()); 97 | return res; 98 | } 99 | } 100 | 101 | using namespace generator; 102 | 103 | template 104 | ostream& operator<< (ostream &other, const vector &v) { 105 | for (const T &x: v) other << x << ' '; 106 | other << '\n'; 107 | return other; 108 | } 109 | 110 | ostream& operator<< (ostream &other, const vector> &v) { 111 | for (const auto &x: v) other << x.first << ' ' << x.second << '\n'; 112 | return other; 113 | } 114 | 115 | // comment the just below line if test cases required 116 | #define SINGLE_TEST 117 | const int max_tests = 10; 118 | 119 | // complete this function according to the requirements 120 | void generate_test() { 121 | int n = rand(1, 40); 122 | cout << n << '\n'; 123 | cout << gen_array(n, 1, 20); 124 | } 125 | 126 | signed main() { 127 | srand(accuracy); 128 | int t = 1; 129 | #ifndef SINGLE_TEST 130 | t = rand(1, max_tests), cout << t << '\n'; 131 | #endif 132 | while (t--) { 133 | generate_test(); 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /stress_test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # To color the output text in different colours. 4 | green=$(tput setaf 71); 5 | red=$(tput setaf 1); 6 | blue=$(tput setaf 32); 7 | orange=$(tput setaf 178); 8 | bold=$(tput bold); 9 | reset=$(tput sgr0); 10 | 11 | CPP_VERSION="c++17" 12 | COMPILE_FLAGS="" 13 | TC_GENERATOR_FILE="tc_generator.cpp" 14 | MAX_TESTS="10" 15 | 16 | BRUTE_FILE="" 17 | MAIN_FILE="" 18 | 19 | ############################################################ 20 | # USAGE 21 | usage(){ 22 | echo " 23 | USAGE: 24 | $(basename "${0}") -b -m [ -t ] 25 | Options: 26 | -b Specify the name of cpp file having brute force solution. [required] 27 | -m Specify the name of cpp file having main solution. [required] 28 | -t No. of test cases to generate and stress test on. (optional, default 10) 29 | " 30 | } 31 | 32 | # Checks if the main, brute and generator files exists or not. 33 | function check_files_exists() { 34 | declare -a cpp_files=("$1" "$2" "$3") 35 | for file in "${cpp_files[@]}"; do 36 | # echo "${file}" 37 | if ! [[ -f "$file" ]]; then 38 | echo "File ${orange}${file}${reset} does not exist in dir $(pwd), exiting..." 39 | exit 1 40 | fi 41 | done 42 | } 43 | 44 | # Compiles a given cpp file and stores the executable in variable `executable_fname` 45 | function compile() { 46 | local cpp_file="$1" 47 | local executable_fname="$2" 48 | 49 | local start_time="$(date +%s)" 50 | g++ -std="${CPP_VERSION}" "${cpp_file}" -o "${executable_fname}" "${COMPILE_FLAGS}" || { echo "${bold}${red}Error when compiling: ${reset}${orange}${cpp_file}${reset}"; exit 1; } 51 | local end_time="$(date +%s)" 52 | 53 | echo "${green}Successfully compiled ${cpp_file}${reset} in ${orange}$((end_time - start_time))s${reset}." 54 | } 55 | 56 | function cleanup() { 57 | rm -f input1.txt generator original brute original_output.txt brute_output.txt 58 | } 59 | 60 | function tips() { 61 | echo "" 62 | echo "${blue}You might want to use ${green}https://www.diffchecker.com/diff${reset} to better visualize the difference in output :)${reset}" 63 | } 64 | 65 | function stress_test() { 66 | local diff_found=0 67 | 68 | echo "" && echo "Starting stress testing on ${orange}${MAX_TESTS}${reset} randomly generated test cases:" && echo "" 69 | 70 | for ((i=0; i<$MAX_TESTS; i++)); do 71 | # Generate test_case and save it in input1.txt 72 | ./generator > input1.txt 73 | 74 | # run original solution, take input from above generated test case i.e. from input1.txt 75 | # and save it in original_output.txt 76 | ./original < input1.txt > original_output.txt #|| {echo failed; exit 1;} 77 | 78 | # run brute force solution, take input from above generated test case i.e. from input1.txt 79 | # and save it in brute_output.txt 80 | ./brute < input1.txt > brute_output.txt 81 | 82 | # check if files original_output and brute_output 83 | # differs(we are ignoring spaces and then comparing files) 84 | if diff -F --label --side-by-side --ignore-space-change original_output.txt brute_output.txt > /dev/null; then 85 | echo "${orange}test_case #$i: ${bold}${green}passed${reset}" 86 | else 87 | echo "${orange}test_case #$i: ${bold}${red}failed${reset}" 88 | diff_found=1 89 | break 90 | fi 91 | done 92 | 93 | if [[ $diff_found -eq 1 ]] 94 | then 95 | echo "${blue}Input: ${reset}" 96 | cat input1.txt 97 | echo "" 98 | 99 | echo "${blue}Output(Main sol): ${reset}" 100 | cat original_output.txt 101 | echo ""; echo "" 102 | 103 | echo "${blue}Expected(Brute sol): ${reset}" 104 | cat brute_output.txt 105 | echo "" 106 | 107 | tips 108 | fi 109 | } 110 | 111 | function main() { 112 | # Parse args 113 | while [[ $# -gt 0 ]]; do 114 | case $1 in 115 | -b) 116 | BRUTE_FILE="$2" 117 | shift # past argument 118 | shift # past value 119 | ;; 120 | -m) 121 | MAIN_FILE="$2" 122 | shift 123 | shift 124 | ;; 125 | -h) 126 | usage 127 | exit 128 | ;; 129 | -t) 130 | MAX_TESTS="$2" 131 | re='^[0-9]+$' 132 | if ! [[ $MAX_TESTS =~ $re ]] ; then 133 | echo "error: argument -t must be a number e.g. -t 69 "; exit 1 134 | fi 135 | shift 136 | shift 137 | ;; 138 | -*|--*) 139 | echo "Unknown option $1" 140 | exit 1 141 | ;; 142 | esac 143 | done 144 | 145 | check_files_exists "${BRUTE_FILE}" "${MAIN_FILE}" "${TC_GENERATOR_FILE}" 146 | compile "${TC_GENERATOR_FILE}" "generator" 147 | compile "${MAIN_FILE}" "original" 148 | compile "${BRUTE_FILE}" "brute" 149 | 150 | stress_test 151 | # cleanup 152 | } 153 | 154 | main "$@" --------------------------------------------------------------------------------