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