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 alpha-numeric 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 std::string _descOrTags = nameAndTags.tags; 63 for (char c : _descOrTags) { 64 if( !inTag ) { 65 if( c == '[' ) 66 inTag = true; 67 else 68 desc += c; 69 } 70 else { 71 if( c == ']' ) { 72 TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); 73 if( ( prop & TestCaseInfo::IsHidden ) != 0 ) 74 isHidden = true; 75 else if( prop == TestCaseInfo::None ) 76 enforceNotReservedTag( tag, _lineInfo ); 77 78 tags.push_back( tag ); 79 tag.clear(); 80 inTag = false; 81 } 82 else 83 tag += c; 84 } 85 } 86 if( isHidden ) { 87 tags.push_back( "." ); 88 } 89 90 TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); 91 return TestCase( _testCase, std::move(info) ); 92 } 93 setTags(TestCaseInfo & testCaseInfo,std::vector<std::string> tags)94 void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { 95 std::sort(begin(tags), end(tags)); 96 tags.erase(std::unique(begin(tags), end(tags)), end(tags)); 97 testCaseInfo.lcaseTags.clear(); 98 99 for( auto const& tag : tags ) { 100 std::string lcaseTag = toLower( tag ); 101 testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); 102 testCaseInfo.lcaseTags.push_back( lcaseTag ); 103 } 104 testCaseInfo.tags = std::move(tags); 105 } 106 TestCaseInfo(std::string const & _name,std::string const & _className,std::string const & _description,std::vector<std::string> const & _tags,SourceLineInfo const & _lineInfo)107 TestCaseInfo::TestCaseInfo( std::string const& _name, 108 std::string const& _className, 109 std::string const& _description, 110 std::vector<std::string> const& _tags, 111 SourceLineInfo const& _lineInfo ) 112 : name( _name ), 113 className( _className ), 114 description( _description ), 115 lineInfo( _lineInfo ), 116 properties( None ) 117 { 118 setTags( *this, _tags ); 119 } 120 isHidden() const121 bool TestCaseInfo::isHidden() const { 122 return ( properties & IsHidden ) != 0; 123 } throws() const124 bool TestCaseInfo::throws() const { 125 return ( properties & Throws ) != 0; 126 } okToFail() const127 bool TestCaseInfo::okToFail() const { 128 return ( properties & (ShouldFail | MayFail ) ) != 0; 129 } expectedToFail() const130 bool TestCaseInfo::expectedToFail() const { 131 return ( properties & (ShouldFail ) ) != 0; 132 } 133 tagsAsString() const134 std::string TestCaseInfo::tagsAsString() const { 135 std::string ret; 136 // '[' and ']' per tag 137 std::size_t full_size = 2 * tags.size(); 138 for (const auto& tag : tags) { 139 full_size += tag.size(); 140 } 141 ret.reserve(full_size); 142 for (const auto& tag : tags) { 143 ret.push_back('['); 144 ret.append(tag); 145 ret.push_back(']'); 146 } 147 148 return ret; 149 } 150 151 TestCase(ITestInvoker * testCase,TestCaseInfo && info)152 TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} 153 154 withName(std::string const & _newName) const155 TestCase TestCase::withName( std::string const& _newName ) const { 156 TestCase other( *this ); 157 other.name = _newName; 158 return other; 159 } 160 invoke() const161 void TestCase::invoke() const { 162 test->invoke(); 163 } 164 operator ==(TestCase const & other) const165 bool TestCase::operator == ( TestCase const& other ) const { 166 return test.get() == other.test.get() && 167 name == other.name && 168 className == other.className; 169 } 170 operator <(TestCase const & other) const171 bool TestCase::operator < ( TestCase const& other ) const { 172 return name < other.name; 173 } 174 getTestCaseInfo() const175 TestCaseInfo const& TestCase::getTestCaseInfo() const 176 { 177 return *this; 178 } 179 180 } // end namespace Catch 181