1
2
3
4
5
6
7
8
9
10
11
12
13
14 package de.softwareforge.testing.postgres.junit5;
15
16 import static com.google.common.base.Preconditions.checkNotNull;
17 import static com.google.common.base.Preconditions.checkState;
18
19 import de.softwareforge.testing.postgres.embedded.DatabaseInfo;
20 import de.softwareforge.testing.postgres.embedded.DatabaseManager;
21 import de.softwareforge.testing.postgres.embedded.DatabaseManager.DatabaseManagerBuilder;
22 import de.softwareforge.testing.postgres.embedded.EmbeddedPostgres;
23
24 import java.lang.reflect.Type;
25 import java.sql.SQLException;
26 import java.util.UUID;
27 import javax.sql.DataSource;
28
29 import com.google.common.annotations.VisibleForTesting;
30 import edu.umd.cs.findbugs.annotations.NonNull;
31 import org.junit.jupiter.api.extension.AfterAllCallback;
32 import org.junit.jupiter.api.extension.AfterEachCallback;
33 import org.junit.jupiter.api.extension.BeforeAllCallback;
34 import org.junit.jupiter.api.extension.BeforeEachCallback;
35 import org.junit.jupiter.api.extension.ExtensionContext;
36 import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
37 import org.junit.jupiter.api.extension.ExtensionContext.Store;
38 import org.junit.jupiter.api.extension.ParameterContext;
39 import org.junit.jupiter.api.extension.ParameterResolutionException;
40 import org.junit.jupiter.api.extension.ParameterResolver;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45
46
47
48
49
50
51
52
53
54 public final class EmbeddedPgExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver {
55
56 private static final Logger LOG = LoggerFactory.getLogger(EmbeddedPgExtension.class);
57
58
59 private final Namespace PG_NAMESPACE = Namespace.create(UUID.randomUUID());
60
61 private final DatabaseManager.Builder<DatabaseManager> databaseManagerBuilder;
62
63 private volatile DatabaseManager databaseManager = null;
64
65 private EmbeddedPgExtension(DatabaseManager.Builder<DatabaseManager> databaseManagerBuilder) {
66 this.databaseManagerBuilder = databaseManagerBuilder;
67 }
68
69
70
71
72
73
74
75 @NonNull
76 static EmbeddedPgExtensionBuilder multiDatabase() {
77 return new EmbeddedPgExtensionBuilder(true);
78 }
79
80
81
82
83
84
85
86 @NonNull
87 static EmbeddedPgExtensionBuilder singleDatabase() {
88 return new EmbeddedPgExtensionBuilder(false);
89 }
90
91
92
93
94
95
96
97
98
99 public EmbeddedPgExtension() {
100 this(new DatabaseManagerBuilder(true).withInstancePreparer(EmbeddedPostgres.Builder::withDefaults));
101 }
102
103
104
105
106
107
108
109
110 @NonNull
111 public DataSource createDataSource() throws SQLException {
112 return createDatabaseInfo().asDataSource();
113 }
114
115 @VisibleForTesting
116 EmbeddedPostgres getEmbeddedPostgres() {
117 return databaseManager.getEmbeddedPostgres();
118 }
119
120
121
122
123
124
125
126
127
128 @NonNull
129 public DatabaseInfo createDatabaseInfo() throws SQLException {
130 checkState(databaseManager != null, "no before method has been called!");
131
132 DatabaseInfo databaseInfo = databaseManager.getDatabaseInfo();
133 LOG.info("Connection to {}", databaseInfo.asJdbcUrl());
134 return databaseInfo;
135 }
136
137 @Override
138 public void beforeAll(@NonNull ExtensionContext extensionContext) throws Exception {
139 checkNotNull(extensionContext, "extensionContext is null");
140
141 Store pgStore = extensionContext.getStore(PG_NAMESPACE);
142
143 TestMode testMode = pgStore.getOrComputeIfAbsent(TestMode.TESTMODE_KEY,
144 k -> new TestMode(extensionContext.getUniqueId(), databaseManagerBuilder.build()),
145 TestMode.class);
146
147 this.databaseManager = testMode.start(extensionContext.getUniqueId());
148 }
149
150 @Override
151 public void afterAll(@NonNull ExtensionContext extensionContext) throws Exception {
152 checkNotNull(extensionContext, "extensionContext is null");
153
154 Store pgStore = extensionContext.getStore(PG_NAMESPACE);
155 TestMode testMode = pgStore.get(TestMode.TESTMODE_KEY, TestMode.class);
156
157 if (testMode != null) {
158 this.databaseManager = testMode.stop(extensionContext.getUniqueId());
159 }
160 }
161
162 @Override
163 public void beforeEach(@NonNull ExtensionContext extensionContext) throws Exception {
164 checkNotNull(extensionContext, "extensionContext is null");
165
166 Store pgStore = extensionContext.getStore(PG_NAMESPACE);
167 TestMode testMode = pgStore.getOrComputeIfAbsent(TestMode.TESTMODE_KEY,
168 k -> new TestMode(extensionContext.getUniqueId(), databaseManagerBuilder.build()),
169 TestMode.class);
170
171 this.databaseManager = testMode.start(extensionContext.getUniqueId());
172 }
173
174 @Override
175 public void afterEach(@NonNull ExtensionContext extensionContext) throws Exception {
176 checkNotNull(extensionContext, "extensionContext is null");
177
178 Store pgStore = extensionContext.getStore(PG_NAMESPACE);
179 TestMode testMode = pgStore.get(TestMode.TESTMODE_KEY, TestMode.class);
180
181 if (testMode != null) {
182 this.databaseManager = testMode.stop(extensionContext.getUniqueId());
183 }
184 }
185
186 @Override
187 public boolean supportsParameter(@NonNull ParameterContext parameterContext, ExtensionContext extensionContext) {
188 Type type = parameterContext.getParameter().getType();
189 return type == EmbeddedPostgres.class
190 || type == DatabaseInfo.class
191 || type == DataSource.class;
192 }
193
194 @Override
195 public Object resolveParameter(@NonNull ParameterContext parameterContext, ExtensionContext extensionContext) {
196 Type type = parameterContext.getParameter().getType();
197 try {
198 if (type == EmbeddedPostgres.class) {
199 return getEmbeddedPostgres();
200 } else if (type == DatabaseInfo.class) {
201 return createDatabaseInfo();
202
203 } else if (type == DataSource.class) {
204 return createDataSource();
205 }
206 } catch (SQLException e) {
207 throw new ParameterResolutionException("Could not create " + type.getTypeName() + " instance", e);
208 }
209 return null;
210 }
211
212
213
214
215 public static final class EmbeddedPgExtensionBuilder extends DatabaseManager.Builder<EmbeddedPgExtension> {
216
217 private EmbeddedPgExtensionBuilder(boolean multiMode) {
218 super(multiMode);
219 }
220
221
222
223
224
225
226 @Override
227 @NonNull
228 public EmbeddedPgExtension build() {
229 DatabaseManager.Builder<DatabaseManager> databaseManagerBuilder = new DatabaseManagerBuilder(multiMode)
230 .withDatabasePreparers(databasePreparers.build())
231 .withInstancePreparers(instancePreparers.build());
232
233 return new EmbeddedPgExtension(databaseManagerBuilder);
234 }
235 }
236
237 private static final class TestMode {
238
239 private static final Object TESTMODE_KEY = new Object();
240
241 private final String id;
242 private final DatabaseManager databaseManager;
243
244 private TestMode(String id, DatabaseManager databaseManager) {
245 this.id = id;
246 this.databaseManager = databaseManager;
247 }
248
249 private DatabaseManager start(String id) throws Exception {
250 if (this.id.equals(id)) {
251 databaseManager.start();
252 }
253
254 return databaseManager;
255 }
256
257 private DatabaseManager stop(String id) throws Exception {
258 if (this.id.equals(id)) {
259 databaseManager.close();
260 return null;
261 }
262
263 return databaseManager;
264 }
265 }
266 }