View Javadoc
1   /*
2   Copyright (c) 2015 James Ahlborn
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
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.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.Calendar;
33  import java.util.Date;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.TimeZone;
37  
38  import static com.healthmarketscience.jackcess.Database.*;
39  import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
40  import com.healthmarketscience.jackcess.impl.ByteUtil;
41  import com.healthmarketscience.jackcess.impl.DatabaseImpl;
42  import com.healthmarketscience.jackcess.impl.IndexData;
43  import com.healthmarketscience.jackcess.impl.IndexImpl;
44  import com.healthmarketscience.jackcess.impl.JetFormatTest.TestDB;
45  import com.healthmarketscience.jackcess.impl.RowIdImpl;
46  import com.healthmarketscience.jackcess.impl.RowImpl;
47  import com.healthmarketscience.jackcess.util.MemFileChannel;
48  import org.junit.Assert;
49  
50  /**
51   * Utilty code for the test cases.
52   *
53   * @author James Ahlborn
54   */
55  public class TestUtil 
56  {
57    public static final TimeZone TEST_TZ =
58      TimeZone.getTimeZone("America/New_York");
59    
60    private static final ThreadLocal<Boolean> _autoSync =         
61      new ThreadLocal<Boolean>();
62  
63    private TestUtil() {}
64  
65    static void setTestAutoSync(boolean autoSync) {
66      _autoSync.set(autoSync);
67    }
68  
69    static void clearTestAutoSync() {
70      _autoSync.remove();
71    }
72  
73    static boolean getTestAutoSync() {
74      Boolean autoSync = _autoSync.get();
75      return ((autoSync != null) ? autoSync : Database.DEFAULT_AUTO_SYNC);
76    }
77  
78    public static Database open(FileFormat fileFormat, File file) 
79      throws Exception 
80    {
81      return open(fileFormat, file, false);
82    }
83  
84    public static Database open(FileFormat fileFormat, File file, boolean inMem) 
85      throws Exception 
86    {
87      FileChannel channel = (inMem ? MemFileChannel.newChannel(file, "rw") 
88                             : null);
89      final Database db = new DatabaseBuilder(file).setReadOnly(true)
90        .setAutoSync(getTestAutoSync()).setChannel(channel).open();
91      Assert.assertEquals("Wrong JetFormat.", 
92                   DatabaseImpl.getFileFormatDetails(fileFormat).getFormat(), 
93                   ((DatabaseImpl)db).getFormat());
94      Assert.assertEquals("Wrong FileFormat.", fileFormat, db.getFileFormat());
95      return db;
96    }
97  
98    public static Database open(TestDB testDB) throws Exception {
99      return open(testDB.getExpectedFileFormat(), testDB.getFile());
100   }
101 
102   public static Database openMem(TestDB testDB) throws Exception {
103     return open(testDB.getExpectedFileFormat(), testDB.getFile(), true);
104   }
105 
106   public static Database create(FileFormat fileFormat) throws Exception {
107     return create(fileFormat, false);
108   }
109 
110   public static Database create(FileFormat fileFormat, boolean keep) 
111     throws Exception 
112   {
113     return create(fileFormat, keep, false);
114   }
115 
116   public static Database createMem(FileFormat fileFormat) throws Exception {
117     return create(fileFormat, false, true);
118   }
119 
120   private static Database create(FileFormat fileFormat, boolean keep, 
121                                  boolean inMem) 
122     throws Exception 
123   {
124     FileChannel channel = (inMem ? MemFileChannel.newChannel() : null);
125     return new DatabaseBuilder(createTempFile(keep)).setFileFormat(fileFormat)
126       .setAutoSync(getTestAutoSync()).setChannel(channel).create();
127   }
128 
129 
130   public static Database openCopy(TestDB testDB) throws Exception {
131     return openCopy(testDB, false);
132   }
133 
134   public static Database openCopy(TestDB testDB, boolean keep)
135     throws Exception
136   {
137     return openCopy(testDB.getExpectedFileFormat(), testDB.getFile(), keep);
138   }
139 
140   public static Database openCopy(FileFormat fileFormat, File file)
141     throws Exception
142   {
143     return openCopy(fileFormat, file, false);
144   }
145 
146   public static Database openCopy(FileFormat fileFormat, File file,
147                                   boolean keep)
148     throws Exception
149   {
150     File tmp = createTempFile(keep);
151     copyFile(file, tmp);
152     Database db = new DatabaseBuilder(tmp).setAutoSync(getTestAutoSync()).open();
153     Assert.assertEquals("Wrong JetFormat.", 
154                  DatabaseImpl.getFileFormatDetails(fileFormat).getFormat(),
155                  ((DatabaseImpl)db).getFormat());
156     Assert.assertEquals("Wrong FileFormat.", fileFormat, db.getFileFormat());
157     return db;
158   }
159 
160 
161   static Object[] createTestRow(String col1Val) {
162     return new Object[] {col1Val, "R", "McCune", 1234, (byte) 0xad, 555.66d,
163         777.88f, (short) 999, new Date()};
164   }
165 
166   public static Object[] createTestRow() {
167     return createTestRow("Tim");
168   }
169   
170   static Map<String,Object> createTestRowMap(String col1Val) {
171     return createExpectedRow("A", col1Val, "B", "R", "C", "McCune",
172                              "D", 1234, "E", (byte) 0xad, "F", 555.66d,
173                              "G", 777.88f, "H", (short) 999, "I", new Date());
174   }
175 
176   public static void createTestTable(Database db) throws Exception {
177     new TableBuilder("test")
178       .addColumn(new ColumnBuilder("A", DataType.TEXT))
179       .addColumn(new ColumnBuilder("B", DataType.TEXT))
180       .addColumn(new ColumnBuilder("C", DataType.TEXT))
181       .addColumn(new ColumnBuilder("D", DataType.LONG))
182       .addColumn(new ColumnBuilder("E", DataType.BYTE))
183       .addColumn(new ColumnBuilder("F", DataType.DOUBLE))
184       .addColumn(new ColumnBuilder("G", DataType.FLOAT))
185       .addColumn(new ColumnBuilder("H", DataType.INT))
186       .addColumn(new ColumnBuilder("I", DataType.SHORT_DATE_TIME))
187       .toTable(db);
188   }
189 
190   public static String createString(int len) {
191     return createString(len, 'a');
192   }
193 
194   static String createNonAsciiString(int len) {
195     return createString(len, '\u0CC0');
196   }
197     
198   private static String createString(int len, char firstChar) {
199     StringBuilder builder = new StringBuilder(len);
200     for(int i = 0; i < len; ++i) {
201       builder.append((char)(firstChar + (i % 26)));
202     }
203     return builder.toString();
204   }
205 
206   static void assertRowCount(int expectedRowCount, Table table)
207     throws Exception
208   {
209     Assert.assertEquals(expectedRowCount, countRows(table));
210     Assert.assertEquals(expectedRowCount, table.getRowCount());
211   }
212   
213   public static int countRows(Table table) throws Exception {
214     int rtn = 0;
215     for(Map<String, Object> row : CursorBuilder.createCursor(table)) {
216       rtn++;
217     }
218     return rtn;
219   }
220 
221   public static void assertTable(
222       List<? extends Map<String, Object>> expectedTable, 
223       Table table)
224     throws IOException
225   {
226     assertCursor(expectedTable, CursorBuilder.createCursor(table));
227   }
228   
229   public static void assertCursor(
230       List<? extends Map<String, Object>> expectedTable, 
231       Cursor cursor)
232   {
233     List<Map<String, Object>> foundTable =
234       new ArrayList<Map<String, Object>>();
235     for(Map<String, Object> row : cursor) {
236       foundTable.add(row);
237     }
238     Assert.assertEquals(expectedTable.size(), foundTable.size());
239     for(int i = 0; i < expectedTable.size(); ++i) {
240       Assert.assertEquals(expectedTable.get(i), foundTable.get(i));
241     } 
242   }
243   
244   public static RowImpl createExpectedRow(Object... rowElements) {
245     RowImpl row = new RowImpl((RowIdImpl)null);
246     for(int i = 0; i < rowElements.length; i += 2) {
247       row.put((String)rowElements[i],
248               rowElements[i + 1]);
249     }
250     return row;
251   }    
252 
253   public static List<Row> createExpectedTable(Row... rows) {
254     return Arrays.<Row>asList(rows);
255   }    
256   
257   public static void dumpDatabase(Database mdb) throws Exception {
258     dumpDatabase(mdb, false);
259   }
260 
261   public static void dumpDatabase(Database mdb, boolean systemTables)
262     throws Exception
263   {
264     dumpDatabase(mdb, systemTables, new PrintWriter(System.out, true));
265   }
266 
267   public static void dumpTable(Table table) throws Exception {
268     dumpTable(table, new PrintWriter(System.out, true));
269   }
270 
271   static void dumpDatabase(Database mdb, boolean systemTables,
272                            PrintWriter writer) throws Exception
273   {
274     writer.println("DATABASE:");
275     for(Table table : mdb) {
276       dumpTable(table, writer);
277     }
278     if(systemTables) {
279       for(String sysTableName : mdb.getSystemTableNames()) {
280         dumpTable(mdb.getSystemTable(sysTableName), writer);
281       }
282     }
283   }
284 
285   static void dumpTable(Table table, PrintWriter writer) throws Exception {
286     // make sure all indexes are read
287     for(Index index : table.getIndexes()) {
288       ((IndexImpl)index).initialize();
289     }
290     
291     writer.println("TABLE: " + table.getName());
292     List<String> colNames = new ArrayList<String>();
293     for(Column col : table.getColumns()) {
294       colNames.add(col.getName());
295     }
296     writer.println("COLUMNS: " + colNames);
297     for(Map<String, Object> row : CursorBuilder.createCursor(table)) {
298       writer.println(massageRow(row));
299     }
300   }
301 
302   private static Map<String,Object> massageRow(Map<String, Object> row)
303     throws IOException
304   {
305       for(Map.Entry<String, Object> entry : row.entrySet()) {
306         Object v = entry.getValue();
307         if(v instanceof byte[]) {
308           // make byte[] printable
309           byte[] bv = (byte[])v;
310           entry.setValue(ByteUtil.toHexString(ByteBuffer.wrap(bv), bv.length));
311         } else if(v instanceof ComplexValueForeignKey) {
312           // deref complex values
313           String str = "ComplexValue(" + v + ")" +
314             ((ComplexValueForeignKey)v).getValues();
315           entry.setValue(str);
316         }
317       }
318 
319       return row;
320   }
321 
322   static void dumpIndex(Index index) throws Exception {
323     dumpIndex(index, new PrintWriter(System.out, true));
324   }
325 
326   static void dumpIndex(Index index, PrintWriter writer) throws Exception {
327     writer.println("INDEX: " + index);
328     IndexData.EntryCursor ec = ((IndexImpl)index).cursor();
329     IndexData.Entry lastE = ec.getLastEntry();
330     IndexData.Entry e = null;
331     while((e = ec.getNextEntry()) != lastE) {
332       writer.println(e);
333     }
334   }
335 
336   static void assertSameDate(Date expected, Date found)
337   {
338     if(expected == found) {
339       return;
340     }
341     if((expected == null) || (found == null)) {
342       throw new AssertionError("Expected " + expected + ", found " + found);
343     }
344     long expTime = expected.getTime();
345     long foundTime = found.getTime();
346     // there are some rounding issues due to dates being stored as doubles,
347     // but it results in a 1 millisecond difference, so i'm not going to worry
348     // about it
349     if((expTime != foundTime) && (Math.abs(expTime - foundTime) > 1)) {
350       throw new AssertionError("Expected " + expTime + " (" + expected +
351                                "), found " + foundTime + " (" + found + ")");
352     }
353   }
354   
355   static void copyFile(File srcFile, File dstFile)
356     throws IOException
357   {
358     // FIXME should really be using commons io FileUtils here, but don't want
359     // to add dep for one simple test method
360     byte[] buf = new byte[1024];
361     OutputStream ostream = new FileOutputStream(dstFile);
362     InputStream istream = new FileInputStream(srcFile);
363     try {
364       int numBytes = 0;
365       while((numBytes = istream.read(buf)) >= 0) {
366         ostream.write(buf, 0, numBytes);
367       }
368     } finally {
369       ostream.close();
370     }
371   }
372 
373   static File createTempFile(boolean keep) throws Exception {
374     File tmp = File.createTempFile("databaseTest", ".mdb");
375     if(keep) {
376       System.out.println("Created " + tmp);
377     } else {
378       tmp.deleteOnExit();
379     }
380     return tmp;
381   }
382 
383   public static void clearTableCache(Database db) throws Exception
384   {
385     Field f = db.getClass().getDeclaredField("_tableCache");
386     f.setAccessible(true);
387     Object val = f.get(db);
388     f = val.getClass().getDeclaredField("_tables");
389     f.setAccessible(true);
390     val = f.get(val);
391     ((Map<?,?>)val).clear();
392   }
393   
394   public static byte[] toByteArray(File file)
395     throws IOException
396   {
397     return toByteArray(new FileInputStream(file), file.length());
398   }
399 
400   public static byte[] toByteArray(InputStream in, long length)
401     throws IOException
402   {
403     // FIXME should really be using commons io IOUtils here, but don't want
404     // to add dep for one simple test method
405     try {
406       DataInputStream din = new DataInputStream(in);
407       byte[] bytes = new byte[(int)length];
408       din.readFully(bytes);
409       return bytes;
410     } finally {
411       in.close();
412     }
413   }
414 
415   static void checkTestDBTable1RowABCDEFG(final TestDB testDB, final Table table, final Row row)
416           throws IOException {
417     Assert.assertEquals("testDB: " + testDB + "; table: " + table, "abcdefg", row.get("A"));
418     Assert.assertEquals("hijklmnop", row.get("B"));
419     Assert.assertEquals(new Byte((byte) 2), row.get("C"));
420     Assert.assertEquals(new Short((short) 222), row.get("D"));
421     Assert.assertEquals(new Integer(333333333), row.get("E"));
422     Assert.assertEquals(new Double(444.555d), row.get("F"));
423     final Calendar cal = Calendar.getInstance();
424     cal.setTime(row.getDate("G"));
425     Assert.assertEquals(Calendar.SEPTEMBER, cal.get(Calendar.MONTH));
426     Assert.assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
427     Assert.assertEquals(1974, cal.get(Calendar.YEAR));
428     Assert.assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
429     Assert.assertEquals(0, cal.get(Calendar.MINUTE));
430     Assert.assertEquals(0, cal.get(Calendar.SECOND));
431     Assert.assertEquals(0, cal.get(Calendar.MILLISECOND));
432     Assert.assertEquals(Boolean.TRUE, row.get("I"));
433   }
434 
435   static void checkTestDBTable1RowA(final TestDB testDB, final Table table, final Row row)
436           throws IOException {
437     Assert.assertEquals("testDB: " + testDB + "; table: " + table, "a", row.get("A"));
438     Assert.assertEquals("b", row.get("B"));
439     Assert.assertEquals(new Byte((byte) 0), row.get("C"));
440     Assert.assertEquals(new Short((short) 0), row.get("D"));
441     Assert.assertEquals(new Integer(0), row.get("E"));
442     Assert.assertEquals(new Double(0d), row.get("F"));
443     final Calendar cal = Calendar.getInstance();
444     cal.setTime(row.getDate("G"));
445     Assert.assertEquals(Calendar.DECEMBER, cal.get(Calendar.MONTH));
446     Assert.assertEquals(12, cal.get(Calendar.DAY_OF_MONTH));
447     Assert.assertEquals(1981, cal.get(Calendar.YEAR));
448     Assert.assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
449     Assert.assertEquals(0, cal.get(Calendar.MINUTE));
450     Assert.assertEquals(0, cal.get(Calendar.SECOND));
451     Assert.assertEquals(0, cal.get(Calendar.MILLISECOND));
452     Assert.assertEquals(Boolean.FALSE, row.get("I"));
453   }
454 
455 }