/** * Email: bt19ex@brocku.ca * Student Number: 6920201 * Licence: GPL3.0 see https://www.gnu.org/licenses/gpl-3.0.en.html for more detail * Basic random test case generator and tester with partially formatted output. Requires terminal unicode and ANSI escape code support for full effect. * * Please use Linux to run this software, tested on Debian stable using CLion 2023.2.2 // GCC version 12.2 * This should work on any linux distro with a C++20 compiler. * CMake is optional as this file can be compiled via g++ * An compile command would be: * `g++ -O3 -o main.run -Wall -Wpedantic -Wextra -Werror main.cpp && ./main.run` * If you use CMake please make sure CMAKE_BUILD_TYPE is release otherwise the program will take a long time to run. */ #include #include #include #include #include /** * Config Variables * --------------------------------------- */ static constexpr size_t DEFAULT_TEST_COUNT = 10; // Default: 10 tests static constexpr size_t MAX_RAM_USAGE = 1ul * 1024ul * 1024ul; // Default: 1mb per test /** * Do not change anything below this line! * --------------------------------------- */ static constexpr auto maxSize = MAX_RAM_USAGE / sizeof(std::int32_t); static const auto maxChars = std::to_string(maxSize).size(); struct failPoint { size_t i, j; }; // fp has no meaning if the test doesn't fail. bool validateTest(const std::vector& in, failPoint& fp) { // empty sets are sorted. if you have a problem with this take it up with god if (in.empty()) return true; for (size_t i = 0; i < in.size(); i++) { for (size_t j = i; j < in.size(); j++) { // if any value after the current value is less, then we have failed to sort. if (in[i] > in[j]) { fp = {i, j}; return false; } } } return true; } std::vector generateRandomData() { // setup random numbers static std::random_device dev; static std::mt19937_64 engine(dev()); // don't want the int array to be too big otherwise we'll run out of ram. you can change this for your machine but im on my laptop rn // if it mattered I'd test it on my desktop with 4gb array allocations, but it doesn't std::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); std::uniform_int_distribution dist_size(0ul, maxSize); // pick a random size size_t size = dist_size(engine); // populate the array std::vector ret; for (size_t i = 0; i < size; i++) ret.push_back(dist(engine)); return ret; } template void runTest(T t, std::string name) { auto data = generateRandomData(); // little extra formatting goes a long way =3 name += "; Data Size: "; auto dataSizeStr = std::to_string(data.size()); name += dataSizeStr; // run the sort on the data t(data); // add padding for nice output :P std::string padding; for (size_t i = 0; i < maxChars - dataSizeStr.size(); i++) padding += ' '; failPoint fp{}; // input and output arrays cannot be shown due to their potential size. Instead, the software will output the failure value and locations. // sometimes they are neighbours sometimes they are far apart. it depends on the randomness if (validateTest(data, fp)) std::cout << "\033[36m[\033[32m✓\033[36m]:\033[0m Test '" << name << "'" << padding << "\t\033[32mPASSED\033[0m" << std::endl; else std::cout << "\033[36m[\033[31mX\033[36m]:\033[0m Test '" << name << "'" << padding << "\t\033[31mFAILED\t\t" << "Value '" << data[fp.i] << "' @ " << fp.i << " is greater than '" << data[fp.j] << "' @ " << fp.j << "\033[0m" << std::endl; } void brettSort(std::vector& v) { if (v.empty()) return; static std::random_device dev; static std::mt19937_64 engine(dev()); // since the STL won't fail (it's pretty good as long as you follow the rules) std::sort(v.begin(), v.end()); // we can add some errors (sometimes) that will cause our test to fail (also sometimes)! std::uniform_int_distribution dist(0, (int) v.size() - 1); std::uniform_real_distribution realDist(0.0, 1.0); int p1 = dist(engine); int p2 = dist(engine); if (!v.empty() && realDist(engine) > 0.85) v[p1] = v[p2]; } int main(int argc, char** argv) { std::cout << "You can provide a test count via ./program [test count]" << std::endl; auto testCount = DEFAULT_TEST_COUNT; if (argc > 1) testCount = std::stoi(argv[1]); std::cout << "Running " << testCount << " tests" << std::endl; // the brett sort should fail at some point for (size_t i = 0; i < testCount; i++) runTest(brettSort, "brettSort"); return 0; }