text: #include <specs/specs.h>
  
  using namespace bandit::fakes;
  namespace bd = bandit::detail;
  
  SPEC_BEGIN(describe)
  
    describe("describe:", [](){
      bandit::detail::voidfunc_t describe_fn;
      fake_reporter_ptr reporter;
      std::unique_ptr<bd::contextstack_t> context_stack;
      std::unique_ptr<fake_context> global_context;
  
      before_each([&](){
        reporter = fake_reporter_ptr(new fake_reporter());
  
        context_stack = std::unique_ptr<bd::contextstack_t>(new bd::contextstack_t());
  
        global_context = std::unique_ptr<fake_context>(new fake_context());
        context_stack->push_back(global_context.get());
      });
  
  
      auto call_describe = [&](){
          describe("context name", describe_fn, *reporter, *context_stack);
      };
  
      describe("with a succeeding 'it'", [&](){
        int context_stack_size_while_running;
  
        before_each([&](){
          context_stack_size_while_running = 0;
          describe_fn = [&](){context_stack_size_while_running = context_stack->size();};
        });
  
        it("tells its parent context that execution has started", [&](){
          // This is important as once execution has started,
          // before_each and after_each calls cannot be guaranteed to
          // be run before any 'it' method.
  
          call_describe();
          AssertThat(global_context->call_log(), Has().AtLeast(1).EqualTo("execution_is_starting"));
        });
  
        it("tells reporter it's starting a run", [&](){
          call_describe();
          AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("context_starting: context name"));
        });
  
        it("tells reporter it's finished a run", [&](){
          call_describe();
          AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("context_ended: context name"));
        });
  
        it("pushes a new context during execution", [&](){
          call_describe();
          AssertThat(context_stack_size_while_running, Equals(2));
        });
  
        it("pops the context from the stack after execution so that only the global context is left", [&](){
            call_describe();
            AssertThat(*context_stack, Is().OfLength(1));
        });
  
      });
  
      describe("with test run error", [&](){
          //
          // This can occur if after_each or before_each are called
          // after execution has started for a context.
          // 
      
        before_each([&](){
          describe_fn = [&](){ throw bandit::detail::test_run_error("we dun goofed!"); };
        });
  
        it("doesn't propagate the error", [&](){
          call_describe();
        });
  
        it("tells reporter to report the error", [&](){
          call_describe();
          AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("test_run_error: context name (we dun goofed!)"));
        });
      
      });
  
      describe("skip", [&](){
        bool context_is_hard_skip;
        describe_fn = 
            [&](){ context_is_hard_skip = context_stack->back()->hard_skip(); };
  
        before_each([&](){
          context_is_hard_skip = false;
        });
  
        describe("describe_skip", [&](){
        
          it("pushes a context marked as skipped on the stack", [&](){
            describe_skip("context name", describe_fn, *reporter, *context_stack);
            AssertThat(context_is_hard_skip, IsTrue());
          });
        
        });
  
        describe("xdescribe", [&](){
  
          it("pushes a context marked as skipped on the stack", [&](){
            xdescribe("context name", describe_fn, *reporter, *context_stack);
            AssertThat(context_is_hard_skip, IsTrue());
          });
  
        });
      });
    });
  
  SPEC_END
  