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