Added AllOf and AnyOf matchers

This commit is contained in:
Phil Nash 2012-10-12 07:58:17 +01:00
parent 9902ac9f1a
commit 9444bbcb7b
7 changed files with 345 additions and 87 deletions

View File

@ -56,14 +56,14 @@ public:
ExpressionBuilder& acceptMatcher( const MatcherT& matcher, ExpressionBuilder& acceptMatcher( const MatcherT& matcher,
const ArgT& arg, const ArgT& arg,
const std::string& matcherCallAsString ) { const std::string& matcherCallAsString ) {
std::string matcherAsString = Catch::toString( matcher ); std::string matcherAsString = matcher.toString();
if( matcherAsString == "{?}" ) if( matcherAsString == "{?}" )
matcherAsString = matcherCallAsString; matcherAsString = matcherCallAsString;
m_result m_result
.setLhs( Catch::toString( arg ) ) .setLhs( Catch::toString( arg ) )
.setRhs( matcherAsString ) .setRhs( matcherAsString )
.setOp( "matches" ) .setOp( "matches" )
.setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed );
return *this; return *this;
} }
@ -71,14 +71,14 @@ public:
ExpressionBuilder& acceptMatcher( const MatcherT& matcher, ExpressionBuilder& acceptMatcher( const MatcherT& matcher,
ArgT* arg, ArgT* arg,
const std::string& matcherCallAsString ) { const std::string& matcherCallAsString ) {
std::string matcherAsString = Catch::toString( matcher ); std::string matcherAsString = matcher.toString();
if( matcherAsString == "{?}" ) if( matcherAsString == "{?}" )
matcherAsString = matcherCallAsString; matcherAsString = matcherCallAsString;
m_result m_result
.setLhs( Catch::toString( arg ) ) .setLhs( Catch::toString( arg ) )
.setRhs( matcherAsString ) .setRhs( matcherAsString )
.setOp( "matches" ) .setOp( "matches" )
.setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed );
return *this; return *this;
} }

View File

