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 #ifndef TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED 9 #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED 10 11 #include "catch_test_spec.hpp" 12 #include "catch_test_case_info.h" 13 #include "catch_interfaces_testcase.h" 14 #include "catch_common.h" 15 16 #include <cctype> 17 18 namespace Catch { 19 parseSpecialTag(std::string const & tag)20 inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { 21 if( startsWith( tag, '.' ) || 22 tag == "hide" || 23 tag == "!hide" ) 24 return TestCaseInfo::IsHidden; 25 else if( tag == "!throws" ) 26 return TestCaseInfo::Throws; 27 else if( tag == "!shouldfail" ) 28 return TestCaseInfo::ShouldFail; 29 else if( tag == "!mayfail" ) 30 return TestCaseInfo::MayFail; 31 else if( tag == "!nonportable" ) 32 return TestCaseInfo::NonPortable; 33 else 34 return TestCaseInfo::None; 35 } isReservedTag(std::string const & tag)36 inline bool isReservedTag( std::string const& tag ) { 37 return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); 38 } enforceNotReservedTag(std::string const & tag,SourceLineInfo const & _lineInfo)39 inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { 40 if( isReservedTag( tag ) ) { 41 std::ostringstream ss; 42 ss << Colour(Colour::Red) 43 << "Tag name [" << tag << "] not allowed.\n" 44 << "Tag names starting with non alpha-numeric characters are reserved\n" 45 << Colour(Colour::FileName) 46 << _lineInfo << '\n'; 47 throw std::runtime_error(ss.str()); 48 } 49 } 50 makeTestCase(ITestCase * _testCase,std::string const & _className,std::string const & _name,std::string const & _descOrTags,SourceLineInfo const & _lineInfo)51 TestCase makeTestCase( ITestCase* _testCase, 52 std::string const& _className, 53 std::string const& _name, 54 std::string const& _descOrTags, 55 SourceLineInfo const& _lineInfo ) 56 { 57 bool isHidden( startsWith( _name, "./" ) ); // Legacy support 58 59 // Parse out tags 60 std::set<std::string> tags; 61 std::string desc, tag; 62 bool inTag = false; 63 for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { 64 char c = _descOrTags[i]; 65 if( !inTag ) { 66 if( c == '[' ) 67 inTag = true; 68 else 69 desc += c; 70 } 71 else { 72 if( c == ']' ) { 73 TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); 74 if( prop == TestCaseInfo::IsHidden ) 75 isHidden = true; 76 else if( prop == TestCaseInfo::None ) 77 enforceNotReservedTag( tag, _lineInfo ); 78 79 tags.insert( tag ); 80 tag.clear(); 81 inTag = false; 82 } 83 else 84 tag += c; 85 } 86 } 87 if( isHidden ) { 88 tags.insert( "hide" ); 89 tags.insert( "." ); 90 } 91 92 TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); 93 return TestCase( _testCase, info ); 94 } 95 setTags(TestCaseInfo & testCaseInfo,std::set<std::string> const & tags)96 void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags ) 97 { 98 testCaseInfo.tags = tags; 99 testCaseInfo.lcaseTags.clear(); 100 101 std::ostringstream oss; 102 for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { 103 oss << '[' << *it << ']'; 104 std::string lcaseTag = toLower( *it ); 105 testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); 106 testCaseInfo.lcaseTags.insert( lcaseTag ); 107 } 108 testCaseInfo.tagsAsString = oss.str(); 109 } 110 TestCaseInfo(std::string const & _name,std::string const & _className,std::string const & _description,std::set<std::string> const & _tags,SourceLineInfo const & _lineInfo)111 TestCaseInfo::TestCaseInfo( std::string const& _name, 112 std::string const& _className, 113 std::string const& _description, 114 std::set<std::string> const& _tags, 115 SourceLineInfo const& _lineInfo ) 116 : name( _name ), 117 className( _className ), 118 description( _description ), 119 lineInfo( _lineInfo ), 120 properties( None ) 121 { 122 setTags( *this, _tags ); 123 } 124 TestCaseInfo(TestCaseInfo const & other)125 TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) 126 : name( other.name ), 127 className( other.className ), 128 description( other.description ), 129 tags( other.tags ), 130 lcaseTags( other.lcaseTags ), 131 tagsAsString( other.tagsAsString ), 132 lineInfo( other.lineInfo ), 133 properties( other.properties ) 134 {} 135 isHidden() const136 bool TestCaseInfo::isHidden() const { 137 return ( properties & IsHidden ) != 0; 138 } throws() const139 bool TestCaseInfo::throws() const { 140 return ( properties & Throws ) != 0; 141 } okToFail() const142 bool TestCaseInfo::okToFail() const { 143 return ( properties & (ShouldFail | MayFail ) ) != 0; 144 } expectedToFail() const145 bool TestCaseInfo::expectedToFail() const { 146 return ( properties & (ShouldFail ) ) != 0; 147 } 148 149 TestCase(ITestCase * testCase,TestCaseInfo const & info)150 TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} 151 TestCase(TestCase const & other)152 TestCase::TestCase( TestCase const& other ) 153 : TestCaseInfo( other ), 154 test( other.test ) 155 {} 156 withName(std::string const & _newName) const157 TestCase TestCase::withName( std::string const& _newName ) const { 158 TestCase other( *this ); 159 other.name = _newName; 160 return other; 161 } 162 swap(TestCase & other)163 void TestCase::swap( TestCase& other ) { 164 test.swap( other.test ); 165 name.swap( other.name ); 166 className.swap( other.className ); 167 description.swap( other.description ); 168 tags.swap( other.tags ); 169 lcaseTags.swap( other.lcaseTags ); 170 tagsAsString.swap( other.tagsAsString ); 171 std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties ); 172 std::swap( lineInfo, other.lineInfo ); 173 } 174 invoke() const175 void TestCase::invoke() const { 176 test->invoke(); 177 } 178 operator ==(TestCase const & other) const179 bool TestCase::operator == ( TestCase const& other ) const { 180 return test.get() == other.test.get() && 181 name == other.name && 182 className == other.className; 183 } 184 operator <(TestCase const & other) const185 bool TestCase::operator < ( TestCase const& other ) const { 186 return name < other.name; 187 } operator =(TestCase const & other)188 TestCase& TestCase::operator = ( TestCase const& other ) { 189 TestCase temp( other ); 190 swap( temp ); 191 return *this; 192 } 193 getTestCaseInfo() const194 TestCaseInfo const& TestCase::getTestCaseInfo() const 195 { 196 return *this; 197 } 198 199 } // end namespace Catch 200 201 #endif // TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED 202