From 5faca8d4f6f079270ee9e4bbd657d627c1032b5e Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Aug 2017 20:36:12 +0300 Subject: [PATCH] Add Catch unittest framework and some tests. --- .gitignore | 4 + .gitmodules | 3 + README.md | 1 + Telegram/Patches/gyp.diff | 42 +++------ Telegram/SourceFiles/base/flat_map.h | 1 + Telegram/SourceFiles/base/flat_map_tests.cpp | 51 ++++++++++ Telegram/SourceFiles/base/flat_set.h | 2 + Telegram/SourceFiles/base/flat_set_tests.cpp | 48 ++++++++++ Telegram/SourceFiles/base/tests_main.cpp | 97 ++++++++++++++++++++ Telegram/ThirdParty/Catch | 1 + Telegram/create.bat | 33 ++++++- Telegram/gyp/Telegram.gyp | 1 + Telegram/gyp/qt.gypi | 16 ++-- Telegram/gyp/tests/common_test.gypi | 34 +++++++ Telegram/gyp/tests/list_tests.py | 79 ++++++++++++++++ Telegram/gyp/tests/tests.gyp | 77 ++++++++++++++++ Telegram/gyp/tests/tests_list.txt | 2 + 17 files changed, 453 insertions(+), 39 deletions(-) create mode 100644 Telegram/SourceFiles/base/flat_map_tests.cpp create mode 100644 Telegram/SourceFiles/base/flat_set_tests.cpp create mode 100644 Telegram/SourceFiles/base/tests_main.cpp create mode 160000 Telegram/ThirdParty/Catch create mode 100644 Telegram/gyp/tests/common_test.gypi create mode 100644 Telegram/gyp/tests/list_tests.py create mode 100644 Telegram/gyp/tests/tests.gyp create mode 100644 Telegram/gyp/tests/tests_list.txt diff --git a/.gitignore b/.gitignore index 6f6bb24089..4928acf931 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,10 @@ /Telegram/Resources/art/sprite_125x.png /Telegram/Resources/art/sprite_150x.png /Telegram/Debug/ +/Telegram/Release/ +/Telegram/tests/ +/Telegram/gyp/tests/*.test +/Telegram/out/ /Telegram/*.user *.vcxproj* *.sln diff --git a/.gitmodules b/.gitmodules index 874ea8efc7..2887410586 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "Telegram/ThirdParty/GSL"] path = Telegram/ThirdParty/GSL url = https://github.com/Microsoft/GSL.git +[submodule "Telegram/ThirdParty/Catch"] + path = Telegram/ThirdParty/Catch + url = https://github.com/philsquared/Catch diff --git a/README.md b/README.md index 4f8d88c27e..7d8685fb40 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ The source code is published under GPLv3 with OpenSSL exception, the license is * Mapbox Variant ([BSD License](https://github.com/mapbox/variant/blob/master/LICENSE)) * Open Sans font ([Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)) * Emoji alpha codes ([MIT License](https://github.com/emojione/emojione/blob/master/extras/alpha-codes/LICENSE.md)) +* Catch test framework ([Boost License](https://github.com/philsquared/Catch/blob/master/LICENSE.txt)) ## Build instructions diff --git a/Telegram/Patches/gyp.diff b/Telegram/Patches/gyp.diff index 13ddd9b7d2..134ae28c04 100644 --- a/Telegram/Patches/gyp.diff +++ b/Telegram/Patches/gyp.diff @@ -1,33 +1,5 @@ -diff --git a/pylib/gyp/generator/cmake.py b/pylib/gyp/generator/cmake.py -index a2b9629..68d7020 100644 ---- a/pylib/gyp/generator/cmake.py -+++ b/pylib/gyp/generator/cmake.py -@@ -1070,6 +1070,23 @@ def WriteTarget(namer, qualified_target, target_dicts, build_dir, config_to_use, - - output.write(')\n') - -+ # Precompile header -+ precompiled_header = config.get('cmake_precompiled_header', '') -+ if precompiled_header: -+ precompiled_header_script = config.get('cmake_precompiled_header_script', '') -+ if not precompiled_header_script: -+ print ('ERROR: cmake_precompiled_header requires cmake_precompiled_header_script') -+ cmake_precompiled_header = NormjoinPath(path_from_cmakelists_to_gyp, precompiled_header) -+ cmake_precompiled_header_script = NormjoinPathForceCMakeSource(path_from_cmakelists_to_gyp, precompiled_header_script) -+ output.write('include(') -+ output.write(cmake_precompiled_header_script) -+ output.write(')\n') -+ output.write('add_precompiled_header(') -+ output.write(cmake_target_name) -+ output.write(' ') -+ output.write(cmake_precompiled_header) -+ output.write(')\n') -+ - UnsetVariable(output, 'TOOLSET') - UnsetVariable(output, 'TARGET') - diff --git a/pylib/gyp/generator/xcode.py b/pylib/gyp/generator/xcode.py -index db99d6a..f8398cc 100644 +index 0e3fb93..4c824ec 100644 --- a/pylib/gyp/generator/xcode.py +++ b/pylib/gyp/generator/xcode.py @@ -72,6 +72,10 @@ generator_additional_non_configuration_keys = [ @@ -41,7 +13,7 @@ index db99d6a..f8398cc 100644 'mac_bundle', 'mac_bundle_resources', 'mac_framework_headers', -@@ -772,6 +776,26 @@ def GenerateOutput(target_list, target_dicts, data, params): +@@ -761,6 +765,26 @@ def GenerateOutput(target_list, target_dicts, data, params): xcode_targets[qualified_target] = xct xcode_target_to_target_dict[xct] = spec @@ -68,3 +40,13 @@ index db99d6a..f8398cc 100644 spec_actions = spec.get('actions', []) spec_rules = spec.get('rules', []) +@@ -1130,7 +1154,8 @@ exit 1 + groups = [x for x in groups if not x.endswith('_excluded')] + for group in groups: + for item in rule.get(group, []): +- pbxp.AddOrGetFileInRootGroup(item) ++ concrete_item = ExpandXcodeVariables(item, rule_input_dict) ++ pbxp.AddOrGetFileInRootGroup(concrete_item) + + # Add "sources". + for source in spec.get('sources', []): diff --git a/Telegram/SourceFiles/base/flat_map.h b/Telegram/SourceFiles/base/flat_map.h index 6d4327726c..d1dc28fd31 100644 --- a/Telegram/SourceFiles/base/flat_map.h +++ b/Telegram/SourceFiles/base/flat_map.h @@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include #include "base/optional.h" namespace base { diff --git a/Telegram/SourceFiles/base/flat_map_tests.cpp b/Telegram/SourceFiles/base/flat_map_tests.cpp new file mode 100644 index 0000000000..88a83a2eea --- /dev/null +++ b/Telegram/SourceFiles/base/flat_map_tests.cpp @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop 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, either version 3 of the License, or +(at your option) any later version. + +It 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "catch.hpp" + +#include "base/flat_map.h" +#include + +using namespace std; + +TEST_CASE("flat_maps should keep items sorted by key", "[flat_map]") { + base::flat_map v; + v.emplace(0, "a"); + v.emplace(5, "b"); + v.emplace(4, "d"); + v.emplace(2, "e"); + + auto checkSorted = [&] { + auto prev = v.begin(); + REQUIRE(prev != v.end()); + for (auto i = prev + 1; i != v.end(); prev = i, ++i) { + REQUIRE(prev->first < i->first); + } + }; + REQUIRE(v.size() == 4); + checkSorted(); + + SECTION("adding item puts it in the right position") { + v.emplace(3, "c"); + REQUIRE(v.size() == 5); + REQUIRE(v.find(3) != v.end()); + checkSorted(); + } +} diff --git a/Telegram/SourceFiles/base/flat_set.h b/Telegram/SourceFiles/base/flat_set.h index be75586b99..7e578d3347 100644 --- a/Telegram/SourceFiles/base/flat_set.h +++ b/Telegram/SourceFiles/base/flat_set.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include + namespace base { template diff --git a/Telegram/SourceFiles/base/flat_set_tests.cpp b/Telegram/SourceFiles/base/flat_set_tests.cpp new file mode 100644 index 0000000000..675b0bdafe --- /dev/null +++ b/Telegram/SourceFiles/base/flat_set_tests.cpp @@ -0,0 +1,48 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop 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, either version 3 of the License, or +(at your option) any later version. + +It 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "catch.hpp" + +#include "base/flat_set.h" + +TEST_CASE("flat_sets should keep items sorted", "[flat_set]") { + base::flat_set v; + v.insert(0); + v.insert(5); + v.insert(4); + v.insert(2); + + auto checkSorted = [&] { + auto prev = v.begin(); + REQUIRE(prev != v.end()); + for (auto i = prev + 1; i != v.end(); prev = i, ++i) { + REQUIRE(*prev < *i); + } + }; + REQUIRE(v.size() == 4); + checkSorted(); + + SECTION("adding item puts it in the right position") { + v.insert(3); + REQUIRE(v.size() == 5); + REQUIRE(v.find(3) != v.end()); + checkSorted(); + } +} diff --git a/Telegram/SourceFiles/base/tests_main.cpp b/Telegram/SourceFiles/base/tests_main.cpp new file mode 100644 index 0000000000..0696e477ea --- /dev/null +++ b/Telegram/SourceFiles/base/tests_main.cpp @@ -0,0 +1,97 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop 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, either version 3 of the License, or +(at your option) any later version. + +It 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#define CATCH_CONFIG_RUNNER +#include "catch.hpp" +#include "reporters/catch_reporter_compact.hpp" +#include + +namespace Catch { + + struct MinimalReporter : CompactReporter { + MinimalReporter( ReporterConfig const& _config ) + : CompactReporter( _config ) + {} + + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( _testRunStats.totals ); + } + + private: + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll( std::size_t count ) const { + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; + } + + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + } + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : std::string(); + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << '.'; + } + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; + } + else { + } + } + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "minimal", MinimalReporter ) + +} // end namespace Catch + +int main(int argc, const char *argv[]) { + const char *catch_argv[] = { argv[0], "-r", "minimal" }; + constexpr auto catch_argc = sizeof(catch_argv) / sizeof(catch_argv[0]); + auto result = Catch::Session().run(catch_argc, catch_argv); + if (result == 0) { + for (auto i = 0; i != argc; ++i) { + if (argv[i] == QString("--touch") && i + 1 != argc) { + QFile(QFile::decodeName(argv[++i])).open(QIODevice::WriteOnly); + } + } + } + return (result < 0xff ? result : 0xff); +} + diff --git a/Telegram/ThirdParty/Catch b/Telegram/ThirdParty/Catch new file mode 160000 index 0000000000..5ca44b6872 --- /dev/null +++ b/Telegram/ThirdParty/Catch @@ -0,0 +1 @@ +Subproject commit 5ca44b68721833ae3731802ed99af67c6f38a53a diff --git a/Telegram/create.bat b/Telegram/create.bat index 9dcf9ac21d..570e75f2b4 100644 --- a/Telegram/create.bat +++ b/Telegram/create.bat @@ -4,7 +4,10 @@ set "FullScriptPath=%~dp0" set "FullExecPath=%cd%" set "Command=%1" -if "%Command%" == "header" ( +if "%Command%" == "test" ( + call :write_test %2 + exit /b %errorlevel% +) else if "%Command%" == "header" ( call :write_header %2 exit /b %errorlevel% ) else if "%Command%" == "source" ( @@ -87,6 +90,34 @@ exit /b %errorlevel% exit /b ) +:write_test +( + set "CommandPath=%1" + set "CommandPathUnix=!CommandPath:\=/!" + set "CommandPathWin=!CommandPath:/=\!" + + if "!CommandPathUnix!" == "" ( + echo Provide source path. + exit /b 1 + ) else if exist "SourceFiles\!CommandPathWin!.cpp" ( + echo This source already exists. + exit /b 1 + ) + echo Generating test !CommandPathUnix!.cpp.. + mkdir "SourceFiles\!CommandPathWin!.cpp" + rmdir "SourceFiles\!CommandPathWin!.cpp" + + call :write_comment !CommandPathWin!.cpp + set "quote=""" + set "quote=!quote:~0,1!" + set "source1=#include !quote!catch.hpp!quote!" + ( + echo !source1! + echo. + )>> "SourceFiles\!CommandPathWin!.cpp" + exit /b +) + :write_comment ( set "Path=%1" diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 9d92b817d1..0d1f38aa75 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -79,6 +79,7 @@ 'codegen.gyp:codegen_lang', 'codegen.gyp:codegen_numbers', 'codegen.gyp:codegen_style', + 'tests/tests.gyp:tests', 'utils.gyp:Updater', '../ThirdParty/libtgvoip/libtgvoip.gyp:libtgvoip', ], diff --git a/Telegram/gyp/qt.gypi b/Telegram/gyp/qt.gypi index 81c13a1822..6032d06647 100644 --- a/Telegram/gyp/qt.gypi +++ b/Telegram/gyp/qt.gypi @@ -225,14 +225,14 @@ '<(linux_lib_ssl)', '<(linux_lib_crypto)', '