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