mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-23 03:43:28 +01:00
Started new reporter, "console", which will replace "basic" when done.
Introduced Option template as part of this.
This commit is contained in:
parent
f276a0588c
commit
fe98123d0b
@ -31,6 +31,7 @@
|
||||
#include "../reporters/catch_reporter_basic.hpp"
|
||||
#include "../reporters/catch_reporter_xml.hpp"
|
||||
#include "../reporters/catch_reporter_junit.hpp"
|
||||
#include "../reporters/catch_reporter_console.hpp"
|
||||
|
||||
namespace Catch {
|
||||
NonCopyable::~NonCopyable() {}
|
||||
@ -54,8 +55,11 @@ namespace Catch {
|
||||
TestCaseStats::~TestCaseStats() {}
|
||||
TestGroupStats::~TestGroupStats() {}
|
||||
TestRunStats::~TestRunStats() {}
|
||||
ThreadedSectionInfo::~ThreadedSectionInfo() {}
|
||||
|
||||
BasicReporter::~BasicReporter() {}
|
||||
AccumulatingReporter::~AccumulatingReporter() {}
|
||||
ConsoleReporter::~ConsoleReporter() {}
|
||||
IRunner::~IRunner() {}
|
||||
IMutableContext::~IMutableContext() {}
|
||||
IConfig::~IConfig() {}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "catch_config.hpp"
|
||||
#include "catch_test_case_info.h"
|
||||
#include "catch_assertionresult.h"
|
||||
#include "catch_option.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
@ -25,7 +26,7 @@ namespace Catch
|
||||
ReporterConfig( std::ostream& _stream, ConfigData const& _fullConfig )
|
||||
: m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
|
||||
|
||||
std::ostream& stream() { return *m_stream; }
|
||||
std::ostream& stream() const { return *m_stream; }
|
||||
std::string name() const { return m_fullConfig.name; }
|
||||
bool includeSuccessfulResults() const { return m_fullConfig.includeWhichResults == Include::SuccessfulResults; }
|
||||
bool warnAboutMissingAssertions() const { return m_fullConfig.warnings & ConfigData::WarnAbout::NoAssertions; }
|
||||
@ -66,6 +67,17 @@ namespace Catch
|
||||
SourceLineInfo lineInfo;
|
||||
};
|
||||
|
||||
struct ThreadedSectionInfo : SectionInfo, SharedImpl<> {
|
||||
ThreadedSectionInfo( SectionInfo const& _sectionInfo, Ptr<ThreadedSectionInfo> const& _parent = Ptr<ThreadedSectionInfo>() )
|
||||
: SectionInfo( _sectionInfo ),
|
||||
parent( _parent )
|
||||
{}
|
||||
virtual ~ThreadedSectionInfo();
|
||||
|
||||
std::vector<Ptr<ThreadedSectionInfo> > children;
|
||||
Ptr<ThreadedSectionInfo> parent;
|
||||
};
|
||||
|
||||
struct AssertionStats : SharedImpl<> {
|
||||
AssertionStats( AssertionResult const& _assertionResult,
|
||||
Totals const& _totals )
|
||||
@ -150,6 +162,10 @@ namespace Catch
|
||||
// !Work In progress
|
||||
struct IStreamingReporter : IShared {
|
||||
virtual ~IStreamingReporter();
|
||||
|
||||
// Implementing class must also provide the following static methid:
|
||||
// static std::string getDescription();
|
||||
|
||||
virtual ReporterPreferences getPreferences() const = 0;
|
||||
|
||||
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
|
||||
@ -171,6 +187,59 @@ namespace Catch
|
||||
// - this would be used by the JUnit reporter, for example.
|
||||
// - it may be used by the basic reporter, too, but that would clear down the stack
|
||||
// as it goes
|
||||
struct AccumulatingReporter : SharedImpl<IStreamingReporter> {
|
||||
|
||||
AccumulatingReporter( ReporterConfig const& _config )
|
||||
: stream( _config.stream() )
|
||||
{}
|
||||
|
||||
virtual ~AccumulatingReporter();
|
||||
|
||||
virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
|
||||
testRunInfo = _testRunInfo;
|
||||
}
|
||||
virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
|
||||
unusedGroupInfo = _groupInfo;
|
||||
}
|
||||
|
||||
virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
|
||||
unusedTestCaseInfo = _testInfo;
|
||||
}
|
||||
virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
|
||||
Ptr<ThreadedSectionInfo> sectionInfo = new ThreadedSectionInfo( _sectionInfo );
|
||||
unusedSectionInfo = sectionInfo;
|
||||
if( !currentSectionInfo ) {
|
||||
currentSectionInfo = sectionInfo;
|
||||
}
|
||||
else {
|
||||
currentSectionInfo->children.push_back( sectionInfo );
|
||||
sectionInfo->parent = currentSectionInfo;
|
||||
currentSectionInfo = sectionInfo;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void sectionEnded( Ptr<SectionStats const> const& /* _sectionStats */ ) {
|
||||
currentSectionInfo = currentSectionInfo->parent;
|
||||
unusedSectionInfo = currentSectionInfo;
|
||||
}
|
||||
virtual void testCaseEnded( Ptr<TestCaseStats const> const& /* _testCaseStats */ ) {
|
||||
unusedTestCaseInfo.reset();
|
||||
}
|
||||
virtual void testGroupEnded( Ptr<TestGroupStats const> const& /* _testGroupStats */ ) {
|
||||
unusedGroupInfo.reset();
|
||||
}
|
||||
virtual void testRunEnded( Ptr<TestRunStats const> const& /* _testRunStats */ ) {
|
||||
}
|
||||
|
||||
Option<TestRunInfo> testRunInfo;
|
||||
Option<GroupInfo> unusedGroupInfo;
|
||||
Option<TestCaseInfo> unusedTestCaseInfo;
|
||||
Ptr<ThreadedSectionInfo> unusedSectionInfo;
|
||||
Ptr<ThreadedSectionInfo> currentSectionInfo;
|
||||
bool currentSectionOpen;
|
||||
std::ostream& stream;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Deprecated
|
||||
|
64
include/internal/catch_option.hpp
Normal file
64
include/internal/catch_option.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Created by Phil on 02/12/2012.
|
||||
* Copyright 2012 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
|
||||
|
||||
#include "catch_common.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
// An optional type
|
||||
template<typename T>
|
||||
class Option {
|
||||
public:
|
||||
Option() : nullableValue( NULL ) {}
|
||||
Option( T const& _value )
|
||||
: nullableValue( new( storage ) T( _value ) )
|
||||
{}
|
||||
Option( Option const& _other )
|
||||
: nullableValue( _other ? new( storage ) T( *_other ) : NULL )
|
||||
{}
|
||||
|
||||
~Option() {
|
||||
reset();
|
||||
}
|
||||
|
||||
Option& operator= ( Option const& _other ) {
|
||||
reset();
|
||||
if( _other )
|
||||
nullableValue = new( storage ) T( *_other );
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if( nullableValue )
|
||||
nullableValue->~T();
|
||||
nullableValue = NULL;
|
||||
}
|
||||
T& operator*() { return *nullableValue; }
|
||||
const T& operator*() const { return *nullableValue; }
|
||||
T* operator->() { return nullableValue; }
|
||||
const T* operator->() const { return nullableValue; }
|
||||
|
||||
bool some() const { return nullableValue != NULL; }
|
||||
bool none() const { return nullableValue == NULL; }
|
||||
|
||||
bool operator !() const { return nullableValue == NULL; }
|
||||
operator SafeBool::type() const {
|
||||
return SafeBool::makeSafe( some() );
|
||||
}
|
||||
|
||||
private:
|
||||
T* nullableValue;
|
||||
char storage[sizeof(T)];
|
||||
};
|
||||
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
|
@ -31,6 +31,11 @@ namespace Catch {
|
||||
if( m_p )
|
||||
m_p->release();
|
||||
}
|
||||
void reset() {
|
||||
if( m_p )
|
||||
m_p->release();
|
||||
m_p = NULL;
|
||||
}
|
||||
Ptr& operator = ( T* p ){
|
||||
Ptr temp( p );
|
||||
swap( temp );
|
||||
@ -47,6 +52,7 @@ namespace Catch {
|
||||
T& operator*() const { return *m_p; }
|
||||
T* operator->() const { return m_p; }
|
||||
bool operator !() const { return m_p == NULL; }
|
||||
operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
|
||||
|
||||
private:
|
||||
T* m_p;
|
||||
|
@ -31,8 +31,8 @@ namespace Catch {
|
||||
std::string className;
|
||||
std::string description;
|
||||
std::set<std::string> tags;
|
||||
bool isHidden;
|
||||
SourceLineInfo lineInfo;
|
||||
bool isHidden;
|
||||
};
|
||||
|
||||
class TestCase : protected TestCaseInfo {
|
||||
|
@ -41,8 +41,8 @@ namespace Catch {
|
||||
className( _className ),
|
||||
description( _description ),
|
||||
tags( _tags ),
|
||||
isHidden( _isHidden ),
|
||||
lineInfo( _lineInfo )
|
||||
lineInfo( _lineInfo ),
|
||||
isHidden( _isHidden )
|
||||
{}
|
||||
|
||||
TestCaseInfo::TestCaseInfo( const TestCaseInfo& other )
|
||||
@ -50,8 +50,8 @@ namespace Catch {
|
||||
className( other.className ),
|
||||
description( other.description ),
|
||||
tags( other.tags ),
|
||||
isHidden( other.isHidden ),
|
||||
lineInfo( other.lineInfo )
|
||||
lineInfo( other.lineInfo ),
|
||||
isHidden( other.isHidden )
|
||||
{}
|
||||
|
||||
TestCase::TestCase( ITestCase* testCase, const TestCaseInfo& info ) : TestCaseInfo( info ), test( testCase ) {}
|
||||
|
99
include/reporters/catch_reporter_console.hpp
Normal file
99
include/reporters/catch_reporter_console.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Created by Phil on 5/12/2012.
|
||||
* Copyright 2012 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
|
||||
|
||||
#include "../internal/catch_interfaces_reporter.h"
|
||||
#include "../internal/catch_reporter_registrars.hpp"
|
||||
#include "../internal/catch_console_colour.hpp"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct ConsoleReporter : AccumulatingReporter {
|
||||
ConsoleReporter( ReporterConfig const& _config )
|
||||
: AccumulatingReporter( _config )
|
||||
{}
|
||||
|
||||
virtual ~ConsoleReporter();
|
||||
static std::string getDescription() {
|
||||
return "Reports test results as plain lines of text";
|
||||
}
|
||||
virtual ReporterPreferences getPreferences() const {
|
||||
ReporterPreferences prefs;
|
||||
prefs.shouldRedirectStdOut = false;
|
||||
return prefs;
|
||||
|
||||
}
|
||||
void lazyPrintRunInfo() {
|
||||
stream << "[Started testing: " << testRunInfo->name << "]" << std::endl;
|
||||
testRunInfo.reset();
|
||||
}
|
||||
void lazyPrintGroupInfo() {
|
||||
if( !unusedGroupInfo->name.empty() )
|
||||
stream << "[Group: " << unusedGroupInfo->name << "]" << std::endl;
|
||||
unusedGroupInfo.reset();
|
||||
}
|
||||
void lazyPrintTestCaseInfo() {
|
||||
stream << "[TestCase: " << unusedTestCaseInfo->name << "]" << std::endl;
|
||||
unusedTestCaseInfo.reset();
|
||||
}
|
||||
void lazyPrintSectionInfo() {
|
||||
ThreadedSectionInfo* section = unusedSectionInfo.get();
|
||||
bool firstSection = true;
|
||||
stream << "[Section: ";
|
||||
do {
|
||||
if( firstSection )
|
||||
firstSection = false;
|
||||
else
|
||||
stream << ",\n ";
|
||||
stream << section->name;
|
||||
section = section->parent.get();
|
||||
}
|
||||
while( section );
|
||||
|
||||
stream << "]" << std::endl;
|
||||
unusedSectionInfo.reset();
|
||||
}
|
||||
void lazyPrint() {
|
||||
if( testRunInfo )
|
||||
lazyPrintRunInfo();
|
||||
if( unusedGroupInfo )
|
||||
lazyPrintGroupInfo();
|
||||
if( unusedTestCaseInfo )
|
||||
lazyPrintTestCaseInfo();
|
||||
if( unusedSectionInfo )
|
||||
lazyPrintSectionInfo();
|
||||
}
|
||||
|
||||
virtual void assertionStarting( AssertionInfo const& _assertionInfo ) {
|
||||
}
|
||||
virtual void assertionEnded( Ptr<AssertionStats const> const& _assertionStats ) {
|
||||
if( !_assertionStats->assertionResult.isOk() )
|
||||
lazyPrint();
|
||||
}
|
||||
|
||||
virtual void sectionEnded( Ptr<SectionStats const> const& _sectionStats ) {
|
||||
if( !unusedSectionInfo ) {
|
||||
stream << "[Section totals: ]" << std::endl;
|
||||
}
|
||||
AccumulatingReporter::sectionEnded( _sectionStats );
|
||||
}
|
||||
virtual void testCaseEnded( Ptr<TestCaseStats const> const& _testCaseStats ) {
|
||||
if( !unusedTestCaseInfo ) {
|
||||
stream << "[TestCase totals: ]\n" << std::endl;
|
||||
}
|
||||
AccumulatingReporter::testCaseEnded( _testCaseStats );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
|
2
projects/SelfTest/SurrogateCpps/catch_option.cpp
Normal file
2
projects/SelfTest/SurrogateCpps/catch_option.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
// This file is only here to verify (to the extent possible) the self sufficiency of the header
|
||||
#include "catch_option.hpp"
|
@ -30,6 +30,7 @@
|
||||
4AB3D99D1616216500C9A0F8 /* catch_interfaces_testcase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AB3D99C1616216500C9A0F8 /* catch_interfaces_testcase.cpp */; };
|
||||
4AB3D9A01616219100C9A0F8 /* catch_interfaces_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AB3D99F1616219100C9A0F8 /* catch_interfaces_config.cpp */; };
|
||||
4AB3D9A2161621B500C9A0F8 /* catch_interfaces_generators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AB3D9A1161621B500C9A0F8 /* catch_interfaces_generators.cpp */; };
|
||||
4ACE21CC166CA1B300FB5509 /* catch_option.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4ACE21CA166CA1B300FB5509 /* catch_option.cpp */; };
|
||||
4AE1840B14EE4F230066340D /* catch_self_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AE1840A14EE4F230066340D /* catch_self_test.cpp */; };
|
||||
4AEE032016142F910071E950 /* catch_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AEE031F16142F910071E950 /* catch_common.cpp */; };
|
||||
4AEE032316142FC70071E950 /* catch_debugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AEE032216142FC70071E950 /* catch_debugger.cpp */; };
|
||||
@ -132,12 +133,15 @@
|
||||
4AB3D99C1616216500C9A0F8 /* catch_interfaces_testcase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_interfaces_testcase.cpp; path = ../../../SelfTest/SurrogateCpps/catch_interfaces_testcase.cpp; sourceTree = "<group>"; };
|
||||
4AB3D99F1616219100C9A0F8 /* catch_interfaces_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_interfaces_config.cpp; path = ../../../SelfTest/SurrogateCpps/catch_interfaces_config.cpp; sourceTree = "<group>"; };
|
||||
4AB3D9A1161621B500C9A0F8 /* catch_interfaces_generators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_interfaces_generators.cpp; path = ../../../SelfTest/SurrogateCpps/catch_interfaces_generators.cpp; sourceTree = "<group>"; };
|
||||
4AB42F84166F3E1A0099F2C8 /* catch_reporter_console.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_reporter_console.hpp; sourceTree = "<group>"; };
|
||||
4AB77CB51551AEA200857BF0 /* catch_ptr.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_ptr.hpp; sourceTree = "<group>"; };
|
||||
4AB77CB71553B72B00857BF0 /* catch_section_info.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_section_info.hpp; sourceTree = "<group>"; };
|
||||
4AB77CB81553BB3800857BF0 /* catch_running_test.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = catch_running_test.hpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
||||
4ABEA80415C90D2B009F0424 /* catch_objc_arc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_objc_arc.hpp; sourceTree = "<group>"; };
|
||||
4AC91CCE155CF02800DC5117 /* catch_expression_lhs.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = catch_expression_lhs.hpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
||||
4AC91CD0155D8DA600DC5117 /* catch_expression_decomposer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = catch_expression_decomposer.hpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
||||
4ACE21C8166CA19700FB5509 /* catch_option.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_option.hpp; sourceTree = "<group>"; };
|
||||
4ACE21CA166CA1B300FB5509 /* catch_option.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_option.cpp; path = ../../../SelfTest/SurrogateCpps/catch_option.cpp; sourceTree = "<group>"; };
|
||||
4AE1840A14EE4F230066340D /* catch_self_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_self_test.cpp; path = ../../../SelfTest/catch_self_test.cpp; sourceTree = "<group>"; };
|
||||
4AEE031F16142F910071E950 /* catch_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_common.cpp; path = ../../../SelfTest/SurrogateCpps/catch_common.cpp; sourceTree = "<group>"; };
|
||||
4AEE032216142FC70071E950 /* catch_debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_debugger.cpp; path = ../../../SelfTest/SurrogateCpps/catch_debugger.cpp; sourceTree = "<group>"; };
|
||||
@ -240,6 +244,7 @@
|
||||
4A6D0C66149B3E3D00DB3EAA /* catch_reporter_basic.hpp */,
|
||||
4A6D0C67149B3E3D00DB3EAA /* catch_reporter_junit.hpp */,
|
||||
4A6D0C68149B3E3D00DB3EAA /* catch_reporter_xml.hpp */,
|
||||
4AB42F84166F3E1A0099F2C8 /* catch_reporter_console.hpp */,
|
||||
);
|
||||
name = reporters;
|
||||
path = ../../../../include/reporters;
|
||||
@ -264,6 +269,7 @@
|
||||
4AB3D99C1616216500C9A0F8 /* catch_interfaces_testcase.cpp */,
|
||||
4AB3D99F1616219100C9A0F8 /* catch_interfaces_config.cpp */,
|
||||
4AB3D9A1161621B500C9A0F8 /* catch_interfaces_generators.cpp */,
|
||||
4ACE21CA166CA1B300FB5509 /* catch_option.cpp */,
|
||||
);
|
||||
name = SurrogateCpps;
|
||||
sourceTree = "<group>";
|
||||
@ -367,6 +373,7 @@
|
||||
4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */,
|
||||
4AB77CB51551AEA200857BF0 /* catch_ptr.hpp */,
|
||||
4AEE0326161431070071E950 /* catch_streambuf.h */,
|
||||
4ACE21C8166CA19700FB5509 /* catch_option.hpp */,
|
||||
);
|
||||
name = Infrastructure;
|
||||
sourceTree = "<group>";
|
||||
@ -457,6 +464,7 @@
|
||||
4AB3D99D1616216500C9A0F8 /* catch_interfaces_testcase.cpp in Sources */,
|
||||
4AB3D9A01616219100C9A0F8 /* catch_interfaces_config.cpp in Sources */,
|
||||
4AB3D9A2161621B500C9A0F8 /* catch_interfaces_generators.cpp in Sources */,
|
||||
4ACE21CC166CA1B300FB5509 /* catch_option.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user