@ -60,6 +60,11 @@ namespace Catch {
TagExtracter::~TagExtracter() {} TagExtracter::~TagExtracter() {}
TagExpressionParser::~TagExpressionParser() {} TagExpressionParser::~TagExpressionParser() {}
Matchers::Impl::StdString::Equals::~Equals() {}
Matchers::Impl::StdString::Contains::~Contains() {}
Matchers::Impl::StdString::StartsWith::~StartsWith() {}
Matchers::Impl::StdString::EndsWith::~EndsWith() {}
void Config::dummy() {} void Config::dummy() {}
INTERNAL_CATCH_REGISTER_REPORTER( "basic", BasicReporter ) INTERNAL_CATCH_REGISTER_REPORTER( "basic", BasicReporter )

View File

@ -11,74 +11,189 @@
namespace Catch { namespace Catch {
namespace Matchers { namespace Matchers {
namespace Impl { namespace Impl {
template<typename ExpressionT>
struct Matcher : SharedImpl<IShared>
{
virtual ~Matcher() {}
virtual Ptr<Matcher> clone() const = 0;
virtual bool match( const ExpressionT& expr ) const = 0;
virtual std::string toString() const = 0;
};
template<typename DerivedT, typename ExpressionT>
struct MatcherImpl : Matcher<ExpressionT> {
virtual Ptr<Matcher<ExpressionT> > clone() const {
return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<const DerivedT&>( *this ) ) );
}
};
namespace Generic {
template<typename ExpressionT>
class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
public:
AllOf() {}
AllOf( const AllOf& other ) : m_matchers( other.m_matchers ) {}
AllOf& add( const Matcher<ExpressionT>& matcher ) {
m_matchers.push_back( matcher.clone() );
return *this;
}
virtual bool match( const ExpressionT& expr ) const
{
for( std::size_t i = 0; i < m_matchers.size(); ++i )
if( !m_matchers[i]->match( expr ) )
return false;
return true;
}
virtual std::string toString() const {
std::ostringstream oss;
oss << "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 )
oss << " and ";
oss << m_matchers[i]->toString();
}
oss << " )";
return oss.str();
}
private:
std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
};
template<typename ExpressionT>
class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
public:
AnyOf() {}
AnyOf( const AnyOf& other ) : m_matchers( other.m_matchers ) {}
AnyOf& add( const Matcher<ExpressionT>& matcher ) {
m_matchers.push_back( matcher.clone() );
return *this;
}
virtual bool match( const ExpressionT& expr ) const
{
for( std::size_t i = 0; i < m_matchers.size(); ++i )
if( m_matchers[i]->match( expr ) )
return true;
return false;
}
virtual std::string toString() const {
std::ostringstream oss;
oss << "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 )
oss << " or ";
oss << m_matchers[i]->toString();
}
oss << " )";
return oss.str();
}
private:
std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
};
}
namespace StdString { namespace StdString {
struct Equals { struct Equals : MatcherImpl<Equals, std::string> {
Equals( const std::string& str ) : m_str( str ){} Equals( const std::string& str ) : m_str( str ){}
Equals( const Equals& other ) : m_str( other.m_str ){}
bool operator()( const std::string& str ) const
{ virtual ~Equals();
return str == m_str;
virtual bool match( const std::string& expr ) const {
return m_str == expr;
} }
virtual std::string toString() const {
friend std::ostream& operator<<( std::ostream& os, const Equals& matcher ) return "equals: \"" + m_str + "\"";
{
os << "equals: \"" << matcher.m_str << "\"";
return os;
} }
std::string m_str; std::string m_str;
}; };
struct Contains { struct Contains : MatcherImpl<Contains, std::string> {
Contains( const std::string& substr ) : m_substr( substr ){} Contains( const std::string& substr ) : m_substr( substr ){}
Contains( const Contains& other ) : m_substr( other.m_substr ){}
bool operator()( const std::string& str ) const
{ virtual ~Contains();
return str.find( m_substr ) != std::string::npos;
virtual bool match( const std::string& expr ) const {
return expr.find( m_substr ) != std::string::npos;
} }
virtual std::string toString() const {
friend std::ostream& operator<<( std::ostream& os, const Contains& matcher ) return "contains: \"" + m_substr + "\"";
{
os << "contains: \"" << matcher.m_substr << "\"";
return os;
} }
std::string m_substr; std::string m_substr;
}; };
struct StartsWith { struct StartsWith : MatcherImpl<StartsWith, std::string> {
StartsWith( const std::string& substr ) : m_substr( substr ){} StartsWith( const std::string& substr ) : m_substr( substr ){}
StartsWith( const StartsWith& other ) : m_substr( other.m_substr ){}
bool operator()( const std::string& str ) const
{ virtual ~StartsWith();
return str.find( m_substr ) == 0;
virtual bool match( const std::string& expr ) const {
return expr.find( m_substr ) == 0;
} }
virtual std::string toString() const {
friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher ) return "starts with: \"" + m_substr + "\"";
{
os << "starts with: \"" << matcher.m_substr << "\"";
return os;
} }
std::string m_substr; std::string m_substr;
}; };
struct EndsWith { struct EndsWith : MatcherImpl<EndsWith, std::string> {
EndsWith( const std::string& substr ) : m_substr( substr ){} EndsWith( const std::string& substr ) : m_substr( substr ){}
EndsWith( const EndsWith& other ) : m_substr( other.m_substr ){}
bool operator()( const std::string& str ) const
{ virtual ~EndsWith();
return str.find( m_substr ) == str.size() - m_substr.size();
virtual bool match( const std::string& expr ) const {
return expr.find( m_substr ) == expr.size() - m_substr.size();
} }
virtual std::string toString() const {
friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher ) return "ends with: \"" + m_substr + "\"";
{
os << "ends with: \"" << matcher.m_substr << "\"";
return os;
} }
std::string m_substr; std::string m_substr;
}; };
} // namespace StdString } // namespace StdString
} // namespace Impl } // namespace Impl
// The following functions create the actual matcher objects.
// This allows the types to be inferred
template<typename ExpressionT>
inline Impl::Generic::AllOf<ExpressionT> AllOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2 ) {
return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
}
template<typename ExpressionT>
inline Impl::Generic::AllOf<ExpressionT> AllOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2,
const Impl::Matcher<ExpressionT>& m3 ) {
return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
}
template<typename ExpressionT>
inline Impl::Generic::AnyOf<ExpressionT> AnyOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2 ) {
return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
}
template<typename ExpressionT>
inline Impl::Generic::AnyOf<ExpressionT> AnyOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2,
const Impl::Matcher<ExpressionT>& m3 ) {
return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
}
inline Impl::StdString::Equals Equals( const std::string& str ){ return Impl::StdString::Equals( str ); } inline Impl::StdString::Equals Equals( const std::string& str ){ return Impl::StdString::Equals( str ); }
inline Impl::StdString::Contains Contains( const std::string& substr ){ return Impl::StdString::Contains( substr ); } inline Impl::StdString::Contains Contains( const std::string& substr ){ return Impl::StdString::Contains( substr ); }
inline Impl::StdString::StartsWith StartsWith( const std::string& substr ){ return Impl::StdString::StartsWith( substr ); } inline Impl::StdString::StartsWith StartsWith( const std::string& substr ){ return Impl::StdString::StartsWith( substr ); }

