1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess;
18
19 import java.io.*;
20 import java.lang.reflect.Field;
21 import java.nio.ByteBuffer;
22 import java.nio.channels.FileChannel;
23 import java.nio.charset.Charset;
24 import java.nio.file.Files;
25 import java.nio.file.StandardCopyOption;
26 import java.time.Instant;
27 import java.time.LocalDateTime;
28 import java.time.ZoneId;
29 import java.util.*;
30 import java.util.stream.StreamSupport;
31
32 import com.healthmarketscience.jackcess.Database.FileFormat;
33 import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
34 import com.healthmarketscience.jackcess.impl.*;
35 import com.healthmarketscience.jackcess.impl.JetFormatTest.TestDB;
36 import com.healthmarketscience.jackcess.util.MemFileChannel;
37 import static org.junit.jupiter.api.Assertions.*;
38
39
40
41
42
43
44 @SuppressWarnings("deprecation")
45 public class TestUtil
46 {
47 public static final TimeZone TEST_TZ =
48 TimeZone.getTimeZone("America/New_York");
49
50 private static final ThreadLocal<Boolean> _autoSync =
51 new ThreadLocal<Boolean>();
52
53 private TestUtil() {}
54
55 static void setTestAutoSync(boolean autoSync) {
56 _autoSync.set(autoSync);
57 }
58
59 static void clearTestAutoSync() {
60 _autoSync.remove();
61 }
62
63 static boolean getTestAutoSync() {
64 Boolean autoSync = _autoSync.get();
65 return ((autoSync != null) ? autoSync : Database.DEFAULT_AUTO_SYNC);
66 }
67
68 public static Database open(FileFormat fileFormat, File file)
69 throws Exception
70 {
71 return open(fileFormat, file, false);
72 }
73
74 public static Database open(FileFormat fileFormat, File file, boolean inMem)
75 throws Exception
76 {
77 return open(fileFormat, file, inMem, null);
78 }
79
80 public static Database open(FileFormat fileFormat, File file, boolean inMem,
81 Charset charset)
82 throws Exception
83 {
84 return openDB(fileFormat, file, inMem, charset, true);
85 }
86
87 public static Database open(TestDB testDB) throws Exception {
88 return open(testDB.getExpectedFileFormat(), testDB.getFile(), false,
89 testDB.getExpectedCharset());
90 }
91
92 public static Database openMem(TestDB testDB) throws Exception {
93 return openDB(testDB.getExpectedFileFormat(), testDB.getFile(), true,
94 testDB.getExpectedCharset(), false);
95 }
96
97 public static Database create(FileFormat fileFormat) throws Exception {
98 return create(fileFormat, false);
99 }
100
101 public static Database create(FileFormat fileFormat, boolean keep)
102 throws Exception
103 {
104 return create(fileFormat, keep, true);
105 }
106
107 public static Database createMem(FileFormat fileFormat) throws Exception {
108 return create(fileFormat);
109 }
110
111 public static Database createFile(FileFormat fileFormat) throws Exception {
112 return create(fileFormat, false, false);
113 }
114
115 private static Database create(FileFormat fileFormat, boolean keep,
116 boolean inMem)
117 throws Exception
118 {
119
120 FileChannel channel = ((inMem && !keep) ? MemFileChannel.newChannel() :
121 null);
122
123 if (fileFormat == FileFormat.GENERIC_JET4) {
124
125
126 try (InputStream inStream = TestUtil.class.getClassLoader()
127 .getResourceAsStream("emptyJet4.mdb")) {
128 File f = createTempFile(keep);
129 if (channel != null) {
130 JetFormatTest.transferDbFrom(channel, inStream);
131 } else {
132 try (OutputStream outStream = new FileOutputStream(f)) {
133 ByteUtil.copy(inStream, outStream);
134 }
135 }
136 return new DatabaseBuilder(f)
137 .setAutoSync(getTestAutoSync()).setChannel(channel).open();
138 }
139 }
140
141 return new DatabaseBuilder(createTempFile(keep)).setFileFormat(fileFormat)
142 .setAutoSync(getTestAutoSync()).setChannel(channel).create();
143 }
144
145
146 public static Database openCopy(TestDB testDB) throws Exception {
147 return openCopy(testDB, false);
148 }
149
150 public static Database openCopy(TestDB testDB, boolean keep)
151 throws Exception
152 {
153 return openCopy(testDB.getExpectedFileFormat(), testDB.getFile(), keep);
154 }
155
156 public static Database openCopy(FileFormat fileFormat, File file)
157 throws Exception
158 {
159 return openCopy(fileFormat, file, false);
160 }
161
162 public static Database openCopy(FileFormat fileFormat, File file,
163 boolean keep)
164 throws Exception
165 {
166 File tmp = createTempFile(keep);
167 copyFile(file, tmp);
168 return openDB(fileFormat, tmp, false, null, false);
169 }
170
171 private static Database openDB(
172 FileFormat fileFormat, File file, boolean inMem, Charset charset,
173 boolean readOnly)
174 throws Exception
175 {
176 FileChannel channel = (inMem ? MemFileChannel.newChannel(
177 file, MemFileChannel.RW_CHANNEL_MODE)
178 : null);
179 final Database db = new DatabaseBuilder(file).setReadOnly(readOnly)
180 .setAutoSync(getTestAutoSync()).setChannel(channel)
181 .setCharset(charset).open();
182 if(fileFormat != null) {
183 assertEquals(
184 DatabaseImpl.getFileFormatDetails(fileFormat).getFormat(),
185 ((DatabaseImpl)db).getFormat(),
186 "Wrong JetFormat.");
187 assertEquals(
188 fileFormat, db.getFileFormat(), "Wrong FileFormat.");
189 }
190 return db;
191 }
192
193 static Object[] createTestRow(String col1Val) {
194 return new Object[] {col1Val, "R", "McCune", 1234, (byte) 0xad, 555.66d,
195 777.88f, (short) 999, new Date()};
196 }
197
198 public static Object[] createTestRow() {
199 return createTestRow("Tim");
200 }
201
202 static Map<String,Object> createTestRowMap(String col1Val) {
203 return createExpectedRow("A", col1Val, "B", "R", "C", "McCune",
204 "D", 1234, "E", (byte) 0xad, "F", 555.66d,
205 "G", 777.88f, "H", (short) 999, "I", new Date());
206 }
207
208 public static void createTestTable(Database db) throws Exception {
209 new TableBuilder("test")
210 .addColumn(new ColumnBuilder("A", DataType.TEXT))
211 .addColumn(new ColumnBuilder("B", DataType.TEXT))
212 .addColumn(new ColumnBuilder("C", DataType.TEXT))
213 .addColumn(new ColumnBuilder("D", DataType.LONG))
214 .addColumn(new ColumnBuilder("E", DataType.BYTE))
215 .addColumn(new ColumnBuilder("F", DataType.DOUBLE))
216 .addColumn(new ColumnBuilder("G", DataType.FLOAT))
217 .addColumn(new ColumnBuilder("H", DataType.INT))
218 .addColumn(new ColumnBuilder("I", DataType.SHORT_DATE_TIME))
219 .toTable(db);
220 }
221
222 public static String createString(int len) {
223 return createString(len, 'a');
224 }
225
226 static String createNonAsciiString(int len) {
227 return createString(len, '\u0CC0');
228 }
229
230 private static String createString(int len, char firstChar) {
231 StringBuilder builder = new StringBuilder(len);
232 for(int i = 0; i < len; ++i) {
233 builder.append((char)(firstChar + (i % 26)));
234 }
235 return builder.toString();
236 }
237
238 static void assertRowCount(int expectedRowCount, Table table)
239 throws Exception
240 {
241 assertEquals(expectedRowCount, countRows(table));
242 assertEquals(expectedRowCount, table.getRowCount());
243 }
244
245 public static int countRows(Table table) throws Exception {
246 Cursor cursor = CursorBuilder.createCursor(table);
247 return (int) StreamSupport.stream(cursor.spliterator(), false).count();
248 }
249
250 public static void assertTable(
251 List<? extends Map<String, Object>> expectedTable,
252 Table table)
253 throws IOException
254 {
255 assertCursor(expectedTable, CursorBuilder.createCursor(table));
256 }
257
258 public static void assertCursor(
259 List<? extends Map<String, Object>> expectedTable,
260 Cursor cursor)
261 {
262 List<Map<String, Object>> foundTable =
263 new ArrayList<Map<String, Object>>();
264 for(Map<String, Object> row : cursor) {
265 foundTable.add(row);
266 }
267 assertEquals(expectedTable.size(), foundTable.size());
268 for(int i = 0; i < expectedTable.size(); ++i) {
269 assertEquals(expectedTable.get(i), foundTable.get(i));
270 }
271 }
272
273 public static RowImpl createExpectedRow(Object... rowElements) {
274 RowImpl row = new RowImpl((RowIdImpl)null);
275 for(int i = 0; i < rowElements.length; i += 2) {
276 row.put((String)rowElements[i],
277 rowElements[i + 1]);
278 }
279 return row;
280 }
281
282 public static List<Row> createExpectedTable(Row... rows) {
283 return Arrays.<Row>asList(rows);
284 }
285
286 public static void dumpDatabase(Database mdb) throws Exception {
287 dumpDatabase(mdb, false);
288 }
289
290 public static void dumpDatabase(Database mdb, boolean systemTables)
291 throws Exception
292 {
293 dumpDatabase(mdb, systemTables, new PrintWriter(System.out, true));
294 }
295
296 public static void dumpTable(Table table) throws Exception {
297 dumpTable(table, new PrintWriter(System.out, true));
298 }
299
300 public static void dumpProperties(Table table) throws Exception {
301 System.out.println("TABLE_PROPS: " + table.getName() + ": " +
302 table.getProperties());
303 for(Column c : table.getColumns()) {
304 System.out.println("COL_PROPS: " + c.getName() + ": " +
305 c.getProperties());
306 }
307 }
308
309 static void dumpDatabase(Database mdb, boolean systemTables,
310 PrintWriter writer) throws Exception
311 {
312 writer.println("DATABASE:");
313 for(Table table : mdb) {
314 dumpTable(table, writer);
315 }
316 if(systemTables) {
317 for(String sysTableName : mdb.getSystemTableNames()) {
318 dumpTable(mdb.getSystemTable(sysTableName), writer);
319 }
320 }
321 }
322
323 static void dumpTable(Table table, PrintWriter writer) throws Exception {
324
325 for(Index index : table.getIndexes()) {
326 ((IndexImpl)index).initialize();
327 }
328
329 writer.println("TABLE: " + table.getName());
330 List<String> colNames = new ArrayList<String>();
331 for(Column col : table.getColumns()) {
332 colNames.add(col.getName());
333 }
334 writer.println("COLUMNS: " + colNames);
335 for(Map<String, Object> row : CursorBuilder.createCursor(table)) {
336 writer.println(massageRow(row));
337 }
338 }
339
340 private static Map<String,Object> massageRow(Map<String, Object> row)
341 throws IOException
342 {
343 for(Map.Entry<String, Object> entry : row.entrySet()) {
344 Object v = entry.getValue();
345 if(v instanceof byte[]) {
346
347 byte[] bv = (byte[])v;
348 entry.setValue(ByteUtil.toHexString(ByteBuffer.wrap(bv), bv.length));
349 } else if(v instanceof ComplexValueForeignKey) {
350
351 String str = "ComplexValue(" + v + ")" +
352 ((ComplexValueForeignKey)v).getValues();
353 entry.setValue(str);
354 }
355 }
356
357 return row;
358 }
359
360 static void dumpIndex(Index index) throws Exception {
361 dumpIndex(index, Integer.MAX_VALUE);
362 }
363
364 static void dumpIndex(Index index, int limit) throws Exception {
365 dumpIndex(index, new PrintWriter(System.out, true), limit);
366 }
367
368 static void dumpIndex(Index index, PrintWriter writer, int limit)
369 throws Exception {
370 writer.println("INDEX: " + index);
371 IndexData.EntryCursor ec = ((IndexImpl)index).cursor();
372 IndexData.Entry lastE = ec.getLastEntry();
373 IndexData.Entry e = null;
374 int count = 0;
375 while((e = ec.getNextEntry()) != lastE) {
376 writer.println(e);
377 if((count++) > limit) {
378 break;
379 }
380 }
381 }
382
383 static void assertSameDate(Date expected, Date found)
384 {
385 if(expected == found) {
386 return;
387 }
388 if((expected == null) || (found == null)) {
389 throw new AssertionError("Expected " + expected + ", found " + found);
390 }
391 long expTime = expected.getTime();
392 long foundTime = found.getTime();
393
394
395
396 if((expTime != foundTime) && (Math.abs(expTime - foundTime) > 1)) {
397 throw new AssertionError("Expected " + expTime + " (" + expected +
398 "), found " + foundTime + " (" + found + ")");
399 }
400 }
401
402 static void assertSameDate(Date expected, LocalDateTime found)
403 {
404 if((expected == null) && (found == null)) {
405 return;
406 }
407 if((expected == null) || (found == null)) {
408 throw new AssertionError("Expected " + expected + ", found " + found);
409 }
410
411 LocalDateTime expectedLdt = LocalDateTime.ofInstant(
412 Instant.ofEpochMilli(expected.getTime()),
413 ZoneId.systemDefault());
414
415 assertEquals(expectedLdt, found);
416 }
417
418 public static void copyFile(File srcFile, File dstFile)
419 throws IOException
420 {
421 Files.copy(srcFile.toPath(), dstFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
422 }
423
424 static void copyStream(InputStream istream, OutputStream ostream)
425 throws IOException
426 {
427
428
429 byte[] buf = new byte[1024];
430 int numBytes = 0;
431 while((numBytes = istream.read(buf)) >= 0) {
432 ostream.write(buf, 0, numBytes);
433 }
434 }
435
436 public static File createTempFile(boolean keep) throws Exception {
437 File tmp = File.createTempFile("databaseTest", ".mdb");
438 if(keep) {
439 System.out.println("Created " + tmp);
440 } else {
441 tmp.deleteOnExit();
442 }
443 return tmp;
444 }
445
446 public static void clearTableCache(Database db) throws Exception
447 {
448 Field f = db.getClass().getDeclaredField("_tableCache");
449 f.setAccessible(true);
450 Object val = f.get(db);
451 f = val.getClass().getDeclaredField("_tables");
452 f.setAccessible(true);
453 val = f.get(val);
454 ((Map<?,?>)val).clear();
455 }
456
457 public static byte[] toByteArray(File file)
458 throws IOException
459 {
460 return Files.readAllBytes(file.toPath());
461 }
462
463 public static byte[] toByteArray(InputStream in, long length)
464 throws IOException
465 {
466
467
468 try (DataInputStream din = new DataInputStream(in)) {
469 byte[] bytes = new byte[(int)length];
470 din.readFully(bytes);
471 return bytes;
472 }
473 }
474
475 static void checkTestDBTable1RowABCDEFG(final TestDB testDB, final Table table, final Row row)
476 {
477 assertEquals("abcdefg", row.get("A"), () -> "testDB: " + testDB + "; table: " + table);
478 assertEquals("hijklmnop", row.get("B"));
479 assertEquals(new Byte((byte) 2), row.get("C"));
480 assertEquals(new Short((short) 222), row.get("D"));
481 assertEquals(Integer.valueOf(333333333), row.get("E"));
482 assertEquals(Double.valueOf(444.555d), row.get("F"));
483 final Calendar cal = Calendar.getInstance();
484 cal.setTime(row.getDate("G"));
485 assertEquals(Calendar.SEPTEMBER, cal.get(Calendar.MONTH));
486 assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
487 assertEquals(1974, cal.get(Calendar.YEAR));
488 assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
489 assertEquals(0, cal.get(Calendar.MINUTE));
490 assertEquals(0, cal.get(Calendar.SECOND));
491 assertEquals(0, cal.get(Calendar.MILLISECOND));
492 assertEquals(Boolean.TRUE, row.get("I"));
493 }
494
495 static void checkTestDBTable1RowA(final TestDB testDB, final Table table, final Row row)
496 {
497 assertEquals("a", row.get("A"), () -> "testDB: " + testDB + "; table: " + table);
498 assertEquals("b", row.get("B"));
499 assertEquals(new Byte((byte) 0), row.get("C"));
500 assertEquals(new Short((short) 0), row.get("D"));
501 assertEquals(Integer.valueOf(0), row.get("E"));
502 assertEquals(Double.valueOf(0d), row.get("F"));
503 final Calendar cal = Calendar.getInstance();
504 cal.setTime(row.getDate("G"));
505 assertEquals(Calendar.DECEMBER, cal.get(Calendar.MONTH));
506 assertEquals(12, cal.get(Calendar.DAY_OF_MONTH));
507 assertEquals(1981, cal.get(Calendar.YEAR));
508 assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
509 assertEquals(0, cal.get(Calendar.MINUTE));
510 assertEquals(0, cal.get(Calendar.SECOND));
511 assertEquals(0, cal.get(Calendar.MILLISECOND));
512 assertEquals(Boolean.FALSE, row.get("I"));
513 }
514
515 }