mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +01:00 
			
		
		
		
	Move tests from projects/ to tests/
This commit is contained in:
		
							
								
								
									
										516
									
								
								tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										516
									
								
								tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,516 @@ | ||||
| /* | ||||
|  *  Created by Phil on 13/5/2013. | ||||
|  *  Copyright 2014 Two Blue Cubes Ltd. All rights reserved. | ||||
|  * | ||||
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
| #include <catch2/catch_test_spec_parser.h> | ||||
| #include <catch2/catch_test_case_info.h> | ||||
| #include <catch2/catch_config.hpp> | ||||
| #include <catch2/catch_commandline.h> | ||||
|  | ||||
| #ifdef __clang__ | ||||
| #   pragma clang diagnostic ignored "-Wc++98-compat" | ||||
| #endif | ||||
|  | ||||
| namespace { | ||||
|     auto fakeTestCase(const char* name, const char* desc = "") { return Catch::makeTestCaseInfo("", { name, desc }, CATCH_INTERNAL_LINEINFO); } | ||||
| } | ||||
|  | ||||
| TEST_CASE( "Parse test names and tags" ) { | ||||
|  | ||||
|     using Catch::parseTestSpec; | ||||
|     using Catch::TestSpec; | ||||
|  | ||||
|     auto tcA = fakeTestCase( "a" ); | ||||
|     auto tcB = fakeTestCase( "b", "[one][x]" ); | ||||
|     auto tcC = fakeTestCase( "longer name with spaces", "[two][three][.][x]" ); | ||||
|     auto tcD = fakeTestCase( "zlonger name with spacesz" ); | ||||
|  | ||||
|     SECTION( "Empty test spec should have no filters" ) { | ||||
|         TestSpec spec; | ||||
|         CHECK( spec.hasFilters() == false ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Test spec from empty string should have no filters" ) { | ||||
|         TestSpec spec = parseTestSpec( "" ); | ||||
|         CHECK( spec.hasFilters() == false ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Test spec from just a comma should have no filters" ) { | ||||
|         TestSpec spec = parseTestSpec( "," ); | ||||
|         CHECK( spec.hasFilters() == false ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Test spec from name should have one filter" ) { | ||||
|         TestSpec spec = parseTestSpec( "b" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Test spec from quoted name should have one filter" ) { | ||||
|         TestSpec spec = parseTestSpec( "\"b\"" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Test spec from name should have one filter" ) { | ||||
|         TestSpec spec = parseTestSpec( "b" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Wildcard at the start" ) { | ||||
|         TestSpec spec = parseTestSpec( "*spaces" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|         CHECK( parseTestSpec( "*a" ).matches( *tcA ) == true ); | ||||
|     } | ||||
|     SECTION( "Wildcard at the end" ) { | ||||
|         TestSpec spec = parseTestSpec( "long*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|         CHECK( parseTestSpec( "a*" ).matches( *tcA ) == true ); | ||||
|     } | ||||
|     SECTION( "Wildcard at both ends" ) { | ||||
|         TestSpec spec = parseTestSpec( "*name*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == true ); | ||||
|         CHECK( parseTestSpec( "*a*" ).matches( *tcA ) == true ); | ||||
|     } | ||||
|     SECTION( "Redundant wildcard at the start" ) { | ||||
|         TestSpec spec = parseTestSpec( "*a" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|     } | ||||
|     SECTION( "Redundant wildcard at the end" ) { | ||||
|         TestSpec spec = parseTestSpec( "a*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|     } | ||||
|     SECTION( "Redundant wildcard at both ends" ) { | ||||
|         TestSpec spec = parseTestSpec( "*a*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|     } | ||||
|     SECTION( "Wildcard at both ends, redundant at start" ) { | ||||
|         TestSpec spec = parseTestSpec( "*longer*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == true ); | ||||
|     } | ||||
|     SECTION( "Just wildcard" ) { | ||||
|         TestSpec spec = parseTestSpec( "*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == true ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Single tag" ) { | ||||
|         TestSpec spec = parseTestSpec( "[one]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|     } | ||||
|     SECTION( "Single tag, two matches" ) { | ||||
|         TestSpec spec = parseTestSpec( "[x]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|     } | ||||
|     SECTION( "Two tags" ) { | ||||
|         TestSpec spec = parseTestSpec( "[two][x]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|     } | ||||
|     SECTION( "Two tags, spare separated" ) { | ||||
|         TestSpec spec = parseTestSpec( "[two] [x]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|     } | ||||
|     SECTION( "Wildcarded name and tag" ) { | ||||
|         TestSpec spec = parseTestSpec( "*name*[x]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|     } | ||||
|     SECTION( "Single tag exclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "~[one]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|     } | ||||
|     SECTION( "One tag exclusion and one tag inclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "~[two][x]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|     } | ||||
|     SECTION( "One tag exclusion and one wldcarded name inclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "~[two]*name*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|         CHECK( spec.matches( *tcD ) == true ); | ||||
|     } | ||||
|     SECTION( "One tag exclusion, using exclude:, and one wldcarded name inclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "exclude:[two]*name*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|         CHECK( spec.matches( *tcD ) == true ); | ||||
|     } | ||||
|     SECTION( "name exclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "~b" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|         CHECK( spec.matches( *tcD ) == true ); | ||||
|     } | ||||
|     SECTION( "wildcarded name exclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "~*name*" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|     } | ||||
|     SECTION( "wildcarded name exclusion with tag inclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "~*name*,[three]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|     } | ||||
|     SECTION( "wildcarded name exclusion, using exclude:, with tag inclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "exclude:*name*,[three]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == true ); | ||||
|         CHECK( spec.matches( *tcB ) == true ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|     } | ||||
|     SECTION( "two wildcarded names" ) { | ||||
|         TestSpec spec = parseTestSpec( "\"longer*\"\"*spaces\"" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == true ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|     } | ||||
|     SECTION( "empty tag" ) { | ||||
|         TestSpec spec = parseTestSpec( "[]" ); | ||||
|         CHECK( spec.hasFilters() == false ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|     } | ||||
|     SECTION( "empty quoted name" ) { | ||||
|         TestSpec spec = parseTestSpec( "\"\"" ); | ||||
|         CHECK( spec.hasFilters() == false ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|         CHECK( spec.matches( *tcD ) == false ); | ||||
|     } | ||||
|     SECTION( "quoted string followed by tag exclusion" ) { | ||||
|         TestSpec spec = parseTestSpec( "\"*name*\"~[.]" ); | ||||
|         CHECK( spec.hasFilters() == true ); | ||||
|         CHECK( spec.matches( *tcA ) == false ); | ||||
|         CHECK( spec.matches( *tcB ) == false ); | ||||
|         CHECK( spec.matches( *tcC ) == false ); | ||||
|         CHECK( spec.matches( *tcD ) == true ); | ||||
|     } | ||||
|     SECTION( "Leading and trailing spaces in test spec" ) { | ||||
|         TestSpec spec = parseTestSpec( "\"  aardvark \"" ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "  aardvark " ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "  aardvark" ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( " aardvark " ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "aardvark " ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "aardvark" ) ) ); | ||||
|  | ||||
|     } | ||||
|     SECTION( "Leading and trailing spaces in test name" ) { | ||||
|         TestSpec spec = parseTestSpec( "aardvark" ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "  aardvark " ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "  aardvark" ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( " aardvark " ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "aardvark " ) ) ); | ||||
|         CHECK( spec.matches( *fakeTestCase( "aardvark" ) ) ); | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE( "Process can be configured on command line", "[config][command-line]" ) { | ||||
|  | ||||
| #ifndef CATCH_CONFIG_DISABLE_MATCHERS | ||||
|     using namespace Catch::Matchers; | ||||
| #endif | ||||
|  | ||||
|     Catch::ConfigData config; | ||||
|     auto cli = Catch::makeCommandLineParser(config); | ||||
|  | ||||
|     SECTION("empty args don't cause a crash") { | ||||
|         auto result = cli.parse({""}); | ||||
|         CHECK(result); | ||||
|         CHECK(config.processName == ""); | ||||
|     } | ||||
|  | ||||
|     SECTION("default - no arguments") { | ||||
|         auto result = cli.parse({"test"}); | ||||
|         CHECK(result); | ||||
|         CHECK(config.processName == "test"); | ||||
|         CHECK(config.shouldDebugBreak == false); | ||||
|         CHECK(config.abortAfter == -1); | ||||
|         CHECK(config.noThrow == false); | ||||
|         CHECK(config.reporterName == "console"); | ||||
|  | ||||
|         Catch::Config cfg(config); | ||||
|         CHECK_FALSE(cfg.hasTestFilters()); | ||||
|     } | ||||
|  | ||||
|     SECTION("test lists") { | ||||
|         SECTION("Specify one test case using") { | ||||
|             auto result = cli.parse({"test", "test1"}); | ||||
|             CHECK(result); | ||||
|  | ||||
|             Catch::Config cfg(config); | ||||
|             REQUIRE(cfg.hasTestFilters()); | ||||
|             REQUIRE(cfg.testSpec().matches(*fakeTestCase("notIncluded")) == false); | ||||
|             REQUIRE(cfg.testSpec().matches(*fakeTestCase("test1"))); | ||||
|         } | ||||
|         SECTION("Specify one test case exclusion using exclude:") { | ||||
|             auto result = cli.parse({"test", "exclude:test1"}); | ||||
|             CHECK(result); | ||||
|  | ||||
|             Catch::Config cfg(config); | ||||
|             REQUIRE(cfg.hasTestFilters()); | ||||
|             REQUIRE(cfg.testSpec().matches(*fakeTestCase("test1")) == false); | ||||
|             REQUIRE(cfg.testSpec().matches(*fakeTestCase("alwaysIncluded"))); | ||||
|         } | ||||
|  | ||||
|         SECTION("Specify one test case exclusion using ~") { | ||||
|             auto result = cli.parse({"test", "~test1"}); | ||||
|             CHECK(result); | ||||
|  | ||||
|             Catch::Config cfg(config); | ||||
|             REQUIRE(cfg.hasTestFilters()); | ||||
|             REQUIRE(cfg.testSpec().matches(*fakeTestCase("test1")) == false); | ||||
|             REQUIRE(cfg.testSpec().matches(*fakeTestCase("alwaysIncluded"))); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     SECTION("reporter") { | ||||
|         SECTION("-r/console") { | ||||
|             CHECK(cli.parse({"test", "-r", "console"})); | ||||
|  | ||||
|             REQUIRE(config.reporterName == "console"); | ||||
|         } | ||||
|         SECTION("-r/xml") { | ||||
|             CHECK(cli.parse({"test", "-r", "xml"})); | ||||
|  | ||||
|             REQUIRE(config.reporterName == "xml"); | ||||
|         } | ||||
|         SECTION("--reporter/junit") { | ||||
|             CHECK(cli.parse({"test", "--reporter", "junit"})); | ||||
|  | ||||
|             REQUIRE(config.reporterName == "junit"); | ||||
|         } | ||||
|         SECTION("Only one reporter is accepted") { | ||||
|             REQUIRE_FALSE(cli.parse({ "test", "-r", "xml", "-r", "junit" })); | ||||
|         } | ||||
|         SECTION("must match one of the available ones") { | ||||
|             auto result = cli.parse({"test", "--reporter", "unsupported"}); | ||||
|             CHECK(!result); | ||||
|  | ||||
| #ifndef CATCH_CONFIG_DISABLE_MATCHERS | ||||
|             REQUIRE_THAT(result.errorMessage(), Contains("Unrecognized reporter")); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("debugger") { | ||||
|         SECTION("-b") { | ||||
|             CHECK(cli.parse({"test", "-b"})); | ||||
|  | ||||
|             REQUIRE(config.shouldDebugBreak == true); | ||||
|         } | ||||
|         SECTION("--break") { | ||||
|             CHECK(cli.parse({"test", "--break"})); | ||||
|  | ||||
|             REQUIRE(config.shouldDebugBreak); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     SECTION("abort") { | ||||
|         SECTION("-a aborts after first failure") { | ||||
|             CHECK(cli.parse({"test", "-a"})); | ||||
|  | ||||
|             REQUIRE(config.abortAfter == 1); | ||||
|         } | ||||
|         SECTION("-x 2 aborts after two failures") { | ||||
|             CHECK(cli.parse({"test", "-x", "2"})); | ||||
|  | ||||
|             REQUIRE(config.abortAfter == 2); | ||||
|         } | ||||
|         SECTION("-x must be numeric") { | ||||
|             auto result = cli.parse({"test", "-x", "oops"}); | ||||
|             CHECK(!result); | ||||
|  | ||||
| #ifndef CATCH_CONFIG_DISABLE_MATCHERS | ||||
|             REQUIRE_THAT(result.errorMessage(), Contains("convert") && Contains("oops")); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("nothrow") { | ||||
|         SECTION("-e") { | ||||
|             CHECK(cli.parse({"test", "-e"})); | ||||
|  | ||||
|             REQUIRE(config.noThrow); | ||||
|         } | ||||
|         SECTION("--nothrow") { | ||||
|             CHECK(cli.parse({"test", "--nothrow"})); | ||||
|  | ||||
|             REQUIRE(config.noThrow); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("output filename") { | ||||
|         SECTION("-o filename") { | ||||
|             CHECK(cli.parse({"test", "-o", "filename.ext"})); | ||||
|  | ||||
|             REQUIRE(config.outputFilename == "filename.ext"); | ||||
|         } | ||||
|         SECTION("--out") { | ||||
|             CHECK(cli.parse({"test", "--out", "filename.ext"})); | ||||
|  | ||||
|             REQUIRE(config.outputFilename == "filename.ext"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("combinations") { | ||||
|         SECTION("Single character flags can be combined") { | ||||
|             CHECK(cli.parse({"test", "-abe"})); | ||||
|  | ||||
|             CHECK(config.abortAfter == 1); | ||||
|             CHECK(config.shouldDebugBreak); | ||||
|             CHECK(config.noThrow == true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     SECTION( "use-colour") { | ||||
|  | ||||
|         using Catch::UseColour; | ||||
|  | ||||
|         SECTION( "without option" ) { | ||||
|             CHECK(cli.parse({"test"})); | ||||
|  | ||||
|             REQUIRE( config.useColour == UseColour::Auto ); | ||||
|         } | ||||
|  | ||||
|         SECTION( "auto" ) { | ||||
|             CHECK(cli.parse({"test", "--use-colour", "auto"})); | ||||
|  | ||||
|             REQUIRE( config.useColour == UseColour::Auto ); | ||||
|         } | ||||
|  | ||||
|         SECTION( "yes" ) { | ||||
|             CHECK(cli.parse({"test", "--use-colour", "yes"})); | ||||
|  | ||||
|             REQUIRE( config.useColour == UseColour::Yes ); | ||||
|         } | ||||
|  | ||||
|         SECTION( "no" ) { | ||||
|             CHECK(cli.parse({"test", "--use-colour", "no"})); | ||||
|  | ||||
|             REQUIRE( config.useColour == UseColour::No ); | ||||
|         } | ||||
|  | ||||
|         SECTION( "error" ) { | ||||
|             auto result = cli.parse({"test", "--use-colour", "wrong"}); | ||||
|             CHECK( !result ); | ||||
| #ifndef CATCH_CONFIG_DISABLE_MATCHERS | ||||
|             CHECK_THAT( result.errorMessage(), Contains( "colour mode must be one of" ) ); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("Benchmark options") { | ||||
|         SECTION("samples") { | ||||
|             CHECK(cli.parse({ "test", "--benchmark-samples=200" })); | ||||
|  | ||||
|             REQUIRE(config.benchmarkSamples == 200); | ||||
|         } | ||||
|  | ||||
|         SECTION("resamples") { | ||||
|             CHECK(cli.parse({ "test", "--benchmark-resamples=20000" })); | ||||
|  | ||||
|             REQUIRE(config.benchmarkResamples == 20000); | ||||
|         } | ||||
|  | ||||
|         SECTION("resamples") { | ||||
|             CHECK(cli.parse({ "test", "--benchmark-confidence-interval=0.99" })); | ||||
|  | ||||
|             REQUIRE(config.benchmarkConfidenceInterval == Catch::Detail::Approx(0.99)); | ||||
|         } | ||||
|  | ||||
|         SECTION("resamples") { | ||||
|             CHECK(cli.parse({ "test", "--benchmark-no-analysis" })); | ||||
|  | ||||
|             REQUIRE(config.benchmarkNoAnalysis); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Test with special, characters \"in name", "[cli][regression]") { | ||||
|     // This test case succeeds if we can invoke it from the CLI | ||||
|     SUCCEED(); | ||||
| } | ||||
							
								
								
									
										23
									
								
								tests/SelfTest/IntrospectiveTests/Details.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tests/SelfTest/IntrospectiveTests/Details.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| /* | ||||
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
|  | ||||
| #if defined(_MSC_VER) | ||||
| #pragma warning(push) | ||||
| #pragma warning(disable:4702) // unreachable code in the macro expansions | ||||
| #endif | ||||
|  | ||||
| TEST_CASE("Check that our error handling macros throw the right exceptions", "[!throws][internals][approvals]") { | ||||
|     REQUIRE_THROWS_AS(CATCH_INTERNAL_ERROR(""), std::logic_error); | ||||
|     REQUIRE_THROWS_AS(CATCH_ERROR(""), std::domain_error); | ||||
|     REQUIRE_THROWS_AS(CATCH_RUNTIME_ERROR(""), std::runtime_error); | ||||
|     REQUIRE_THROWS_AS([](){CATCH_ENFORCE(false, "");}(), std::domain_error); | ||||
|     REQUIRE_NOTHROW([](){CATCH_ENFORCE(true, "");}()); | ||||
| } | ||||
|  | ||||
| #if defined(_MSC_VER) | ||||
| #pragma warning(pop) // unreachable code in the macro expansions | ||||
| #endif | ||||
							
								
								
									
										337
									
								
								tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,337 @@ | ||||
| #include <catch2/catch.hpp> | ||||
|  | ||||
| // Tests of generator implementation details | ||||
| TEST_CASE("Generators internals", "[generators][internals]") { | ||||
|     using namespace Catch::Generators; | ||||
|  | ||||
|     SECTION("Single value") { | ||||
|         auto gen = value(123); | ||||
|         REQUIRE(gen.get() == 123); | ||||
|         REQUIRE_FALSE(gen.next()); | ||||
|     } | ||||
|     SECTION("Preset values") { | ||||
|         auto gen = values({ 1, 3, 5 }); | ||||
|         REQUIRE(gen.get() == 1); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 3); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 5); | ||||
|         REQUIRE_FALSE(gen.next()); | ||||
|     } | ||||
|     SECTION("Generator combinator") { | ||||
|         auto gen = makeGenerators(1, 5, values({ 2, 4 }), 0); | ||||
|         REQUIRE(gen.get() == 1); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 5); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 2); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 4); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 0); | ||||
|         REQUIRE_FALSE(gen.next()); | ||||
|     } | ||||
|     SECTION("Explicitly typed generator sequence") { | ||||
|         auto gen = makeGenerators(as<std::string>{}, "aa", "bb", "cc"); | ||||
|         // This just checks that the type is std::string: | ||||
|         REQUIRE(gen.get().size() == 2); | ||||
|         // Iterate over the generator | ||||
|         REQUIRE(gen.get() == "aa"); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == "bb"); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == "cc"); | ||||
|         REQUIRE_FALSE(gen.next()); | ||||
|     } | ||||
|     SECTION("Filter generator") { | ||||
|         // Normal usage | ||||
|         auto gen = filter([] (int i) { return i != 2; }, values({ 2, 1, 2, 3, 2, 2 })); | ||||
|         REQUIRE(gen.get() == 1); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 3); | ||||
|         REQUIRE_FALSE(gen.next()); | ||||
|  | ||||
|         // Completely filtered-out generator should throw on construction | ||||
|         REQUIRE_THROWS_AS(filter([] (int) { return false; }, value(1)), Catch::GeneratorException); | ||||
|     } | ||||
|     SECTION("Take generator") { | ||||
|         SECTION("Take less") { | ||||
|             auto gen = take(2, values({ 1, 2, 3 })); | ||||
|             REQUIRE(gen.get() == 1); | ||||
|             REQUIRE(gen.next()); | ||||
|             REQUIRE(gen.get() == 2); | ||||
|             REQUIRE_FALSE(gen.next()); | ||||
|         } | ||||
|         SECTION("Take more") { | ||||
|             auto gen = take(2, value(1)); | ||||
|             REQUIRE(gen.get() == 1); | ||||
|             REQUIRE_FALSE(gen.next()); | ||||
|         } | ||||
|     } | ||||
|     SECTION("Map with explicit return type") { | ||||
|         auto gen = map<double>([] (int i) {return 2.0 * i; }, values({ 1, 2, 3 })); | ||||
|         REQUIRE(gen.get() == 2.0); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 4.0); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 6.0); | ||||
|         REQUIRE_FALSE(gen.next()); | ||||
|     } | ||||
|     SECTION("Map with deduced return type") { | ||||
|         auto gen = map([] (int i) {return 2.0 * i; }, values({ 1, 2, 3 })); | ||||
|         REQUIRE(gen.get() == 2.0); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 4.0); | ||||
|         REQUIRE(gen.next()); | ||||
|         REQUIRE(gen.get() == 6.0); | ||||
|         REQUIRE_FALSE(gen.next()); | ||||
|     } | ||||
|     SECTION("Repeat") { | ||||
|         SECTION("Singular repeat") { | ||||
|             auto gen = repeat(1, value(3)); | ||||
|             REQUIRE(gen.get() == 3); | ||||
|             REQUIRE_FALSE(gen.next()); | ||||
|         } | ||||
|         SECTION("Actual repeat") { | ||||
|             auto gen = repeat(2, values({ 1, 2, 3 })); | ||||
|             REQUIRE(gen.get() == 1); | ||||
|             REQUIRE(gen.next()); | ||||
|             REQUIRE(gen.get() == 2); | ||||
|             REQUIRE(gen.next()); | ||||
|             REQUIRE(gen.get() == 3); | ||||
|             REQUIRE(gen.next()); | ||||
|             REQUIRE(gen.get() == 1); | ||||
|             REQUIRE(gen.next()); | ||||
|             REQUIRE(gen.get() == 2); | ||||
|             REQUIRE(gen.next()); | ||||
|             REQUIRE(gen.get() == 3); | ||||
|             REQUIRE_FALSE(gen.next()); | ||||
|         } | ||||
|     } | ||||
|     SECTION("Range") { | ||||
|         SECTION("Positive auto step") { | ||||
|             SECTION("Integer") { | ||||
|                 auto gen = range(-2, 2); | ||||
|                 REQUIRE(gen.get() == -2); | ||||
|                 REQUIRE(gen.next()); | ||||
|                 REQUIRE(gen.get() == -1); | ||||
|                 REQUIRE(gen.next()); | ||||
|                 REQUIRE(gen.get() == 0); | ||||
|                 REQUIRE(gen.next()); | ||||
|                 REQUIRE(gen.get() == 1); | ||||
|                 REQUIRE_FALSE(gen.next()); | ||||
|             } | ||||
|         } | ||||
|         SECTION("Negative auto step") { | ||||
|             SECTION("Integer") { | ||||
|                 auto gen = range(2, -2); | ||||
|                 REQUIRE(gen.get() == 2); | ||||
|                 REQUIRE(gen.next()); | ||||
|                 REQUIRE(gen.get() == 1); | ||||
|                 REQUIRE(gen.next()); | ||||
|                 REQUIRE(gen.get() == 0); | ||||
|                 REQUIRE(gen.next()); | ||||
|                 REQUIRE(gen.get() == -1); | ||||
|                 REQUIRE_FALSE(gen.next()); | ||||
|             } | ||||
|         } | ||||
|         SECTION("Positive manual step") { | ||||
|             SECTION("Integer") { | ||||
|                 SECTION("Exact") { | ||||
|                     auto gen = range(-7, 5, 3); | ||||
|                     REQUIRE(gen.get() == -7); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -4); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -1); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == 2); | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|                 SECTION("Slightly over end") { | ||||
|                     auto gen = range(-7, 4, 3); | ||||
|                     REQUIRE(gen.get() == -7); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -4); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -1); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == 2); | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|                 SECTION("Slightly under end") { | ||||
|                     auto gen = range(-7, 6, 3); | ||||
|                     REQUIRE(gen.get() == -7); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -4); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -1); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == 2); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == 5); | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             SECTION("Floating Point") { | ||||
|                 SECTION("Exact") { | ||||
|                     const auto rangeStart = -1.; | ||||
|                     const auto rangeEnd = 1.; | ||||
|                     const auto step = .1; | ||||
|  | ||||
|                     auto gen = range(rangeStart, rangeEnd, step); | ||||
|                     auto expected = rangeStart; | ||||
|                     while( (rangeEnd - expected) > step ) { | ||||
|                         INFO( "Current expected value is " << expected ); | ||||
|                         REQUIRE(gen.get() == Approx(expected)); | ||||
|                         REQUIRE(gen.next()); | ||||
|  | ||||
|                         expected += step; | ||||
|                     } | ||||
|                     REQUIRE(gen.get() == Approx( rangeEnd ) ); | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|                 SECTION("Slightly over end") { | ||||
|                     const auto rangeStart = -1.; | ||||
|                     const auto rangeEnd = 1.; | ||||
|                     const auto step = .3; | ||||
|  | ||||
|                     auto gen = range(rangeStart, rangeEnd, step); | ||||
|                     auto expected = rangeStart; | ||||
|                     while( (rangeEnd - expected) > step ) { | ||||
|                        INFO( "Current expected value is " << expected ); | ||||
|                        REQUIRE(gen.get() == Approx(expected)); | ||||
|                        REQUIRE(gen.next()); | ||||
|  | ||||
|                        expected += step; | ||||
|                     } | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|                 SECTION("Slightly under end") { | ||||
|                     const auto rangeStart = -1.; | ||||
|                     const auto rangeEnd = .9; | ||||
|                     const auto step = .3; | ||||
|  | ||||
|                     auto gen = range(rangeStart, rangeEnd, step); | ||||
|                     auto expected = rangeStart; | ||||
|                     while( (rangeEnd - expected) > step ) { | ||||
|                        INFO( "Current expected value is " << expected ); | ||||
|                        REQUIRE(gen.get() == Approx(expected)); | ||||
|                        REQUIRE(gen.next()); | ||||
|  | ||||
|                        expected += step; | ||||
|                     } | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         SECTION("Negative manual step") { | ||||
|             SECTION("Integer") { | ||||
|                 SECTION("Exact") { | ||||
|                     auto gen = range(5, -7, -3); | ||||
|                     REQUIRE(gen.get() == 5); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == 2); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -1); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -4); | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|                 SECTION("Slightly over end") { | ||||
|                     auto gen = range(5, -6, -3); | ||||
|                     REQUIRE(gen.get() == 5); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == 2); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -1); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -4); | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|                 SECTION("Slightly under end") { | ||||
|                     auto gen = range(5, -8, -3); | ||||
|                     REQUIRE(gen.get() == 5); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == 2); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -1); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -4); | ||||
|                     REQUIRE(gen.next()); | ||||
|                     REQUIRE(gen.get() == -7); | ||||
|                     REQUIRE_FALSE(gen.next()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| // todo: uncopyable type used in a generator | ||||
| //  idea: uncopyable tag type for a stupid generator | ||||
|  | ||||
| namespace { | ||||
| struct non_copyable { | ||||
|     non_copyable() = default; | ||||
|     non_copyable(non_copyable const&) = delete; | ||||
|     non_copyable& operator=(non_copyable const&) = delete; | ||||
|     int value = -1; | ||||
| }; | ||||
|  | ||||
| // This class shows how to implement a simple generator for Catch tests | ||||
| class TestGen : public Catch::Generators::IGenerator<int> { | ||||
|     int current_number; | ||||
| public: | ||||
|  | ||||
|     TestGen(non_copyable const& nc): | ||||
|         current_number(nc.value) {} | ||||
|  | ||||
|     int const& get() const override; | ||||
|     bool next() override { | ||||
|         return false; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| // Avoids -Wweak-vtables | ||||
| int const& TestGen::get() const { | ||||
|     return current_number; | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| TEST_CASE("GENERATE capture macros", "[generators][internals][approvals]") { | ||||
|     auto value = GENERATE(take(10, random(0, 10))); | ||||
|  | ||||
|     non_copyable nc; nc.value = value; | ||||
|     // neither `GENERATE_COPY` nor plain `GENERATE` would compile here | ||||
|     auto value2 = GENERATE_REF(Catch::Generators::GeneratorWrapper<int>(std::unique_ptr<Catch::Generators::IGenerator<int>>(new TestGen(nc)))); | ||||
|     REQUIRE(value == value2); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Multiple random generators in one test case output different values", "[generators][internals][approvals]") { | ||||
|     SECTION("Integer") { | ||||
|         auto random1 = Catch::Generators::random(0, 1000); | ||||
|         auto random2 = Catch::Generators::random(0, 1000); | ||||
|         size_t same = 0; | ||||
|         for (size_t i = 0; i < 1000; ++i) { | ||||
|             same += random1.get() == random2.get(); | ||||
|             random1.next(); random2.next(); | ||||
|         } | ||||
|         // 0.5% seems like a sane bound for random identical elements within 1000 runs | ||||
|         REQUIRE(same < 5); | ||||
|     } | ||||
|     SECTION("Float") { | ||||
|         auto random1 = Catch::Generators::random(0., 1000.); | ||||
|         auto random2 = Catch::Generators::random(0., 1000.); | ||||
|         size_t same = 0; | ||||
|         for (size_t i = 0; i < 1000; ++i) { | ||||
|             same += random1.get() == random2.get(); | ||||
|             random1.next(); random2.next(); | ||||
|         } | ||||
|         // 0.5% seems like a sane bound for random identical elements within 1000 runs | ||||
|         REQUIRE(same < 5); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										405
									
								
								tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										405
									
								
								tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,405 @@ | ||||
| /* | ||||
|  *  Created by Joachim on 16/04/2019. | ||||
|  *  Adapted from donated nonius code. | ||||
|  * | ||||
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
| #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) | ||||
| namespace { | ||||
|     struct manual_clock { | ||||
|     public: | ||||
|         using duration = std::chrono::nanoseconds; | ||||
|         using time_point = std::chrono::time_point<manual_clock, duration>; | ||||
|         using rep = duration::rep; | ||||
|         using period = duration::period; | ||||
|         enum { is_steady = true }; | ||||
|  | ||||
|         static time_point now() { | ||||
|             return time_point(duration(tick())); | ||||
|         } | ||||
|  | ||||
|         static void advance(int ticks = 1) { | ||||
|             tick() += ticks; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         static rep& tick() { | ||||
|             static rep the_tick = 0; | ||||
|             return the_tick; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     struct counting_clock { | ||||
|     public: | ||||
|         using duration = std::chrono::nanoseconds; | ||||
|         using time_point = std::chrono::time_point<counting_clock, duration>; | ||||
|         using rep = duration::rep; | ||||
|         using period = duration::period; | ||||
|         enum { is_steady = true }; | ||||
|  | ||||
|         static time_point now() { | ||||
|             static rep ticks = 0; | ||||
|             return time_point(duration(ticks += rate())); | ||||
|         } | ||||
|  | ||||
|         static void set_rate(rep new_rate) { rate() = new_rate; } | ||||
|  | ||||
|     private: | ||||
|         static rep& rate() { | ||||
|             static rep the_rate = 1; | ||||
|             return the_rate; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     struct TestChronometerModel : Catch::Benchmark::Detail::ChronometerConcept { | ||||
|         int started = 0; | ||||
|         int finished = 0; | ||||
|  | ||||
|         void start() override { ++started; } | ||||
|         void finish() override { ++finished; } | ||||
|     }; | ||||
| } // namespace | ||||
|  | ||||
| TEST_CASE("warmup", "[benchmark]") { | ||||
|     auto rate = 1000; | ||||
|     counting_clock::set_rate(rate); | ||||
|  | ||||
|     auto start = counting_clock::now(); | ||||
|     auto iterations = Catch::Benchmark::Detail::warmup<counting_clock>(); | ||||
|     auto end = counting_clock::now(); | ||||
|  | ||||
|     REQUIRE((iterations * rate) > Catch::Benchmark::Detail::warmup_time.count()); | ||||
|     REQUIRE((end - start) > Catch::Benchmark::Detail::warmup_time); | ||||
| } | ||||
|  | ||||
| TEST_CASE("resolution", "[benchmark]") { | ||||
|     auto rate = 1000; | ||||
|     counting_clock::set_rate(rate); | ||||
|  | ||||
|     size_t count = 10; | ||||
|     auto res = Catch::Benchmark::Detail::resolution<counting_clock>(static_cast<int>(count)); | ||||
|  | ||||
|     REQUIRE(res.size() == count); | ||||
|  | ||||
|     for (size_t i = 1; i < count; ++i) { | ||||
|         REQUIRE(res[i] == rate); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("estimate_clock_resolution", "[benchmark]") { | ||||
|     auto rate = 1000; | ||||
|     counting_clock::set_rate(rate); | ||||
|  | ||||
|     int iters = 160000; | ||||
|     auto res = Catch::Benchmark::Detail::estimate_clock_resolution<counting_clock>(iters); | ||||
|  | ||||
|     REQUIRE(res.mean.count() == rate); | ||||
|     REQUIRE(res.outliers.total() == 0); | ||||
| } | ||||
|  | ||||
| TEST_CASE("benchmark function call", "[benchmark]") { | ||||
|     SECTION("without chronometer") { | ||||
|         auto called = 0; | ||||
|         auto model = TestChronometerModel{}; | ||||
|         auto meter = Catch::Benchmark::Chronometer{ model, 1 }; | ||||
|         auto fn = Catch::Benchmark::Detail::BenchmarkFunction{ [&] { | ||||
|                 CHECK(model.started == 1); | ||||
|                 CHECK(model.finished == 0); | ||||
|                 ++called; | ||||
|             } }; | ||||
|  | ||||
|         fn(meter); | ||||
|  | ||||
|         CHECK(model.started == 1); | ||||
|         CHECK(model.finished == 1); | ||||
|         CHECK(called == 1); | ||||
|     } | ||||
|  | ||||
|     SECTION("with chronometer") { | ||||
|         auto called = 0; | ||||
|         auto model = TestChronometerModel{}; | ||||
|         auto meter = Catch::Benchmark::Chronometer{ model, 1 }; | ||||
|         auto fn = Catch::Benchmark::Detail::BenchmarkFunction{ [&](Catch::Benchmark::Chronometer) { | ||||
|                 CHECK(model.started == 0); | ||||
|                 CHECK(model.finished == 0); | ||||
|                 ++called; | ||||
|             } }; | ||||
|  | ||||
|         fn(meter); | ||||
|  | ||||
|         CHECK(model.started == 0); | ||||
|         CHECK(model.finished == 0); | ||||
|         CHECK(called == 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("uniform samples", "[benchmark]") { | ||||
|     std::vector<double> samples(100); | ||||
|     std::fill(samples.begin(), samples.end(), 23); | ||||
|  | ||||
|     using it = std::vector<double>::iterator; | ||||
|     auto e = Catch::Benchmark::Detail::bootstrap(0.95, samples.begin(), samples.end(), samples, [](it a, it b) { | ||||
|         auto sum = std::accumulate(a, b, 0.); | ||||
|         return sum / (b - a); | ||||
|     }); | ||||
|     CHECK(e.point == 23); | ||||
|     CHECK(e.upper_bound == 23); | ||||
|     CHECK(e.lower_bound == 23); | ||||
|     CHECK(e.confidence_interval == 0.95); | ||||
| } | ||||
|  | ||||
|  | ||||
| TEST_CASE("normal_cdf", "[benchmark]") { | ||||
|     using Catch::Benchmark::Detail::normal_cdf; | ||||
|     CHECK(normal_cdf(0.000000) == Approx(0.50000000000000000)); | ||||
|     CHECK(normal_cdf(1.000000) == Approx(0.84134474606854293)); | ||||
|     CHECK(normal_cdf(-1.000000) == Approx(0.15865525393145705)); | ||||
|     CHECK(normal_cdf(2.809729) == Approx(0.99752083845315409)); | ||||
|     CHECK(normal_cdf(-1.352570) == Approx(0.08809652095066035)); | ||||
| } | ||||
|  | ||||
| TEST_CASE("erfc_inv", "[benchmark]") { | ||||
|     using Catch::Benchmark::Detail::erfc_inv; | ||||
|     CHECK(erfc_inv(1.103560) == Approx(-0.09203687623843015)); | ||||
|     CHECK(erfc_inv(1.067400) == Approx(-0.05980291115763361)); | ||||
|     CHECK(erfc_inv(0.050000) == Approx(1.38590382434967796)); | ||||
| } | ||||
|  | ||||
| TEST_CASE("normal_quantile", "[benchmark]") { | ||||
|     using Catch::Benchmark::Detail::normal_quantile; | ||||
|     CHECK(normal_quantile(0.551780) == Approx(0.13015979861484198)); | ||||
|     CHECK(normal_quantile(0.533700) == Approx(0.08457408802851875)); | ||||
|     CHECK(normal_quantile(0.025000) == Approx(-1.95996398454005449)); | ||||
| } | ||||
|  | ||||
|  | ||||
| TEST_CASE("mean", "[benchmark]") { | ||||
|     std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; | ||||
|  | ||||
|     auto m = Catch::Benchmark::Detail::mean(x.begin(), x.end()); | ||||
|  | ||||
|     REQUIRE(m == 19.); | ||||
| } | ||||
|  | ||||
| TEST_CASE("weighted_average_quantile", "[benchmark]") { | ||||
|     std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; | ||||
|  | ||||
|     auto q1 = Catch::Benchmark::Detail::weighted_average_quantile(1, 4, x.begin(), x.end()); | ||||
|     auto med = Catch::Benchmark::Detail::weighted_average_quantile(1, 2, x.begin(), x.end()); | ||||
|     auto q3 = Catch::Benchmark::Detail::weighted_average_quantile(3, 4, x.begin(), x.end()); | ||||
|  | ||||
|     REQUIRE(q1 == 14.5); | ||||
|     REQUIRE(med == 18.); | ||||
|     REQUIRE(q3 == 23.); | ||||
| } | ||||
|  | ||||
| TEST_CASE("classify_outliers", "[benchmark]") { | ||||
|     auto require_outliers = [](Catch::Benchmark::OutlierClassification o, int los, int lom, int him, int his) { | ||||
|         REQUIRE(o.low_severe == los); | ||||
|         REQUIRE(o.low_mild == lom); | ||||
|         REQUIRE(o.high_mild == him); | ||||
|         REQUIRE(o.high_severe == his); | ||||
|         REQUIRE(o.total() == los + lom + him + his); | ||||
|     }; | ||||
|  | ||||
|     SECTION("none") { | ||||
|         std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; | ||||
|  | ||||
|         auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | ||||
|  | ||||
|         REQUIRE(o.samples_seen == static_cast<int>(x.size())); | ||||
|         require_outliers(o, 0, 0, 0, 0); | ||||
|     } | ||||
|     SECTION("low severe") { | ||||
|         std::vector<double> x{ -12., 20., 14., 16., 30., 24. }; | ||||
|  | ||||
|         auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | ||||
|  | ||||
|         REQUIRE(o.samples_seen == static_cast<int>(x.size())); | ||||
|         require_outliers(o, 1, 0, 0, 0); | ||||
|     } | ||||
|     SECTION("low mild") { | ||||
|         std::vector<double> x{ 1., 20., 14., 16., 30., 24. }; | ||||
|  | ||||
|         auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | ||||
|  | ||||
|         REQUIRE(o.samples_seen == static_cast<int>(x.size())); | ||||
|         require_outliers(o, 0, 1, 0, 0); | ||||
|     } | ||||
|     SECTION("high mild") { | ||||
|         std::vector<double> x{ 10., 20., 14., 16., 36., 24. }; | ||||
|  | ||||
|         auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | ||||
|  | ||||
|         REQUIRE(o.samples_seen == static_cast<int>(x.size())); | ||||
|         require_outliers(o, 0, 0, 1, 0); | ||||
|     } | ||||
|     SECTION("high severe") { | ||||
|         std::vector<double> x{ 10., 20., 14., 16., 49., 24. }; | ||||
|  | ||||
|         auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | ||||
|  | ||||
|         REQUIRE(o.samples_seen == static_cast<int>(x.size())); | ||||
|         require_outliers(o, 0, 0, 0, 1); | ||||
|     } | ||||
|     SECTION("mixed") { | ||||
|         std::vector<double> x{ -20., 20., 14., 16., 39., 24. }; | ||||
|  | ||||
|         auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | ||||
|  | ||||
|         REQUIRE(o.samples_seen == static_cast<int>(x.size())); | ||||
|         require_outliers(o, 1, 0, 1, 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("analyse", "[benchmark]") { | ||||
|     Catch::ConfigData data{}; | ||||
|     data.benchmarkConfidenceInterval = 0.95; | ||||
|     data.benchmarkNoAnalysis = false; | ||||
|     data.benchmarkResamples = 1000; | ||||
|     data.benchmarkSamples = 99; | ||||
|     Catch::Config config{data}; | ||||
|  | ||||
|     using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>; | ||||
|  | ||||
|     Catch::Benchmark::Environment<Duration> env; | ||||
|     std::vector<Duration> samples(99); | ||||
|     for (size_t i = 0; i < samples.size(); ++i) { | ||||
|         samples[i] = Duration(23 + (i % 3 - 1)); | ||||
|     } | ||||
|  | ||||
|     auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); | ||||
|     CHECK(analysis.mean.point.count() == 23); | ||||
|     CHECK(analysis.mean.lower_bound.count() < 23); | ||||
|     CHECK(analysis.mean.lower_bound.count() > 22); | ||||
|     CHECK(analysis.mean.upper_bound.count() > 23); | ||||
|     CHECK(analysis.mean.upper_bound.count() < 24); | ||||
|  | ||||
|     CHECK(analysis.standard_deviation.point.count() > 0.5); | ||||
|     CHECK(analysis.standard_deviation.point.count() < 1); | ||||
|     CHECK(analysis.standard_deviation.lower_bound.count() > 0.5); | ||||
|     CHECK(analysis.standard_deviation.lower_bound.count() < 1); | ||||
|     CHECK(analysis.standard_deviation.upper_bound.count() > 0.5); | ||||
|     CHECK(analysis.standard_deviation.upper_bound.count() < 1); | ||||
|  | ||||
|     CHECK(analysis.outliers.total() == 0); | ||||
|     CHECK(analysis.outliers.low_mild == 0); | ||||
|     CHECK(analysis.outliers.low_severe == 0); | ||||
|     CHECK(analysis.outliers.high_mild == 0); | ||||
|     CHECK(analysis.outliers.high_severe == 0); | ||||
|     CHECK(analysis.outliers.samples_seen == samples.size()); | ||||
|  | ||||
|     CHECK(analysis.outlier_variance < 0.5); | ||||
|     CHECK(analysis.outlier_variance > 0); | ||||
| } | ||||
|  | ||||
| TEST_CASE("analyse no analysis", "[benchmark]") { | ||||
|     Catch::ConfigData data{}; | ||||
|     data.benchmarkConfidenceInterval = 0.95; | ||||
|     data.benchmarkNoAnalysis = true; | ||||
|     data.benchmarkResamples = 1000; | ||||
|     data.benchmarkSamples = 99; | ||||
|     Catch::Config config{ data }; | ||||
|  | ||||
|     using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>; | ||||
|  | ||||
|     Catch::Benchmark::Environment<Duration> env; | ||||
|     std::vector<Duration> samples(99); | ||||
|     for (size_t i = 0; i < samples.size(); ++i) { | ||||
|         samples[i] = Duration(23 + (i % 3 - 1)); | ||||
|     } | ||||
|  | ||||
|     auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); | ||||
|     CHECK(analysis.mean.point.count() == 23); | ||||
|     CHECK(analysis.mean.lower_bound.count() == 23); | ||||
|     CHECK(analysis.mean.upper_bound.count() == 23); | ||||
|  | ||||
|     CHECK(analysis.standard_deviation.point.count() == 0); | ||||
|     CHECK(analysis.standard_deviation.lower_bound.count() == 0); | ||||
|     CHECK(analysis.standard_deviation.upper_bound.count() == 0); | ||||
|  | ||||
|     CHECK(analysis.outliers.total() == 0); | ||||
|     CHECK(analysis.outliers.low_mild == 0); | ||||
|     CHECK(analysis.outliers.low_severe == 0); | ||||
|     CHECK(analysis.outliers.high_mild == 0); | ||||
|     CHECK(analysis.outliers.high_severe == 0); | ||||
|     CHECK(analysis.outliers.samples_seen == 0); | ||||
|  | ||||
|     CHECK(analysis.outlier_variance == 0); | ||||
| } | ||||
|  | ||||
| TEST_CASE("run_for_at_least, int", "[benchmark]") { | ||||
|     manual_clock::duration time(100); | ||||
|  | ||||
|     int old_x = 1; | ||||
|     auto Timing = Catch::Benchmark::Detail::run_for_at_least<manual_clock>(time, 1, [&old_x](int x) -> int { | ||||
|         CHECK(x >= old_x); | ||||
|         manual_clock::advance(x); | ||||
|         old_x = x; | ||||
|         return x + 17; | ||||
|     }); | ||||
|  | ||||
|     REQUIRE(Timing.elapsed >= time); | ||||
|     REQUIRE(Timing.result == Timing.iterations + 17); | ||||
|     REQUIRE(Timing.iterations >= time.count()); | ||||
| } | ||||
|  | ||||
| TEST_CASE("run_for_at_least, chronometer", "[benchmark]") { | ||||
|     manual_clock::duration time(100); | ||||
|  | ||||
|     int old_runs = 1; | ||||
|     auto Timing = Catch::Benchmark::Detail::run_for_at_least<manual_clock>(time, 1, [&old_runs](Catch::Benchmark::Chronometer meter) -> int { | ||||
|         CHECK(meter.runs() >= old_runs); | ||||
|         manual_clock::advance(100); | ||||
|         meter.measure([] { | ||||
|             manual_clock::advance(1); | ||||
|         }); | ||||
|         old_runs = meter.runs(); | ||||
|         return meter.runs() + 17; | ||||
|     }); | ||||
|  | ||||
|     REQUIRE(Timing.elapsed >= time); | ||||
|     REQUIRE(Timing.result == Timing.iterations + 17); | ||||
|     REQUIRE(Timing.iterations >= time.count()); | ||||
| } | ||||
|  | ||||
|  | ||||
| TEST_CASE("measure", "[benchmark]") { | ||||
|     auto r = Catch::Benchmark::Detail::measure<manual_clock>([](int x) -> int { | ||||
|         CHECK(x == 17); | ||||
|         manual_clock::advance(42); | ||||
|         return 23; | ||||
|     }, 17); | ||||
|     auto s = Catch::Benchmark::Detail::measure<manual_clock>([](int x) -> int { | ||||
|         CHECK(x == 23); | ||||
|         manual_clock::advance(69); | ||||
|         return 17; | ||||
|     }, 23); | ||||
|  | ||||
|     CHECK(r.elapsed.count() == 42); | ||||
|     CHECK(r.result == 23); | ||||
|     CHECK(r.iterations == 1); | ||||
|  | ||||
|     CHECK(s.elapsed.count() == 69); | ||||
|     CHECK(s.result == 17); | ||||
|     CHECK(s.iterations == 1); | ||||
| } | ||||
|  | ||||
| TEST_CASE("run benchmark", "[benchmark]") { | ||||
|     counting_clock::set_rate(1000); | ||||
|     auto start = counting_clock::now(); | ||||
|      | ||||
|     Catch::Benchmark::Benchmark bench{ "Test Benchmark", [](Catch::Benchmark::Chronometer meter) { | ||||
|         counting_clock::set_rate(100000); | ||||
|         meter.measure([] { return counting_clock::now(); }); | ||||
|     } }; | ||||
|  | ||||
|     bench.run<counting_clock>(); | ||||
|     auto end = counting_clock::now(); | ||||
|  | ||||
|     CHECK((end - start).count() == 2867251000); | ||||
| } | ||||
| #endif // CATCH_CONFIG_ENABLE_BENCHMARKING | ||||
							
								
								
									
										206
									
								
								tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | ||||
| /* | ||||
|  *  Created by Phil on 1/10/2015. | ||||
|  *  Copyright 2015 Two Blue Cubes Ltd | ||||
|  * | ||||
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
| #include <catch2/catch_suppress_warnings.h> | ||||
| #include <catch2/catch_test_case_tracker.h> | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
|  | ||||
| using namespace Catch; | ||||
|  | ||||
| namespace { | ||||
| Catch::TestCaseTracking::NameAndLocation makeNAL( std::string const& name ) { | ||||
|     return Catch::TestCaseTracking::NameAndLocation( name, Catch::SourceLineInfo("",0) ); | ||||
| } | ||||
| } | ||||
|  | ||||
| TEST_CASE( "Tracker" ) { | ||||
|  | ||||
|     TrackerContext ctx; | ||||
|     ctx.startRun(); | ||||
|     ctx.startCycle(); | ||||
|  | ||||
|  | ||||
|     ITracker& testCase = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); | ||||
|     REQUIRE( testCase.isOpen() ); | ||||
|  | ||||
|     ITracker& s1 = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); | ||||
|     REQUIRE( s1.isOpen() ); | ||||
|  | ||||
|     SECTION( "successfully close one section" ) { | ||||
|         s1.close(); | ||||
|         REQUIRE( s1.isSuccessfullyCompleted() ); | ||||
|         REQUIRE( testCase.isComplete() == false ); | ||||
|  | ||||
|         testCase.close(); | ||||
|         REQUIRE( ctx.completedCycle() ); | ||||
|         REQUIRE( testCase.isSuccessfullyCompleted() ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "fail one section" ) { | ||||
|         s1.fail(); | ||||
|         REQUIRE( s1.isComplete() ); | ||||
|         REQUIRE( s1.isSuccessfullyCompleted() == false ); | ||||
|         REQUIRE( testCase.isComplete() == false ); | ||||
|  | ||||
|         testCase.close(); | ||||
|         REQUIRE( ctx.completedCycle() ); | ||||
|         REQUIRE( testCase.isSuccessfullyCompleted() == false ); | ||||
|  | ||||
|         SECTION( "re-enter after failed section" ) { | ||||
|             ctx.startCycle(); | ||||
|             ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); | ||||
|             REQUIRE( testCase2.isOpen() ); | ||||
|  | ||||
|             ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); | ||||
|             REQUIRE( s1b.isOpen() == false ); | ||||
|  | ||||
|             testCase2.close(); | ||||
|             REQUIRE( ctx.completedCycle() ); | ||||
|             REQUIRE( testCase.isComplete() ); | ||||
|             REQUIRE( testCase.isSuccessfullyCompleted() ); | ||||
|         } | ||||
|         SECTION( "re-enter after failed section and find next section" ) { | ||||
|             ctx.startCycle(); | ||||
|             ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); | ||||
|             REQUIRE( testCase2.isOpen() ); | ||||
|  | ||||
|             ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); | ||||
|             REQUIRE( s1b.isOpen() == false ); | ||||
|  | ||||
|             ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); | ||||
|             REQUIRE( s2.isOpen() ); | ||||
|  | ||||
|             s2.close(); | ||||
|             REQUIRE( ctx.completedCycle() ); | ||||
|  | ||||
|             testCase2.close(); | ||||
|             REQUIRE( testCase.isComplete() ); | ||||
|             REQUIRE( testCase.isSuccessfullyCompleted() ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION( "successfully close one section, then find another" ) { | ||||
|         s1.close(); | ||||
|  | ||||
|         ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); | ||||
|         REQUIRE( s2.isOpen() == false ); | ||||
|  | ||||
|         testCase.close(); | ||||
|         REQUIRE( testCase.isComplete() == false ); | ||||
|  | ||||
|         SECTION( "Re-enter - skips S1 and enters S2" ) { | ||||
|             ctx.startCycle(); | ||||
|             ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); | ||||
|             REQUIRE( testCase2.isOpen() ); | ||||
|  | ||||
|             ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); | ||||
|             REQUIRE( s1b.isOpen() == false ); | ||||
|  | ||||
|             ITracker& s2b = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); | ||||
|             REQUIRE( s2b.isOpen() ); | ||||
|  | ||||
|             REQUIRE( ctx.completedCycle() == false ); | ||||
|  | ||||
|             SECTION ("Successfully close S2") { | ||||
|                 s2b.close(); | ||||
|                 REQUIRE( ctx.completedCycle() ); | ||||
|  | ||||
|                 REQUIRE( s2b.isSuccessfullyCompleted() ); | ||||
|                 REQUIRE( testCase2.isComplete() == false ); | ||||
|  | ||||
|                 testCase2.close(); | ||||
|                 REQUIRE( testCase2.isSuccessfullyCompleted() ); | ||||
|             } | ||||
|             SECTION ("fail S2") { | ||||
|                 s2b.fail(); | ||||
|                 REQUIRE( ctx.completedCycle() ); | ||||
|  | ||||
|                 REQUIRE( s2b.isComplete() ); | ||||
|                 REQUIRE( s2b.isSuccessfullyCompleted() == false ); | ||||
|  | ||||
|                 testCase2.close(); | ||||
|                 REQUIRE( testCase2.isSuccessfullyCompleted() == false ); | ||||
|  | ||||
|                 // Need a final cycle | ||||
|                 ctx.startCycle(); | ||||
|                 ITracker& testCase3 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); | ||||
|                 REQUIRE( testCase3.isOpen() ); | ||||
|  | ||||
|                 ITracker& s1c = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); | ||||
|                 REQUIRE( s1c.isOpen() == false ); | ||||
|  | ||||
|                 ITracker& s2c = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); | ||||
|                 REQUIRE( s2c.isOpen() == false ); | ||||
|  | ||||
|                 testCase3.close(); | ||||
|                 REQUIRE( testCase3.isSuccessfullyCompleted() ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION( "open a nested section" ) { | ||||
|         ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); | ||||
|         REQUIRE( s2.isOpen() ); | ||||
|  | ||||
|         s2.close(); | ||||
|         REQUIRE( s2.isComplete() ); | ||||
|         REQUIRE( s1.isComplete() == false ); | ||||
|  | ||||
|         s1.close(); | ||||
|         REQUIRE( s1.isComplete() ); | ||||
|         REQUIRE( testCase.isComplete() == false ); | ||||
|  | ||||
|         testCase.close(); | ||||
|         REQUIRE( testCase.isComplete() ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static bool previouslyRun = false; | ||||
| static bool previouslyRunNested = false; | ||||
|  | ||||
| TEST_CASE( "#1394", "[.][approvals][tracker]" ) { | ||||
|     // -- Don't re-run after specified section is done | ||||
|     REQUIRE(previouslyRun == false); | ||||
|  | ||||
|     SECTION( "RunSection" ) { | ||||
|         previouslyRun = true; | ||||
|     } | ||||
|     SECTION( "SkipSection" ) { | ||||
|         // cause an error if this section is called because it shouldn't be | ||||
|         REQUIRE(1 == 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE( "#1394 nested", "[.][approvals][tracker]" ) { | ||||
|     REQUIRE(previouslyRunNested == false); | ||||
|  | ||||
|     SECTION( "NestedRunSection" ) { | ||||
|         SECTION( "s1" ) { | ||||
|             previouslyRunNested = true; | ||||
|         } | ||||
|     } | ||||
|     SECTION( "NestedSkipSection" ) { | ||||
|         // cause an error if this section is called because it shouldn't be | ||||
|         REQUIRE(1 == 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Selecting a "not last" section inside a test case via -c "section" would | ||||
| // previously only run the first subsection, instead of running all of them. | ||||
| // This allows us to check that `"#1670 regression check" -c A` leads to | ||||
| // 2 successful assertions. | ||||
| TEST_CASE("#1670 regression check", "[.approvals][tracker]") { | ||||
|     SECTION("A") { | ||||
|         SECTION("1") SUCCEED(); | ||||
|         SECTION("2") SUCCEED(); | ||||
|     } | ||||
|     SECTION("B") { | ||||
|         SECTION("1") SUCCEED(); | ||||
|         SECTION("2") SUCCEED(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
|  *  Created by Martin on 06/10/2019. | ||||
|  * | ||||
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
|  | ||||
| #include <catch2/catch_random_number_generator.h> | ||||
|  | ||||
| TEST_CASE("Our PCG implementation provides expected results for known seeds", "[rng]") { | ||||
|     Catch::SimplePcg32 rng; | ||||
|     SECTION("Default seeded") { | ||||
|         REQUIRE(rng() == 0xfcdb943b); | ||||
|         REQUIRE(rng() == 0x6f55b921); | ||||
|         REQUIRE(rng() == 0x4c17a916); | ||||
|         REQUIRE(rng() == 0x71eae25f); | ||||
|         REQUIRE(rng() == 0x6ce7909c); | ||||
|     } | ||||
|     SECTION("Specific seed") { | ||||
|         rng.seed(0xabcd1234); | ||||
|         REQUIRE(rng() == 0x57c08495); | ||||
|         REQUIRE(rng() == 0x33c956ac); | ||||
|         REQUIRE(rng() == 0x2206fd76); | ||||
|         REQUIRE(rng() == 0x3501a35b); | ||||
|         REQUIRE(rng() == 0xfdffb30f); | ||||
|  | ||||
|         // Also check repeated output after reseeding | ||||
|         rng.seed(0xabcd1234); | ||||
|         REQUIRE(rng() == 0x57c08495); | ||||
|         REQUIRE(rng() == 0x33c956ac); | ||||
|         REQUIRE(rng() == 0x2206fd76); | ||||
|         REQUIRE(rng() == 0x3501a35b); | ||||
|         REQUIRE(rng() == 0xfdffb30f); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Comparison ops", "[rng]") { | ||||
|     using Catch::SimplePcg32; | ||||
|     REQUIRE(SimplePcg32{} == SimplePcg32{}); | ||||
|     REQUIRE(SimplePcg32{ 0 } != SimplePcg32{}); | ||||
|     REQUIRE_FALSE(SimplePcg32{ 1 } == SimplePcg32{ 2 }); | ||||
|     REQUIRE_FALSE(SimplePcg32{ 1 } != SimplePcg32{ 1 }); | ||||
| } | ||||
							
								
								
									
										150
									
								
								tests/SelfTest/IntrospectiveTests/String.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								tests/SelfTest/IntrospectiveTests/String.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| #include <catch2/catch_stringref.h> | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
|  | ||||
| #include <cstring> | ||||
|  | ||||
| TEST_CASE( "StringRef", "[Strings][StringRef]" ) { | ||||
|  | ||||
|     using Catch::StringRef; | ||||
|  | ||||
|     SECTION( "Empty string" ) { | ||||
|         StringRef empty; | ||||
|         REQUIRE( empty.empty() ); | ||||
|         REQUIRE( empty.size() == 0 ); | ||||
|         REQUIRE( empty.isNullTerminated() ); | ||||
|         REQUIRE( std::strcmp( empty.c_str(), "" ) == 0 ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "From string literal" ) { | ||||
|         StringRef s = "hello"; | ||||
|         REQUIRE( s.empty() == false ); | ||||
|         REQUIRE( s.size() == 5 ); | ||||
|         REQUIRE( s.isNullTerminated() ); | ||||
|  | ||||
|         auto rawChars = s.data(); | ||||
|         REQUIRE( std::strcmp( rawChars, "hello" ) == 0 ); | ||||
|  | ||||
|         REQUIRE_NOTHROW(s.c_str()); | ||||
|         REQUIRE(s.c_str() == rawChars); | ||||
|         REQUIRE(s.data() == rawChars); | ||||
|     } | ||||
|     SECTION( "From sub-string" ) { | ||||
|         StringRef original = StringRef( "original string" ).substr(0, 8); | ||||
|         REQUIRE( original == "original" ); | ||||
|  | ||||
|         REQUIRE_FALSE(original.isNullTerminated()); | ||||
|         REQUIRE_THROWS(original.c_str()); | ||||
|         REQUIRE_NOTHROW(original.data()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     SECTION( "Substrings" ) { | ||||
|         StringRef s = "hello world!"; | ||||
|         StringRef ss = s.substr(0, 5); | ||||
|  | ||||
|         SECTION( "zero-based substring" ) { | ||||
|             REQUIRE( ss.empty() == false ); | ||||
|             REQUIRE( ss.size() == 5 ); | ||||
|             REQUIRE( std::strncmp( ss.data(), "hello", 5 ) == 0 ); | ||||
|             REQUIRE( ss == "hello" ); | ||||
|         } | ||||
|  | ||||
|         SECTION( "non-zero-based substring") { | ||||
|             ss = s.substr( 6, 6 ); | ||||
|             REQUIRE( ss.size() == 6 ); | ||||
|             REQUIRE( std::strcmp( ss.c_str(), "world!" ) == 0 ); | ||||
|         } | ||||
|  | ||||
|         SECTION( "Pointer values of full refs should match" ) { | ||||
|             StringRef s2 = s; | ||||
|             REQUIRE( s.data() == s2.data() ); | ||||
|         } | ||||
|  | ||||
|         SECTION( "Pointer values of substring refs should also match" ) { | ||||
|             REQUIRE( s.data() == ss.data() ); | ||||
|         } | ||||
|  | ||||
|         SECTION("Past the end substring") { | ||||
|             REQUIRE(s.substr(s.size() + 1, 123).empty()); | ||||
|         } | ||||
|  | ||||
|         SECTION("Substring off the end are trimmed") { | ||||
|             ss = s.substr(6, 123); | ||||
|             REQUIRE(std::strcmp(ss.c_str(), "world!") == 0); | ||||
|         } | ||||
|         // TODO: substring into string + size is longer than end | ||||
|     } | ||||
|  | ||||
|     SECTION( "Comparisons are deep" ) { | ||||
|         char buffer1[] = "Hello"; | ||||
|         char buffer2[] = "Hello"; | ||||
|         CHECK((char*)buffer1 != (char*)buffer2); | ||||
|  | ||||
|         StringRef left(buffer1), right(buffer2); | ||||
|         REQUIRE( left == right ); | ||||
|         REQUIRE(left != left.substr(0, 3)); | ||||
|     } | ||||
|  | ||||
|     SECTION( "from std::string" ) { | ||||
|         std::string stdStr = "a standard string"; | ||||
|  | ||||
|         SECTION( "implicitly constructed" ) { | ||||
|             StringRef sr = stdStr; | ||||
|             REQUIRE( sr == "a standard string" ); | ||||
|             REQUIRE( sr.size() == stdStr.size() ); | ||||
|         } | ||||
|         SECTION( "explicitly constructed" ) { | ||||
|             StringRef sr( stdStr ); | ||||
|             REQUIRE( sr == "a standard string" ); | ||||
|             REQUIRE( sr.size() == stdStr.size() ); | ||||
|         } | ||||
|         SECTION( "assigned" ) { | ||||
|             StringRef sr; | ||||
|             sr = stdStr; | ||||
|             REQUIRE( sr == "a standard string" ); | ||||
|             REQUIRE( sr.size() == stdStr.size() ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION( "to std::string" ) { | ||||
|         StringRef sr = "a stringref"; | ||||
|  | ||||
|         SECTION( "explicitly constructed" ) { | ||||
|             std::string stdStr( sr ); | ||||
|             REQUIRE( stdStr == "a stringref" ); | ||||
|             REQUIRE( stdStr.size() == sr.size() ); | ||||
|         } | ||||
|         SECTION( "assigned" ) { | ||||
|             std::string stdStr; | ||||
|             stdStr = static_cast<std::string>(sr); | ||||
|             REQUIRE( stdStr == "a stringref" ); | ||||
|             REQUIRE( stdStr.size() == sr.size() ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("StringRef at compilation time", "[Strings][StringRef][constexpr]") { | ||||
|     using Catch::StringRef; | ||||
|     SECTION("Simple constructors") { | ||||
|         STATIC_REQUIRE(StringRef{}.size() == 0); | ||||
|  | ||||
|         STATIC_REQUIRE(StringRef{ "abc", 3 }.size() == 3); | ||||
|         STATIC_REQUIRE(StringRef{ "abc", 3 }.isNullTerminated()); | ||||
|  | ||||
|         STATIC_REQUIRE(StringRef{ "abc", 2 }.size() == 2); | ||||
|         STATIC_REQUIRE_FALSE(StringRef{ "abc", 2 }.isNullTerminated()); | ||||
|     } | ||||
|     SECTION("UDL construction") { | ||||
|         constexpr auto sr1 = "abc"_catch_sr; | ||||
|         STATIC_REQUIRE_FALSE(sr1.empty()); | ||||
|         STATIC_REQUIRE(sr1.size() == 3); | ||||
|         STATIC_REQUIRE(sr1.isNullTerminated()); | ||||
|  | ||||
|         using Catch::operator"" _sr; | ||||
|         constexpr auto sr2 = ""_sr; | ||||
|         STATIC_REQUIRE(sr2.empty()); | ||||
|         STATIC_REQUIRE(sr2.size() == 0); | ||||
|         STATIC_REQUIRE(sr2.isNullTerminated()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										67
									
								
								tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| #include <catch2/catch_string_manip.h> | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
|  | ||||
| static const char * const no_whitespace = "There is no extra whitespace here"; | ||||
| static const char * const leading_whitespace = " \r \t\n There is no extra whitespace here"; | ||||
| static const char * const trailing_whitespace = "There is no extra whitespace here \t \n \r "; | ||||
| static const char * const whitespace_at_both_ends = " \r\n \t There is no extra whitespace here  \t\t\t \n"; | ||||
|  | ||||
| TEST_CASE("Trim strings", "[string-manip]") { | ||||
|     using Catch::trim; using Catch::StringRef; | ||||
|     static_assert(std::is_same<std::string, decltype(trim(std::string{}))>::value, "Trimming std::string should return std::string"); | ||||
|     static_assert(std::is_same<StringRef, decltype(trim(StringRef{}))>::value, "Trimming StringRef should return StringRef"); | ||||
|  | ||||
|     REQUIRE(trim(std::string(no_whitespace)) == no_whitespace); | ||||
|     REQUIRE(trim(std::string(leading_whitespace)) == no_whitespace); | ||||
|     REQUIRE(trim(std::string(trailing_whitespace)) == no_whitespace); | ||||
|     REQUIRE(trim(std::string(whitespace_at_both_ends)) == no_whitespace); | ||||
|  | ||||
|     REQUIRE(trim(StringRef(no_whitespace)) == StringRef(no_whitespace)); | ||||
|     REQUIRE(trim(StringRef(leading_whitespace)) == StringRef(no_whitespace)); | ||||
|     REQUIRE(trim(StringRef(trailing_whitespace)) == StringRef(no_whitespace)); | ||||
|     REQUIRE(trim(StringRef(whitespace_at_both_ends)) == StringRef(no_whitespace)); | ||||
| } | ||||
|  | ||||
| TEST_CASE("replaceInPlace", "[string-manip]") { | ||||
|     std::string letters = "abcdefcg"; | ||||
|     SECTION("replace single char") { | ||||
|         CHECK(Catch::replaceInPlace(letters, "b", "z")); | ||||
|         CHECK(letters == "azcdefcg"); | ||||
|     } | ||||
|     SECTION("replace two chars") { | ||||
|         CHECK(Catch::replaceInPlace(letters, "c", "z")); | ||||
|         CHECK(letters == "abzdefzg"); | ||||
|     } | ||||
|     SECTION("replace first char") { | ||||
|         CHECK(Catch::replaceInPlace(letters, "a", "z")); | ||||
|         CHECK(letters == "zbcdefcg"); | ||||
|     } | ||||
|     SECTION("replace last char") { | ||||
|         CHECK(Catch::replaceInPlace(letters, "g", "z")); | ||||
|         CHECK(letters == "abcdefcz"); | ||||
|     } | ||||
|     SECTION("replace all chars") { | ||||
|         CHECK(Catch::replaceInPlace(letters, letters, "replaced")); | ||||
|         CHECK(letters == "replaced"); | ||||
|     } | ||||
|     SECTION("replace no chars") { | ||||
|         CHECK_FALSE(Catch::replaceInPlace(letters, "x", "z")); | ||||
|         CHECK(letters == letters); | ||||
|     } | ||||
|     SECTION("escape '") { | ||||
|         std::string s = "didn't"; | ||||
|         CHECK(Catch::replaceInPlace(s, "'", "|'")); | ||||
|         CHECK(s == "didn|'t"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("splitString", "[string-manip]") { | ||||
|     using namespace Catch::Matchers; | ||||
|     using Catch::splitStringRef; | ||||
|     using Catch::StringRef; | ||||
|  | ||||
|     CHECK_THAT(splitStringRef("", ','), Equals(std::vector<StringRef>())); | ||||
|     CHECK_THAT(splitStringRef("abc", ','), Equals(std::vector<StringRef>{"abc"})); | ||||
|     CHECK_THAT(splitStringRef("abc,def", ','), Equals(std::vector<StringRef>{"abc", "def"})); | ||||
| } | ||||
							
								
								
									
										53
									
								
								tests/SelfTest/IntrospectiveTests/Tag.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								tests/SelfTest/IntrospectiveTests/Tag.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| /* | ||||
|  *  Created by Phil on 27/06/2014. | ||||
|  *  Copyright 2014 Two Blue Cubes Ltd. All rights reserved. | ||||
|  * | ||||
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
|  | ||||
| #include <catch2/catch.hpp> | ||||
| #include <catch2/catch_tag_alias_registry.h> | ||||
|  | ||||
| TEST_CASE( "Tag alias can be registered against tag patterns" ) { | ||||
|  | ||||
|     Catch::TagAliasRegistry registry; | ||||
|  | ||||
|     registry.add( "[@zzz]", "[one][two]", Catch::SourceLineInfo( "file", 2 ) ); | ||||
|  | ||||
|     SECTION( "The same tag alias can only be registered once" ) { | ||||
|  | ||||
|         try { | ||||
|             registry.add( "[@zzz]", "[one][two]", Catch::SourceLineInfo( "file", 10 ) ); | ||||
|             FAIL( "expected exception" ); | ||||
|         } | ||||
|         catch( std::exception& ex ) { | ||||
| #ifndef CATCH_CONFIG_DISABLE_MATCHERS | ||||
|             std::string what = ex.what(); | ||||
|             using namespace Catch::Matchers; | ||||
|             CHECK_THAT( what, Contains( "[@zzz]" ) ); | ||||
|             CHECK_THAT( what, Contains( "file" ) ); | ||||
|             CHECK_THAT( what, Contains( "2" ) ); | ||||
|             CHECK_THAT( what, Contains( "10" ) ); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION( "Tag aliases must be of the form [@name]" ) { | ||||
|         CHECK_THROWS( registry.add( "[no ampersat]", "", Catch::SourceLineInfo( "file", 3 ) ) ); | ||||
|         CHECK_THROWS( registry.add( "[the @ is not at the start]", "", Catch::SourceLineInfo( "file", 3 ) ) ); | ||||
|         CHECK_THROWS( registry.add( "@no square bracket at start]", "", Catch::SourceLineInfo( "file", 3 ) ) ); | ||||
|         CHECK_THROWS( registry.add( "[@no square bracket at end", "", Catch::SourceLineInfo( "file", 3 ) ) ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("shortened hide tags are split apart") { | ||||
|     using Catch::StringRef; | ||||
|     auto testcase = Catch::makeTestCaseInfo("", {"fake test name", "[.magic-tag]"}, CATCH_INTERNAL_LINEINFO); | ||||
|     // Transform ... | ||||
|     std::vector<StringRef> tags; | ||||
|     for (auto const& tag : testcase->tags) { | ||||
|         tags.push_back(tag.original); | ||||
|     } | ||||
|     REQUIRE_THAT(tags, Catch::VectorContains("magic-tag"_catch_sr) && Catch::VectorContains("."_catch_sr)); | ||||
| } | ||||
							
								
								
									
										42
									
								
								tests/SelfTest/IntrospectiveTests/ToString.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/SelfTest/IntrospectiveTests/ToString.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| #include <catch2/catch.hpp> | ||||
|  | ||||
| #include <catch2/catch_enum_values_registry.h> | ||||
|  | ||||
| enum class EnumClass3 { Value1, Value2, Value3, Value4 }; | ||||
|  | ||||
|  | ||||
| TEST_CASE( "parseEnums", "[Strings][enums]" ) { | ||||
|     using namespace Catch::Matchers; | ||||
|     using Catch::Detail::parseEnums; | ||||
|  | ||||
|     SECTION( "No enums" ) | ||||
|         CHECK_THAT( parseEnums( "" ), Equals( std::vector<Catch::StringRef>{} ) ); | ||||
|  | ||||
|     SECTION( "One enum value" ) { | ||||
|         CHECK_THAT( parseEnums( "ClassName::EnumName::Value1" ), | ||||
|                 Equals(std::vector<Catch::StringRef>{"Value1"} ) ); | ||||
|         CHECK_THAT( parseEnums( "Value1" ), | ||||
|                 Equals( std::vector<Catch::StringRef>{"Value1"} ) ); | ||||
|         CHECK_THAT( parseEnums( "EnumName::Value1" ), | ||||
|                 Equals(std::vector<Catch::StringRef>{"Value1"} ) ); | ||||
|     } | ||||
|  | ||||
|     SECTION( "Multiple enum values" ) { | ||||
|         CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), | ||||
|                     Equals( std::vector<Catch::StringRef>{"Value1", "Value2"} ) ); | ||||
|         CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), | ||||
|                     Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) ); | ||||
|         CHECK_THAT( parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), | ||||
|                     Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE( "Directly creating an EnumInfo" ) { | ||||
|  | ||||
|     using namespace Catch::Detail; | ||||
|     std::unique_ptr<EnumInfo> enumInfo = makeEnumInfo( "EnumName", "EnumName::Value1, EnumName::Value2", {0, 1} ); | ||||
|  | ||||
|     CHECK( enumInfo->lookup(0) == "Value1" ); | ||||
|     CHECK( enumInfo->lookup(1) == "Value2" ); | ||||
|     CHECK( enumInfo->lookup(3) == "{** unexpected enum value **}" ); | ||||
| } | ||||
							
								
								
									
										114
									
								
								tests/SelfTest/IntrospectiveTests/Xml.tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								tests/SelfTest/IntrospectiveTests/Xml.tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| #include <catch2/catch.hpp> | ||||
| #include <catch2/catch_xmlwriter.h> | ||||
|  | ||||
| #include <sstream> | ||||
|  | ||||
| inline std::string encode( std::string const& str, Catch::XmlEncode::ForWhat forWhat = Catch::XmlEncode::ForTextNodes ) { | ||||
|     std::ostringstream oss; | ||||
|     oss << Catch::XmlEncode( str, forWhat ); | ||||
|     return oss.str(); | ||||
| } | ||||
|  | ||||
| TEST_CASE( "XmlEncode", "[XML]" ) { | ||||
|     SECTION( "normal string" ) { | ||||
|         REQUIRE( encode( "normal string" ) == "normal string" ); | ||||
|     } | ||||
|     SECTION( "empty string" ) { | ||||
|         REQUIRE( encode( "" ) == "" ); | ||||
|     } | ||||
|     SECTION( "string with ampersand" ) { | ||||
|         REQUIRE( encode( "smith & jones" ) == "smith & jones" ); | ||||
|     } | ||||
|     SECTION( "string with less-than" ) { | ||||
|         REQUIRE( encode( "smith < jones" ) == "smith < jones" ); | ||||
|     } | ||||
|     SECTION( "string with greater-than" ) { | ||||
|         REQUIRE( encode( "smith > jones" ) == "smith > jones" ); | ||||
|         REQUIRE( encode( "smith ]]> jones" ) == "smith ]]> jones" ); | ||||
|     } | ||||
|     SECTION( "string with quotes" ) { | ||||
|         std::string stringWithQuotes = "don't \"quote\" me on that"; | ||||
|         REQUIRE( encode( stringWithQuotes ) == stringWithQuotes ); | ||||
|         REQUIRE( encode( stringWithQuotes, Catch::XmlEncode::ForAttributes ) == "don't "quote" me on that" ); | ||||
|     } | ||||
|     SECTION( "string with control char (1)" ) { | ||||
|         REQUIRE( encode( "[\x01]" ) == "[\\x01]" ); | ||||
|     } | ||||
|     SECTION( "string with control char (x7F)" ) { | ||||
|         REQUIRE( encode( "[\x7F]" ) == "[\\x7F]" ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Thanks to Peter Bindels (dascandy) for some of the tests | ||||
| TEST_CASE("XmlEncode: UTF-8", "[XML][UTF-8][approvals]") { | ||||
| #define ESC(lit) (char*)(lit) | ||||
|     SECTION("Valid utf-8 strings") { | ||||
|         CHECK(encode(ESC(u8"Here be 👾")) == ESC(u8"Here be 👾")); | ||||
|         CHECK(encode(ESC(u8"šš")) == ESC(u8"šš")); | ||||
|  | ||||
|         CHECK(encode("\xDF\xBF")         == "\xDF\xBF"); // 0x7FF | ||||
|         CHECK(encode("\xE0\xA0\x80")     == "\xE0\xA0\x80"); // 0x800 | ||||
|         CHECK(encode("\xED\x9F\xBF")     == "\xED\x9F\xBF"); // 0xD7FF | ||||
|         CHECK(encode("\xEE\x80\x80")     == "\xEE\x80\x80"); // 0xE000 | ||||
|         CHECK(encode("\xEF\xBF\xBF")     == "\xEF\xBF\xBF"); // 0xFFFF | ||||
|         CHECK(encode("\xF0\x90\x80\x80") == "\xF0\x90\x80\x80"); // 0x10000 | ||||
|         CHECK(encode("\xF4\x8F\xBF\xBF") == "\xF4\x8F\xBF\xBF"); // 0x10FFFF | ||||
|     } | ||||
|     SECTION("Invalid utf-8 strings") { | ||||
|         SECTION("Various broken strings") { | ||||
|             CHECK(encode(ESC("Here \xFF be \xF0\x9F\x91\xBE")) == ESC(u8"Here \\xFF be 👾")); | ||||
|             CHECK(encode("\xFF") == "\\xFF"); | ||||
|             CHECK(encode("\xC5\xC5\xA0") == ESC(u8"\\xC5Š")); | ||||
|             CHECK(encode("\xF4\x90\x80\x80") == ESC(u8"\\xF4\\x90\\x80\\x80")); // 0x110000 -- out of unicode range | ||||
|         } | ||||
|  | ||||
|         SECTION("Overlong encodings") { | ||||
|             CHECK(encode("\xC0\x80") == "\\xC0\\x80"); // \0 | ||||
|             CHECK(encode("\xF0\x80\x80\x80") == "\\xF0\\x80\\x80\\x80"); // Super-over-long \0 | ||||
|             CHECK(encode("\xC1\xBF") == "\\xC1\\xBF"); // ASCII char as UTF-8 (0x7F) | ||||
|             CHECK(encode("\xE0\x9F\xBF") == "\\xE0\\x9F\\xBF"); // 0x7FF | ||||
|             CHECK(encode("\xF0\x8F\xBF\xBF") == "\\xF0\\x8F\\xBF\\xBF"); // 0xFFFF | ||||
|         } | ||||
|  | ||||
|         // Note that we actually don't modify surrogate pairs, as we do not do strict checking | ||||
|         SECTION("Surrogate pairs") { | ||||
|             CHECK(encode("\xED\xA0\x80") == "\xED\xA0\x80"); // Invalid surrogate half 0xD800 | ||||
|             CHECK(encode("\xED\xAF\xBF") == "\xED\xAF\xBF"); // Invalid surrogate half 0xDBFF | ||||
|             CHECK(encode("\xED\xB0\x80") == "\xED\xB0\x80"); // Invalid surrogate half 0xDC00 | ||||
|             CHECK(encode("\xED\xBF\xBF") == "\xED\xBF\xBF"); // Invalid surrogate half 0xDFFF | ||||
|         } | ||||
|  | ||||
|         SECTION("Invalid start byte") { | ||||
|             CHECK(encode("\x80") == "\\x80"); | ||||
|             CHECK(encode("\x81") == "\\x81"); | ||||
|             CHECK(encode("\xBC") == "\\xBC"); | ||||
|             CHECK(encode("\xBF") == "\\xBF"); | ||||
|             // Out of range | ||||
|             CHECK(encode("\xF5\x80\x80\x80") == "\\xF5\\x80\\x80\\x80"); | ||||
|             CHECK(encode("\xF6\x80\x80\x80") == "\\xF6\\x80\\x80\\x80"); | ||||
|             CHECK(encode("\xF7\x80\x80\x80") == "\\xF7\\x80\\x80\\x80"); | ||||
|         } | ||||
|  | ||||
|         SECTION("Missing continuation byte(s)") { | ||||
|             // Missing first continuation byte | ||||
|             CHECK(encode("\xDE") == "\\xDE"); | ||||
|             CHECK(encode("\xDF") == "\\xDF"); | ||||
|             CHECK(encode("\xE0") == "\\xE0"); | ||||
|             CHECK(encode("\xEF") == "\\xEF"); | ||||
|             CHECK(encode("\xF0") == "\\xF0"); | ||||
|             CHECK(encode("\xF4") == "\\xF4"); | ||||
|  | ||||
|             // Missing second continuation byte | ||||
|             CHECK(encode("\xE0\x80") == "\\xE0\\x80"); | ||||
|             CHECK(encode("\xE0\xBF") == "\\xE0\\xBF"); | ||||
|             CHECK(encode("\xE1\x80") == "\\xE1\\x80"); | ||||
|             CHECK(encode("\xF0\x80") == "\\xF0\\x80"); | ||||
|             CHECK(encode("\xF4\x80") == "\\xF4\\x80"); | ||||
|  | ||||
|             // Missing third continuation byte | ||||
|             CHECK(encode("\xF0\x80\x80") == "\\xF0\\x80\\x80"); | ||||
|             CHECK(encode("\xF4\x80\x80") == "\\xF4\\x80\\x80"); | ||||
|         } | ||||
|     } | ||||
| #undef ESC | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský