diff --git a/.gitignore b/.gitignore index 3f4df27..09a1857 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,5 @@ harbour-patience-deck /dist/screenshots/*.png /installroot/ /tools/convert -/tools/exerciser/games/ -/tools/exerciser/engine-exerciser .qmake.stash /libs/ diff --git a/tools/exerciser/.gitignore b/tools/exerciser/.gitignore new file mode 100644 index 0000000..f4b1ba9 --- /dev/null +++ b/tools/exerciser/.gitignore @@ -0,0 +1,2 @@ +/games/ +/engine-exerciser diff --git a/tools/itertest/.gitignore b/tools/itertest/.gitignore new file mode 100644 index 0000000..501a8e0 --- /dev/null +++ b/tools/itertest/.gitignore @@ -0,0 +1 @@ +/itertest diff --git a/tools/itertest/itertest.cpp b/tools/itertest/itertest.cpp new file mode 100644 index 0000000..0a5a847 --- /dev/null +++ b/tools/itertest/itertest.cpp @@ -0,0 +1,314 @@ +/* + * Tests for Patience Deck itertools + * Copyright (C) 2023 Tomi Leppänen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "itertools.h" + +#define ASSERT_TRUE(test) do { \ + if (!(test)) { \ + cout << "assertion (" << #test << ") is true failed" << endl; \ + return false; \ + } \ +} while (0) +#define ASSERT_FALSE(test) do { \ + if (test) { \ + cout << "assertion (" << #test << ") is false failed" << endl; \ + return false; \ + return false; \ + } \ +} while (0) +#define ASSERT_SAME(a, b) do { \ + if ((a) != (b)) { \ + cout << "assertion (" << #a << " == " << #b << ") failed" << endl; \ + return false; \ + } \ +} while (0) +#define SUCCESS() do { return true; } while (0) + +namespace { + using std::advance; + using std::cout; + using std::endl; + using std::function; + using std::get; + using std::mt19937; + using std::string; + using std::tuple; + using std::unitbuf; + using std::unordered_set; + using std::vector; + + const vector data { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +} // namespace + +bool test_range_simple() +{ + Range range(0, 10); + auto it1 = range.begin(); + auto it2 = data.begin(); + while (it1 != range.end() || it2 != data.end()) { + ASSERT_SAME(*it1, *it2); + ++it1, ++it2; + } + ASSERT_FALSE(it1 != range.end()); + ASSERT_TRUE(it2 == data.end()); + SUCCESS(); +} + +bool test_range_consistent_begin() +{ + Range range(0, 100); + auto first = range.begin(); + auto second = range.begin(); + ASSERT_FALSE(first != second); + ASSERT_TRUE(++first != second); + ASSERT_FALSE(first != ++second); + SUCCESS(); +} + +bool test_range_consistent_end() +{ + Range range(10, 12); + ASSERT_SAME(range.end(), range.end()); + auto it = range.begin(); + advance(it, 4); + ASSERT_SAME(it, range.end()); + SUCCESS(); +} + +bool test_range_empty() +{ + Range range(1, 1); + ASSERT_SAME(range.begin(), range.end()); + SUCCESS(); +} + +bool test_indexed_simple() +{ + vector indices { 8, 9, 2, 5, 4, 3, 6, 7, 1, 0 }; + vector values(data); + IndexedIterator::iterator> iterator(values.begin(), values.end(), indices); + auto it1 = iterator.begin(); + auto it2 = indices.begin(); + while (it1 != iterator.end() || it2 != indices.end()) { + ASSERT_SAME(*it1, *it2); + ++it1, ++it2; + } + ASSERT_FALSE(it1 != iterator.end()); + ASSERT_TRUE(it2 == indices.end()); + SUCCESS(); +} + +bool test_indexed_consistent_begin() +{ + IndexedIterator::const_iterator> iterator(data.begin(), data.end(), data); + auto first = iterator.begin(); + auto second = iterator.begin(); + ASSERT_FALSE(first != second); + ASSERT_TRUE(++first != second); + ASSERT_FALSE(first != ++second); + SUCCESS(); +} + +bool test_indexed_consistent_end() +{ + IndexedIterator::const_iterator> iterator(data.begin(), data.end(), data); + ASSERT_SAME(iterator.end(), iterator.end()); + auto it = iterator.begin(); + advance(it, 10); + ASSERT_SAME(it, iterator.end()); + SUCCESS(); +} + +bool test_indexed_empty() +{ + IndexedIterator::const_iterator> iterator(data.begin(), data.begin(), std::vector()); + ASSERT_SAME(iterator.begin(), iterator.end()); + SUCCESS(); +} + +bool test_shuffled_simple(int value, const vector &expected) +{ + vector values(data); + mt19937 generator(value); + IndexedIterator::iterator> iterator = shuffled(values, generator); + auto it1 = iterator.begin(); + auto it2 = expected.begin(); + while (it1 != iterator.end() || it2 != expected.end()) { + ASSERT_SAME(*it1, *it2); + ++it1, ++it2; + } + ASSERT_FALSE(it1 != iterator.end()); + ASSERT_TRUE(it2 == expected.end()); + SUCCESS(); +} + +bool test_shuffled_simple_0() +{ + const vector expected { 0, 2, 1, 5, 9, 8, 4, 7, 6, 3 }; + return test_shuffled_simple(0, expected); +} + +bool test_shuffled_simple_42() +{ + const vector expected { 1, 6, 7, 0, 5, 9, 8, 2, 3, 4 }; + return test_shuffled_simple(42, expected); +} + +bool test_grouped_simple() +{ + vector groups { 3, 2, 4, 1 }; + vector values(data); + GroupedIterator::iterator> iterator(values.begin(), values.end(), groups); + vector> expected { + { 0, 1, 2 }, + { 3, 4 }, + { 5, 6, 7, 8 }, + { 9 }, + }; + auto itA1 = iterator.begin(); + auto itA2 = expected.begin(); + while (itA1 != iterator.end() || itA2 != expected.end()) { + auto itB1 = (*itA1).begin(); + auto itB2 = itA2->begin(); + while (itB1 != (*itA1).end() || itB2 != itA2->end()) { + ASSERT_SAME(*itB1, *itB2); + ++itB1, ++itB2; + } + ASSERT_FALSE(itB1 != (*itA1).end()); + ASSERT_TRUE(itB2 == itA2->end()); + ++itA1, ++itA2; + } + ASSERT_FALSE(itA1 != iterator.end()); + ASSERT_TRUE(itA2 == expected.end()); + SUCCESS(); +} + +bool test_grouped_consistent_begin() +{ + const vector groups { 2, 2, 2, 2, 2 }; + GroupedIterator::const_iterator> iterator(data.begin(), data.end(), groups); + auto first = iterator.begin(); + auto second = iterator.begin(); + ASSERT_FALSE(first != second); + ASSERT_TRUE(++first != second); + ASSERT_FALSE(first != ++second); + SUCCESS(); +} + +bool test_grouped_consistent_end() +{ + const vector groups { 2, 2, 2, 2, 2 }; + GroupedIterator::const_iterator> iterator(data.begin(), data.end(), groups); + ASSERT_SAME(iterator.end(), iterator.end()); + auto it = iterator.begin(); + advance(it, 5); + ASSERT_SAME(it, iterator.end()); + SUCCESS(); +} + +bool test_grouped_empty() +{ + GroupedIterator::const_iterator> iterator(data.begin(), data.begin(), std::vector()); + ASSERT_SAME(iterator.begin(), iterator.end()); + SUCCESS(); +} + +bool test_groupedfunc_simple() +{ + unordered_set groups { 3, 5, 8 }; + vector values(data); + GroupedIterator::iterator> iterator = grouped>( + values, [&groups](int value) { return groups.find(value) != groups.end(); }); + vector> expected { + { 0, 1, 2, 3 }, + { 4, 5 }, + { 6, 7, 8 }, + { 9 }, + }; + auto itA1 = iterator.begin(); + auto itA2 = expected.begin(); + while (itA1 != iterator.end() || itA2 != expected.end()) { + auto itB1 = (*itA1).begin(); + auto itB2 = itA2->begin(); + while (itB1 != (*itA1).end() || itB2 != itA2->end()) { + ASSERT_SAME(*itB1, *itB2); + ++itB1, ++itB2; + } + ASSERT_FALSE(itB1 != (*itA1).end()); + ASSERT_TRUE(itB2 == itA2->end()); + ++itA1, ++itA2; + } + ASSERT_FALSE(itA1 != iterator.end()); + ASSERT_TRUE(itA2 == expected.end()); + SUCCESS(); +} + +bool test_groupedfunc_empty() +{ + std::vector data; + GroupedIterator::iterator> iterator = grouped(data, [](int value) { + (void)value; + return true; + }); + ASSERT_SAME(iterator.begin(), iterator.end()); + SUCCESS(); +} + +vector>> tests = { + { "range/simple", test_range_simple }, + { "range/consistent_begin", test_range_consistent_begin }, + { "range/consistent_end", test_range_consistent_end }, + { "range/empty", test_range_empty }, + { "indexed/simple", test_indexed_simple }, + { "indexed/consistent_begin", test_indexed_consistent_begin }, + { "indexed/consistent_end", test_indexed_consistent_end }, + { "indexed/empty", test_indexed_empty }, + { "shuffled/simple(0)", test_shuffled_simple_0 }, + { "shuffled/simple(42)", test_shuffled_simple_42 }, + { "grouped/simple", test_grouped_simple }, + { "grouped/consistent_begin", test_grouped_consistent_begin }, + { "grouped/consistent_end", test_grouped_consistent_end }, + { "grouped/empty", test_grouped_empty }, + { "groupedfunc/simple", test_groupedfunc_simple }, + { "groupedfunc/empty", test_groupedfunc_empty }, +}; + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + cout << unitbuf; + + uint count = 0; + for (auto test : tests) { + string name = get<0>(test); + cout << "Test '" << name << "' "; + bool success = get<1>(test)(); + if (success) + cout << "succeeded" << endl; + count += success; + } + + cout << count << "/" << tests.size() << " tests succeeded" << endl; + return tests.size() - count; +} diff --git a/tools/itertest/itertest.pro b/tools/itertest/itertest.pro new file mode 100644 index 0000000..1965664 --- /dev/null +++ b/tools/itertest/itertest.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = itertest +CONFIG -= qt + +INCLUDEPATH += ../../src/common + +SOURCES = \ + itertest.cpp \ + ../../src/common/itertools.cpp + +HEADERS += \ + ../../src/common/itertools.h