#include "UnitTest++/Config.h"

#ifndef UNITTEST_NO_DEFERRED_REPORTER

#include "UnitTest++/UnitTestPP.h"
#include "UnitTest++/DeferredTestReporter.h"
#include <cstring>

namespace UnitTest
{

   namespace
   {

#ifndef UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM
      MemoryOutStream& operator <<(MemoryOutStream& lhs, const std::string& rhs)
      {
         lhs << rhs.c_str();
         return lhs;
      }
#endif

      struct MockDeferredTestReporter : public DeferredTestReporter
      {
         virtual void ReportSummary(int, int, int, float)
         {}
      };

      struct DeferredTestReporterFixture
      {
         DeferredTestReporterFixture()
            : testName("UniqueTestName")
            , testSuite("UniqueTestSuite")
            , fileName("filename.h")
            , lineNumber(12)
            , details(testName.c_str(), testSuite.c_str(), fileName.c_str(), lineNumber)
         {}

         MockDeferredTestReporter reporter;
         std::string const testName;
         std::string const testSuite;
         std::string const fileName;
         int const lineNumber;
         TestDetails const details;
      };

      TEST_FIXTURE(DeferredTestReporterFixture, ReportTestStartCreatesANewDeferredTest)
      {
         reporter.ReportTestStart(details);
         CHECK_EQUAL(1, (int)reporter.GetResults().size());
      }

      TEST_FIXTURE(DeferredTestReporterFixture, ReportTestStartCapturesTestNameAndSuite)
      {
         reporter.ReportTestStart(details);

         DeferredTestResult const& result = reporter.GetResults().at(0);
         CHECK_EQUAL(testName.c_str(), result.testName.c_str());
         CHECK_EQUAL(testSuite.c_str(), result.suiteName.c_str());
      }

      TEST_FIXTURE(DeferredTestReporterFixture, ReportTestEndCapturesTestTime)
      {
         float const elapsed = 123.45f;
         reporter.ReportTestStart(details);
         reporter.ReportTestFinish(details, elapsed);

         DeferredTestResult const& result = reporter.GetResults().at(0);
         CHECK_CLOSE(elapsed, result.timeElapsed, 0.0001f);
      }

      TEST_FIXTURE(DeferredTestReporterFixture, ReportFailureSavesFailureDetails)
      {
         char const* failure = "failure";

         reporter.ReportTestStart(details);
         reporter.ReportFailure(details, failure);

         DeferredTestResult const& result = reporter.GetResults().at(0);
         CHECK(result.failed == true);
         CHECK_EQUAL(fileName.c_str(), result.failureFile.c_str());
      }

      TEST_FIXTURE(DeferredTestReporterFixture, ReportFailureSavesFailureDetailsForMultipleFailures)
      {
         char const* failure1 = "failure 1";
         char const* failure2 = "failure 2";

         reporter.ReportTestStart(details);
         reporter.ReportFailure(details, failure1);
         reporter.ReportFailure(details, failure2);

         DeferredTestResult const& result = reporter.GetResults().at(0);
         CHECK_EQUAL(2, (int)result.failures.size());
         CHECK_EQUAL(failure1, result.failures[0].failureStr);
         CHECK_EQUAL(failure2, result.failures[1].failureStr);
      }

      TEST_FIXTURE(DeferredTestReporterFixture, DeferredTestReporterTakesCopyOfFailureMessage)
      {
         reporter.ReportTestStart(details);

         char failureMessage[128];
         char const* goodStr = "Real failure message";
         char const* badStr = "Bogus failure message";

         using namespace std;

         strcpy(failureMessage, goodStr);
         reporter.ReportFailure(details, failureMessage);
         strcpy(failureMessage, badStr);

         DeferredTestResult const& result = reporter.GetResults().at(0);
         DeferredTestFailure const& failure = result.failures.at(0);
         CHECK_EQUAL(goodStr, failure.failureStr);
      }

   }
}

#endif
