View Javadoc
1   package org.melati.poem.test;
2   
3   import junit.framework.*;
4   import org.melati.poem.*;
5   
6   import java.io.*;
7   import java.lang.reflect.Method;
8   import java.lang.reflect.Modifier;
9   import java.sql.Connection;
10  import java.sql.Statement;
11  import java.util.Enumeration;
12  import java.util.Properties;
13  
14  /**
15   * A TestCase that runs in a Database session.
16   * 
17   * @author timp
18   * @since 19-May-2006
19   */
20  public class PoemTestCase extends TestCase implements Test {
21  
22    protected static TestResult result;
23    private static String propertiesFileName = "org.melati.poem.test.PoemTestCase.properties";
24    protected int maxTrans = 0;
25    boolean problem = false;
26    String dbUrl = null;
27    /**
28     * The name of the test case
29     */
30    private String fName;
31    /** Default db name */
32    private String databaseName = "melatijunit";  // change to poemtest
33    private AccessToken userToRunAs;
34    /**
35     * Constructor.
36     */
37    public PoemTestCase() {
38      super();
39      fName = null;
40    }
41  
42    public PoemTestCase(String name) {
43      super(name);
44      fName = name;
45    }
46  
47    static public void assertEquals(String message, int expected, int actual) {
48      try {
49        Assert.assertEquals(message, expected, actual);
50      } catch (Error e) {
51        result.stop();
52        throw e;
53      }
54    }
55  
56    /**
57     * Return a property.
58     *
59     * @param properties   the {@link Properties} object to look in
60     * @param propertyName the property to get
61     * @return the property value
62     * @throws RuntimeException if the property is not set
63     */
64    public static String getOrDie(Properties properties, String propertyName) {
65      String value = properties.getProperty(propertyName);
66      if (value == null)
67        throw new RuntimeException("Property " + propertyName + " not found in " + properties);
68      return value;
69    }
70  
71    /**
72     * Taken from junit-addons.sourceforge.net.
73     * Asserts that two files are equal. Throws an
74     * <tt>AssertionFailedError</tt> if they are not.<p>
75     * <p>
76     * <b>Note</b>: This assertion method rely on the standard
77     * <tt>junit.framework.Assert(String expected, String actual)</tt> method
78     * to compare the lines of the files.  JUnit > 3.8 provides a nicer way to
79     * display differences between two strings but since only lines are
80     * compared (and not entire paragraphs) you can still use JUnit 3.7.
81     */
82    public static void assertEquals(String message,
83                                    File expected,
84                                    File actual) {
85      Assert.assertNotNull(expected);
86      Assert.assertNotNull(actual);
87  
88      Assert.assertTrue("File does not exist [" + expected.getAbsolutePath() + "]", expected.exists());
89      Assert.assertTrue("File does not exist [" + actual.getAbsolutePath() + "]", actual.exists());
90  
91      Assert.assertTrue("Expected file not readable", expected.canRead());
92      Assert.assertTrue("Actual file not readable", actual.canRead());
93  
94      FileInputStream eis = null;
95      FileInputStream ais = null;
96  
97      try {
98        try {
99          eis = new FileInputStream(expected);
100         ais = new FileInputStream(actual);
101 
102         BufferedReader expData = new BufferedReader(new InputStreamReader(eis));
103         BufferedReader actData = new BufferedReader(new InputStreamReader(ais));
104 
105         Assert.assertNotNull(message, expData);
106         Assert.assertNotNull(message, actData);
107 
108         assertEquals(message, expData, actData);
109       } finally {
110         if (eis != null) eis.close();
111         if (ais != null) ais.close();
112       }
113     } catch (IOException e) {
114       throw new AssertionFailedError(e.toString());
115     }
116   }
117 
118   /**
119    * Asserts that two files are equal. Throws an
120    * <tt>AssertionFailedError</tt> if they are not.
121    */
122   public static void assertEquals(File expected,
123                                   File actual) {
124     assertEquals(null, expected, actual);
125   }
126 
127   protected void setUp() throws Exception {
128     super.setUp();
129     problem = false;
130     int freeTrans = getDb().getFreeTransactionsCount();
131     assertEquals("Not all transactions free", maxTrans, freeTrans);
132   }
133 
134   protected void tearDown() throws Exception {
135     if (!problem) {
136       checkDbUnchanged();
137       assertEquals("Not all transactions free", maxTrans, getDb().getFreeTransactionsCount());
138     }
139   }
140 
141   /**
142    * Runs the test case and collects the results in TestResult.
143    */
144   public void run(TestResult resultIn) {
145     PoemTestCase.result = resultIn;
146     super.run(resultIn);
147   }
148   
149   /**
150    * Run the test in a session.
151    *
152    * @see junit.framework.TestCase#runTest()
153    */
154   protected void runTest() throws Throwable {
155     assertNotNull(fName);
156     try {
157       // use getMethod to get all public inherited
158       // methods. getDeclaredMethods returns all
159       // methods of this class but excludes the
160       // inherited ones.
161       final Method runMethod = getClass().getMethod(fName, (Class[])null);
162       if (!Modifier.isPublic(runMethod.getModifiers())) {
163         fail("Method \"" + fName + "\" should be public");
164       }
165       // Ensures that we are invoking on
166       // the object that method belongs to.
167       final Object _this = this;
168       getDb().inSession(getUserToRunAs(),
169           new PoemTask() {
170             public void run() {
171               try {
172                 runMethod.invoke(_this, new Object[0]);
173               } catch (Throwable e) {
174                 problem = true;
175                 if (e.getCause() instanceof ComparisonFailure) {
176                   throw (ComparisonFailure) e.getCause();
177                 } else {
178                   throw new RuntimeException(e);
179                 }
180               }
181             }
182 
183             public String toString() {
184               return "PoemTestCase:"+ fName;
185             }
186           });
187     } catch (NoSuchMethodException e) {
188       fail("Method \"" + fName + "\" not found");
189     }
190   }
191  
192   protected void checkDbUnchanged() {
193     getDb().inSession(AccessToken.root,
194         new PoemTask() {
195           public void run() {
196             databaseUnchanged();
197           }
198         });
199 
200   }
201 
202   protected void databaseUnchanged() {
203     //assertEquals("Setting changed", 0, getDb().getSettingTable().count());
204     assertEquals("Group changed", 1, getDb().getGroupTable().count());
205     assertEquals("GroupMembership changed", 1, getDb().getGroupMembershipTable().count());
206     assertEquals("Capability changed", 5, getDb().getCapabilityTable().count());
207     assertEquals("GroupCapability changed", 1, getDb().getGroupCapabilityTable().count());
208     assertEquals("TableCategory changed", 3, getDb().getTableCategoryTable().count());
209     assertEquals("User changed", 2, getDb().getUserTable().count());
210     ColumnInfo newOne = null;
211     try{
212       newOne = (ColumnInfo)getDb().getColumnInfoTable().getObject(69);
213     } catch (Exception e) {
214     }
215     if (newOne != null) {
216       String errStr = "Extra column in " + getDb() + " " + newOne.getName() + " " + newOne.getTableinfo().getName();
217       newOne.delete();
218       fail(errStr);
219     } else {
220       if (getDb().getDbms().canDropColumns()) {
221         assertEquals("ColumnInfo changed", 69, getDb().getColumnInfoTable().count());
222         assertEquals("TableInfo changed", 9, getDb().getTableInfoTable().count());
223         checkTablesAndColumns(9,69);
224       }
225     }
226   }
227 
228   protected void dropTable(String tableName) {
229     Connection c = getDb().getCommittedConnection();
230     Table<?> table = null;
231     try {
232       table = getDb().getTable(tableName);
233       Statement s = c.createStatement();
234       if (table != null && table.getTableInfo().statusExistent()) {
235         s.executeUpdate("DROP TABLE " + getDb().getDbms().getQuotedName(tableName));
236       }
237       s.close();
238       c.commit();
239     } catch (NoSuchTablePoemException e) {
240       e = null;
241     } catch (Exception e) {
242       e.printStackTrace();
243       fail("Something bombed");
244     }
245 
246   }
247   
248   protected void checkTablesAndColumns(int tableCount, int columnCount) {
249     checkTables(tableCount);
250     checkColumns(columnCount);
251   }
252 
253   protected void checkTables(int tableCount) {
254     Enumeration<Table<?>> e = getDb().tables();
255     int count = 0;
256     while (e.hasMoreElements()) {
257       Table<?> t = e.nextElement();
258       if (t.getTableInfo().statusExistent()) count++;
259     }
260     if (count != tableCount) {
261       System.out.println(fName + " Additional tables - expected:" +
262               tableCount + " found:" + count);
263       e = getDb().tables();
264       while (e.hasMoreElements()) {
265         Table<?> t = e.nextElement();
266         System.out.println(t.getTableInfo().getTroid() + " " +
267                 t.getTableInfo().statusExistent() + " " +
268             t);
269       }
270     }
271     assertEquals(tableCount, count);
272   }
273 
274   protected void checkColumns(int columnCount) {
275     Enumeration<Column<?>> e = getDb().columns();
276     int count = 0;
277     while (e.hasMoreElements()) {
278       Column<?> c = e.nextElement();
279       if (c.getColumnInfo().statusExistent())
280         count++;
281     }
282     if (count != columnCount) {
283       System.out.println(fName + " Additional columns - expected:" +
284               columnCount + " found:" + count);
285       e = getDb().columns();
286       while (e.hasMoreElements()) {
287         System.out.println(e.nextElement());
288       }
289     }
290     assertEquals(columnCount, count);
291   }
292 
293   protected <P extends Persistent> void dumpTable(Table<P> t) {
294     Enumeration<P> them = t.selection();
295     while (them.hasMoreElements()) {
296       P it = them.nextElement();
297       System.err.println(it.getTroid() + " " + it.getCooked("name") + " " +
298           it.getTable().getName());
299     }
300 
301   }
302 
303   /**
304    * Gets the name of a TestCase.
305    *
306    * @return returns a String
307    */
308   public String getName() {
309     return fName;
310   }
311 
312   /**
313    * Sets the name of a TestCase.
314    *
315    * @param name
316    *          The name to set
317    */
318   public void setName(String name) {
319     fName = name;
320   }
321 
322   /**
323    * @return Returns the db.
324    */
325   public Database getDb() {
326     return getDb(getDatabaseName());
327   }
328 
329   /**
330    * @param dbNameP the name of the logical db
331    * @return a Database
332    */
333   public Database getDb(String dbNameP) {
334     if (dbNameP == null)
335       throw new NullPointerException();
336     return getDatabase(dbNameP);
337   }
338 
339   /**
340    * @param name the name of the logical db
341    * @return a Database
342    */
343   public Database getDatabase(String name){
344     Properties defs = getProperties();
345     String pref = "org.melati.poem.test.PoemTestCase." + name + ".";
346     maxTrans = new Integer(getOrDie(defs, pref + "maxtransactions")).intValue();
347     String url = getOrDie(defs, pref + "url");
348     return PoemDatabaseFactory.getDatabase(name,
349         url,
350             getOrDie(defs, pref + "user"),
351             getOrDie(defs, pref + "password"),
352             getOrDie(defs, pref + "class"),
353             getOrDie(defs, pref + "dbmsclass"),
354             new Boolean(getOrDie(defs, pref + "addconstraints")).booleanValue(),
355             new Boolean(getOrDie(defs, pref + "logsql")).booleanValue(),
356             new Boolean(getOrDie(defs, pref + "logcommits")).booleanValue(),
357             maxTrans);
358   }
359   
360   /**
361    * @return the user
362    */
363   public AccessToken getUserToRunAs() {
364     if (userToRunAs == null) return AccessToken.root;
365     return userToRunAs;
366   }
367 
368   /**
369    * @param userToRunAs the user
370    */
371   public void setUserToRunAs(AccessToken userToRunAs) {
372     if (userToRunAs == null)
373       this.userToRunAs = AccessToken.root;
374     else
375       this.userToRunAs = userToRunAs;
376   }
377 
378   /**
379    * @return a Properties
380    */
381   public Properties getProperties() {
382     InputStream is = PoemTestCase.class.getResourceAsStream(getPropertiesFileName());
383 
384     if (is == null)
385       throw new RuntimeException(
386               new FileNotFoundException(getPropertiesFileName() + ": is it in CLASSPATH?"));
387 
388     Properties them = new Properties();
389     try {
390       them.load(is);
391       is.close();
392     } catch (IOException e) {
393       throw new RuntimeException(
394               new IOException("Corrupt properties file `" + getPropertiesFileName() + "': " +
395       e.getMessage()));
396     }
397     return them;
398   }
399 
400   /**
401    * @return the properties name
402    */
403   public String getPropertiesFileName() {
404     return propertiesFileName;
405   }
406 
407   /**
408    * @param propertiesFileNameIn the name to set
409    */
410   public void setPropertiesFileName(String propertiesFileNameIn) {
411     PoemTestCase.propertiesFileName = propertiesFileNameIn;
412   }
413 
414   /**
415    * @return the db name
416    */
417   public String getDatabaseName() {
418     return databaseName;
419   }
420 
421   /**
422    * @param databaseName the db name
423    */
424   public void setDatabaseName(String databaseName) {
425     this.databaseName = databaseName;
426   }
427 
428   /**
429    * Some test runners seem to think there should be a test in this file.
430    */
431   public void testNothing() { 
432     
433   }
434 }