1 /* 2 * Created by Phil on 14/08/2012. 3 * Copyright 2012 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 9 #include "catch_test_case_info.h" 10 #include "catch_enforce.h" 11 #include "catch_test_spec.h" 12 #include "catch_interfaces_testcase.h" 13 #include "catch_string_manip.h" 14 15 #include <cctype> 16 #include <exception> 17 #include <algorithm> 18 #include <sstream> 19 20 namespace Catch { 21 22 namespace { parseSpecialTag(std::string const & tag)23 TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { 24 if( startsWith( tag, '.' ) || 25 tag == "!hide" ) 26 return TestCaseInfo::IsHidden; 27 else if( tag == "!throws" ) 28 return TestCaseInfo::Throws; 29 else if( tag == "!shouldfail" ) 30 return TestCaseInfo::ShouldFail; 31 else if( tag == "!mayfail" ) 32 return TestCaseInfo::MayFail; 33 else if( tag == "!nonportable" ) 34 return TestCaseInfo::NonPortable; 35 else if( tag == "!benchmark" ) 36 return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); 37 else 38 return TestCaseInfo::None; 39 } isReservedTag(std::string const & tag)40 bool isReservedTag( std::string const& tag ) { 41 return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) ); 42 } enforceNotReservedTag(std::string const & tag,SourceLineInfo const & _lineInfo)43 void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { 44 CATCH_ENFORCE( !isReservedTag(tag), 45 "Tag name: [" << tag << "] is not allowed.\n" 46 << "Tag names starting with non alphanumeric characters are reserved\n" 47 << _lineInfo ); 48 } 49 } 50 makeTestCase(ITestInvoker * _testCase,std::string const & _className,NameAndTags const & nameAndTags,SourceLineInfo const & _lineInfo)51 TestCase makeTestCase( ITestInvoker* _testCase, 52 std::string const& _className, 53 NameAndTags const& nameAndTags, 54 SourceLineInfo const& _lineInfo ) 55 { 56 bool isHidden = false; 57 58 // Parse out tags 59 std::vector<std::string> tags; 60 std::string desc, tag; 61 bool inTag = false; 62 for (char c : nameAndTags.tags) { 63 if( !inTag ) { 64 if( c == '[' ) 65 inTag = true; 66 else 67 desc += c; 68 } 69 else { 70 if( c == ']' ) { 71 TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); 72 if( ( prop & TestCaseInfo::IsHidden ) != 0 ) 73 isHidden = true; 74 else if( prop == TestCaseInfo::None ) 75 enforceNotReservedTag( tag, _lineInfo ); 76 77 // Merged hide tags like `[.approvals]` should be added as 78 // `[.][approvals]`. The `[.]` is added at later point, so 79 // we only strip the prefix 80 if (startsWith(tag, '.') && tag.size() > 1) { 81 tag.erase(0, 1); 82 } 83 tags.push_back( tag ); 84 tag.clear(); 85 inTag = false; 86 } 87 else 88 tag += c; 89 } 90 } 91 if( isHidden ) { 92 tags.push_back( "." ); 93 } 94 95 TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo ); 96 return TestCase( _testCase, std::move(info) ); 97 } 98 setTags(TestCaseInfo & testCaseInfo,std::vector<std::string> tags)99 void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { 100 std::sort(begin(tags), end(tags)); 101 tags.erase(std::unique(begin(tags), end(tags)), end(tags)); 102 testCaseInfo.lcaseTags.clear(); 103 104 for( auto const& tag : tags ) { 105 std::string lcaseTag = toLower( tag ); 106 testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); 107 testCaseInfo.lcaseTags.push_back( lcaseTag ); 108 } 109 testCaseInfo.tags = std::move(tags); 110 } 111 TestCaseInfo(std::string const & _name,std::string const & _className,std::string const & _description,std::vector<std::string> const & _tags,SourceLineInfo const & _lineInfo)112 TestCaseInfo::TestCaseInfo( std::string const& _name, 113 std::string const& _className, 114 std::string const& _description, 115 std::vector<std::string> const& _tags, 116 SourceLineInfo const& _lineInfo ) 117 : name( _name ), 118 className( _className ), 119 description( _description ), 120 lineInfo( _lineInfo ), 121 properties( None ) 122 { 123 setTags( *this, _tags ); 124 } 125 isHidden() const126 bool TestCaseInfo::isHidden() const { 127 return ( properties & IsHidden ) != 0; 128 } throws() const129 bool TestCaseInfo::throws() const { 130 return ( properties & Throws ) != 0; 131 } okToFail() const132 bool TestCaseInfo::okToFail() const { 133 return ( properties & (ShouldFail | MayFail ) ) != 0; 134 } expectedToFail() const135 bool TestCaseInfo::expectedToFail() const { 136 return ( properties & (ShouldFail ) ) != 0; 137 } 138 tagsAsString() const139 std::string TestCaseInfo::tagsAsString() const { 140 std::string ret; 141 // '[' and ']' per tag 142 std::size_t full_size = 2 * tags.size(); 143 for (const auto& tag : tags) { 144 full_size += tag.size(); 145 } 146 ret.reserve(full_size); 147 for (const auto& tag : tags) { 148 ret.push_back('['); 149 ret.append(tag); 150 ret.push_back(']'); 151 } 152 153 return ret; 154 } 155 156 TestCase(ITestInvoker * testCase,TestCaseInfo && info)157 TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} 158 159 withName(std::string const & _newName) const160 TestCase TestCase::withName( std::string const& _newName ) const { 161 TestCase other( *this ); 162 other.name = _newName; 163 return other; 164 } 165 invoke() const166 void TestCase::invoke() const { 167 test->invoke(); 168 } 169 operator ==(TestCase const & other) const170 bool TestCase::operator == ( TestCase const& other ) const { 171 return test.get() == other.test.get() && 172 name == other.name && 173 className == other.className; 174 } 175 operator <(TestCase const & other) const176 bool TestCase::operator < ( TestCase const& other ) const { 177 return name < other.name; 178 } 179 getTestCaseInfo() const180 TestCaseInfo const& TestCase::getTestCaseInfo() const 181 { 182 return *this; 183 } 184 185 } // end namespace Catch 186