View File

@ -78,7 +78,7 @@ namespace Catch {
struct SharedImpl : T { struct SharedImpl : T {
SharedImpl() : m_rc( 0 ){} SharedImpl() : m_rc( 0 ){}
virtual void addRef(){ virtual void addRef(){
++m_rc; ++m_rc;
} }

View File

@ -234,6 +234,10 @@ namespace Catch {
if( resultInfo.hasExpandedExpression() ) { if( resultInfo.hasExpandedExpression() ) {
m_config.stream << " for: "; m_config.stream << " for: ";
if( resultInfo.getExpandedExpression().size() > 40 )
m_config.stream << "\n";
if( resultInfo.getExpandedExpression().size() < 70 )
m_config.stream << "\t";
TextColour colour( TextColour::ReconstructedExpression ); TextColour colour( TextColour::ReconstructedExpression );
m_config.stream << resultInfo.getExpandedExpression(); m_config.stream << resultInfo.getExpandedExpression();
} }

View File

@ -270,6 +270,16 @@ TEST_CASE("./failing/matchers/Equals", "")
CHECK_THAT( testStringForMatching(), Equals( "something else" ) ); CHECK_THAT( testStringForMatching(), Equals( "something else" ) );
} }
TEST_CASE("/succeeding/matchers/AllOf", "")
{
CHECK_THAT( testStringForMatching(), AllOf( Catch::Contains( "string" ), Catch::Contains( "abc" ) ) );
}
TEST_CASE("/succeeding/matchers/AnyOf", "")
{
CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "string" ), Catch::Contains( "not there" ) ) );
CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "not there" ), Catch::Contains( "string" ) ) );
}
TEST_CASE("./succeeding/matchers/Equals", "") TEST_CASE("./succeeding/matchers/Equals", "")
{ {
CHECK_THAT( testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) ); CHECK_THAT( testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) );

View File

