/
AutowiringEnclosure.h
120 lines (100 loc) · 4.03 KB
/
AutowiringEnclosure.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#pragma once
#include <autowiring/autowiring.h>
#include "demangle.h"
#include <autowiring/at_exit.h>
#include MEMORY_HEADER
#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
#error Please include your version of gtest.h before including the autowiring enclosure
#endif
/// <summary>
/// General purpose exception filter, used for tracking exceptions thrown from unit test CoreThreads
/// </summary>
class AutowiringEnclosureExceptionFilter:
public ContextMember,
public ExceptionFilter
{
public:
AutowiringEnclosureExceptionFilter(void) :
ContextMember("AutowiringEnclosureExceptionFilter"),
m_excepted(false)
{}
bool m_excepted;
const std::type_info* m_ti;
std::string m_what;
void Filter(void) override {
m_excepted = true;
try {
throw;
}
catch(std::exception& except) {
m_ti = &typeid(except);
m_what = except.what();
}
catch(...) {}
}
};
struct TestInfoProxy {
TestInfoProxy(const testing::TestInfo& info) :
m_info(info)
{}
const testing::TestInfo& m_info;
};
/// <summary>
/// Provides a test fixture which ensures proper cleanup of a created subcontext
/// </summary>
/// <remarks>
/// Created subcontexts will be terminated and waited upon in the destructor, by default. Contexts
/// must exit within 250ms of being signalled to stop, or an error will result in order to ensure
/// tests complete in a timely fashion. If a longer stop duration is desired, you must provide a
/// destructor which performs teardown in a different way.
/// </remarks>
class AutowiringEnclosure:
public testing::EmptyTestEventListener
{
public:
// Base overrides:
void OnTestStart(const testing::TestInfo& info) override {
AutoRequired<AutowiringEnclosureExceptionFilter> filter;
// Set global context current first, then initiate
AutoGlobalContext global;
global->SetCurrent();
global->Initiate();
// The context proper. This is automatically assigned as the current
// context when SetUp is invoked.
AutoCreateContext create;
create->Inject<TestInfoProxy>(info);
// Add exception filter in this context:
create->Inject<AutowiringEnclosureExceptionFilter>();
// Now make it current and let the test run:
create->SetCurrent();
}
void OnTestEnd(const testing::TestInfo& info) override {
auto setglobal = MakeAtExit([] {
// Unconditionally reset the global context as the current context
AutoGlobalContext()->SetCurrent();
});
// Verify we can grab the test case back out and that the pointer is correct:
Autowired<AutowiringEnclosureExceptionFilter> ecef;
// Global initialization tests are special, we don't bother checking closure principle on them:
if(!strcmp("GlobalInitTest", info.test_case_name()))
return;
// Need to make sure we got back our exception filter before continuing:
ASSERT_TRUE(ecef.IsAutowired()) << "Failed to find the enclosed context exception filter; unit test may have incorrectly reset the enclosing context before returning";
// Now try to tear down the test context enclosure:
auto ctxt = ecef ? ecef->GetContext() : nullptr;
ctxt->SignalShutdown();
// Do not allow teardown to take more than 5 seconds. This is considered a "timely teardown" limit.
// If it takes more than this amount of time to tear down, the test case itself should invoke SignalShutdown
// and Wait itself with the extended teardown period specified.
ASSERT_TRUE(ctxt->Wait(std::chrono::seconds(5))) << "Test case took too long to tear down, unit tests running after this point are untrustworthy";
static const char sc_autothrow [] = "AUTOTHROW_";
if(!strncmp(sc_autothrow, info.name(), sizeof(sc_autothrow) - 1))
// Throw expected, end here
return;
// If an exception occurred somewhere, report it:
ASSERT_FALSE(ecef->m_excepted)
<< "An unhandled exception occurred in this context" << std::endl
<< "[" << (ecef->m_ti ? autowiring::demangle(*ecef->m_ti) : "unknown") << "] " << ecef->m_what;
}
};