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