@ -1,5 +1,5 @@
/* /*
* Generated: 2012-10-09 20:58:02.234458 * Generated: 2012-10-12 07:57:48.487873
* ---------------------------------------------------------- * ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly * This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@ -1041,14 +1041,14 @@ public:
ExpressionBuilder& acceptMatcher( const MatcherT& matcher, ExpressionBuilder& acceptMatcher( const MatcherT& matcher,
const ArgT& arg, const ArgT& arg,
const std::string& matcherCallAsString ) { const std::string& matcherCallAsString ) {
std::string matcherAsString = Catch::toString( matcher ); std::string matcherAsString = matcher.toString();
if( matcherAsString == "{?}" ) if( matcherAsString == "{?}" )
matcherAsString = matcherCallAsString; matcherAsString = matcherCallAsString;
m_result m_result
.setLhs( Catch::toString( arg ) ) .setLhs( Catch::toString( arg ) )
.setRhs( matcherAsString ) .setRhs( matcherAsString )
.setOp( "matches" ) .setOp( "matches" )
.setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed );
return *this; return *this;
} }
@ -1056,14 +1056,14 @@ public:
ExpressionBuilder& acceptMatcher( const MatcherT& matcher, ExpressionBuilder& acceptMatcher( const MatcherT& matcher,
ArgT* arg, ArgT* arg,
const std::string& matcherCallAsString ) { const std::string& matcherCallAsString ) {
std::string matcherAsString = Catch::toString( matcher ); std::string matcherAsString = matcher.toString();
if( matcherAsString == "{?}" ) if( matcherAsString == "{?}" )
matcherAsString = matcherCallAsString; matcherAsString = matcherCallAsString;
m_result m_result
.setLhs( Catch::toString( arg ) ) .setLhs( Catch::toString( arg ) )
.setRhs( matcherAsString ) .setRhs( matcherAsString )
.setOp( "matches" ) .setOp( "matches" )
.setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed );
return *this; return *this;
} }
@ -2521,74 +2521,189 @@ inline std::string toString<Detail::Approx>( const Detail::Approx& value ) {
namespace Catch { namespace Catch {
namespace Matchers { namespace Matchers {
namespace Impl { namespace Impl {
template<typename ExpressionT>
struct Matcher : SharedImpl<IShared>
{
virtual ~Matcher() {}
virtual Ptr<Matcher> clone() const = 0;
virtual bool match( const ExpressionT& expr ) const = 0;
virtual std::string toString() const = 0;
};
template<typename DerivedT, typename ExpressionT>
struct MatcherImpl : Matcher<ExpressionT> {
virtual Ptr<Matcher<ExpressionT> > clone() const {
return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<const DerivedT&>( *this ) ) );
}
};
namespace Generic {
template<typename ExpressionT>
class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
public:
AllOf() {}
AllOf( const AllOf& other ) : m_matchers( other.m_matchers ) {}
AllOf& add( const Matcher<ExpressionT>& matcher ) {
m_matchers.push_back( matcher.clone() );
return *this;
}
virtual bool match( const ExpressionT& expr ) const
{
for( std::size_t i = 0; i < m_matchers.size(); ++i )
if( !m_matchers[i]->match( expr ) )
return false;
return true;
}
virtual std::string toString() const {
std::ostringstream oss;
oss << "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 )
oss << " and ";
oss << m_matchers[i]->toString();
}
oss << " )";
return oss.str();
}
private:
std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
};
template<typename ExpressionT>
class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
public:
AnyOf() {}
AnyOf( const AnyOf& other ) : m_matchers( other.m_matchers ) {}
AnyOf& add( const Matcher<ExpressionT>& matcher ) {
m_matchers.push_back( matcher.clone() );
return *this;
}
virtual bool match( const ExpressionT& expr ) const
{
for( std::size_t i = 0; i < m_matchers.size(); ++i )
if( m_matchers[i]->match( expr ) )
return true;
return false;
}
virtual std::string toString() const {
std::ostringstream oss;
oss << "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 )
oss << " or ";
oss << m_matchers[i]->toString();
}
oss << " )";
return oss.str();
}
private:
std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
};
}
namespace StdString { namespace StdString {
struct Equals { struct Equals : MatcherImpl<Equals, std::string> {
Equals( const std::string& str ) : m_str( str ){} Equals( const std::string& str ) : m_str( str ){}
Equals( const Equals& other ) : m_str( other.m_str ){}
bool operator()( const std::string& str ) const virtual ~Equals();
{
return str == m_str; virtual bool match( const std::string& expr ) const {
return m_str == expr;
}
virtual std::string toString() const {
return "equals: \"" + m_str + "\"";
} }
friend std::ostream& operator<<( std::ostream& os, const Equals& matcher )
{
os << "equals: \"" << matcher.m_str << "\"";
return os;
}
std::string m_str; std::string m_str;
}; };
struct Contains { struct Contains : MatcherImpl<Contains, std::string> {
Contains( const std::string& substr ) : m_substr( substr ){} Contains( const std::string& substr ) : m_substr( substr ){}
Contains( const Contains& other ) : m_substr( other.m_substr ){}
bool operator()( const std::string& str ) const virtual ~Contains();
{
return str.find( m_substr ) != std::string::npos; virtual bool match( const std::string& expr ) const {
return expr.find( m_substr ) != std::string::npos;
}
virtual std::string toString() const {
return "contains: \"" + m_substr + "\"";
} }
friend std::ostream& operator<<( std::ostream& os, const Contains& matcher )
{
os << "contains: \"" << matcher.m_substr << "\"";
return os;
}
std::string m_substr; std::string m_substr;
}; };
struct StartsWith { struct StartsWith : MatcherImpl<StartsWith, std::string> {
StartsWith( const std::string& substr ) : m_substr( substr ){} StartsWith( const std::string& substr ) : m_substr( substr ){}
StartsWith( const StartsWith& other ) : m_substr( other.m_substr ){}
bool operator()( const std::string& str ) const virtual ~StartsWith();
{
return str.find( m_substr ) == 0; virtual bool match( const std::string& expr ) const {
return expr.find( m_substr ) == 0;
}
virtual std::string toString() const {
return "starts with: \"" + m_substr + "\"";
} }
friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher )
{
os << "starts with: \"" << matcher.m_substr << "\"";
return os;
}
std::string m_substr; std::string m_substr;
}; };
struct EndsWith { struct EndsWith : MatcherImpl<EndsWith, std::string> {
EndsWith( const std::string& substr ) : m_substr( substr ){} EndsWith( const std::string& substr ) : m_substr( substr ){}
EndsWith( const EndsWith& other ) : m_substr( other.m_substr ){}
bool operator()( const std::string& str ) const virtual ~EndsWith();
{
return str.find( m_substr ) == str.size() - m_substr.size(); virtual bool match( const std::string& expr ) const {
return expr.find( m_substr ) == expr.size() - m_substr.size();
}
virtual std::string toString() const {
return "ends with: \"" + m_substr + "\"";
} }
friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher )
{
os << "ends with: \"" << matcher.m_substr << "\"";
return os;
}
std::string m_substr; std::string m_substr;
}; };
} // namespace StdString } // namespace StdString
} // namespace Impl } // namespace Impl
// The following functions create the actual matcher objects.
// This allows the types to be inferred
template<typename ExpressionT>
inline Impl::Generic::AllOf<ExpressionT> AllOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2 ) {
return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
}
template<typename ExpressionT>
inline Impl::Generic::AllOf<ExpressionT> AllOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2,
const Impl::Matcher<ExpressionT>& m3 ) {
return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
}
template<typename ExpressionT>
inline Impl::Generic::AnyOf<ExpressionT> AnyOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2 ) {
return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
}
template<typename ExpressionT>
inline Impl::Generic::AnyOf<ExpressionT> AnyOf( const Impl::Matcher<ExpressionT>& m1,
const Impl::Matcher<ExpressionT>& m2,
const Impl::Matcher<ExpressionT>& m3 ) {
return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
}
inline Impl::StdString::Equals Equals( const std::string& str ){ return Impl::StdString::Equals( str ); } inline Impl::StdString::Equals Equals( const std::string& str ){ return Impl::StdString::Equals( str ); }
inline Impl::StdString::Contains Contains( const std::string& substr ){ return Impl::StdString::Contains( substr ); } inline Impl::StdString::Contains Contains( const std::string& substr ){ return Impl::StdString::Contains( substr ); }
inline Impl::StdString::StartsWith StartsWith( const std::string& substr ){ return Impl::StdString::StartsWith( substr ); } inline Impl::StdString::StartsWith StartsWith( const std::string& substr ){ return Impl::StdString::StartsWith( substr ); }
@ -5374,6 +5489,10 @@ namespace Catch {
if( resultInfo.hasExpandedExpression() ) { if( resultInfo.hasExpandedExpression() ) {
m_config.stream << " for: "; m_config.stream << " for: ";
if( resultInfo.getExpandedExpression().size() > 40 )
m_config.stream << "\n";
if( resultInfo.getExpandedExpression().size() < 70 )
m_config.stream << "\t";
TextColour colour( TextColour::ReconstructedExpression ); TextColour colour( TextColour::ReconstructedExpression );
m_config.stream << resultInfo.getExpandedExpression(); m_config.stream << resultInfo.getExpandedExpression();
} }
@ -6045,6 +6164,11 @@ namespace Catch {
TagExtracter::~TagExtracter() {} TagExtracter::~TagExtracter() {}
TagExpressionParser::~TagExpressionParser() {} TagExpressionParser::~TagExpressionParser() {}
Matchers::Impl::StdString::Equals::~Equals() {}
Matchers::Impl::StdString::Contains::~Contains() {}
Matchers::Impl::StdString::StartsWith::~StartsWith() {}
Matchers::Impl::StdString::EndsWith::~EndsWith() {}
void Config::dummy() {} void Config::dummy() {}
INTERNAL_CATCH_REGISTER_REPORTER( "basic", BasicReporter ) INTERNAL_CATCH_REGISTER_REPORTER( "basic", BasicReporter )