org.eigenbase.test
Class DiffRepository

java.lang.Object
  extended by org.eigenbase.test.DiffRepository

public class DiffRepository
extends Object

A collection of resources used by tests.

Loads files containing test input and output into memory. If there are differences, writes out a log file containing the actual output.

Typical usage is as follows:

  1. A testcase class defines a method
    
     package com.acme.test;
    
     public class MyTest extends TestCase {
         public DiffRepository getDiffRepos() {
             return DiffRepository.lookup(MyTest.class);
         }
    
         public void testToUpper() {
              getDiffRepos().assertEquals("${result}", "${string}");
         }
    
         public void testToLower() {
              getDiffRepos().assertEquals("Multi-line\nstring", "${string}");
         }
     }
    There is an accompanying reference file named after the class, com/acme/test/MyTest.ref.xml:
     <Root>
         <TestCase name="testToUpper">
             <Resource name="string">
                 <![CDATA[String to be converted to upper case]]>
             </Resource>
             <Resource name="result">
                 <![CDATA[STRING TO BE CONVERTED TO UPPER CASE]]>
             </Resource>
         </TestCase>
         <TestCase name="testToLower">
             <Resource name="result">
                 <![CDATA[multi-line
     string]]>
             </Resource>
         </TestCase>
     </Root>
     

    If any of the testcases fails, a log file is generated, called com/acme/test/MyTest.log.xml containing the actual output. The log file is otherwise identical to the reference log, so once the log file has been verified, it can simply be copied over to become the new reference log.

    If a resource or testcase does not exist, DiffRepository creates them in the log file. Because DiffRepository is so forgiving, it is very easy to create new tests and testcases.

    The lookup(java.lang.Class) method ensures that all test cases share the same instance of the repository. This is important more than one one test case fails. The shared instance ensures that the generated .log.xml file contains the actual for both test cases.

    Version:
    $Id: //open/dev/farrago/src/org/eigenbase/test/DiffRepository.java#15 $
    Author:
    jhyde

    Nested Class Summary
    static interface DiffRepository.Filter
              Callback to filter strings before returning them.
     
    Field Summary
    private  DiffRepository baseRepos
               
    private  Document doc
               
    private  DocumentBuilder docBuilder
               
    private  DiffRepository.Filter filter
               
    private  File logFile
               
    private static Map<Class,DiffRepository> mapClassToRepos
              Holds one diff-repository per class.
    private  File refFile
               
    private static String ResourceNameAttr
               
    private static String ResourceTag
               
    private  Element root
               
    private static String RootTag
               
    private static String TestCaseNameAttr
               
    private static String TestCaseOverridesAttr
               
    private static String TestCaseTag
               
     
    Constructor Summary
    private DiffRepository(File refFile, File logFile, DiffRepository baseRepos, DiffRepository.Filter filter)
              Creates a DiffRepository.
     
    Method Summary
     void amend(String expected, String actual)
               
     void assertEquals(String tag, String expected, String actual)
               
     void assertEqualsMulti(String[] tags, String[] expecteds, String[] actuals, boolean ignoreNulls)
              As assertEquals(String, String, String), but checks multiple values in parallel.
     String expand(String tag, String text)
              Expands a string containing one or more variables.
    private static File findFile(Class clazz, String suffix)
               
    private  void flushDoc()
              Flush the reference document to the file system.
    private  String get(String testCaseName, String resourceName)
              Returns a given resource from a given testcase.
    private  String getCurrentTestCaseName(boolean fail)
              Returns the name of the current testcase by looking up the call stack for a method whose name starts with "test", for example "testFoo".
    private static File getFileBase(Class clazz)
              Returns the base directory relative to which test logs are stored.
    private static File getFileBaseGivenRoot(Class clazz, String root, boolean searchParent)
               
    private static Element getResourceElement(Element testCaseElement, String resourceName)
              Returns a given resource from a given testcase.
    private  Element getTestCaseElement(String testCaseName, boolean checkOverride)
              Returns the <TestCase> element corresponding to the current test case.
    private static String getText(Element element)
              Returns the text under an element.
    private static boolean isWhitespace(String text)
               
    static DiffRepository lookup(Class clazz)
              Finds the repository instance for a given class, with no base repository or filter.
    static DiffRepository lookup(Class clazz, DiffRepository baseRepos)
              Finds the repository instance for a given class and inheriting from a given repository.
    static DiffRepository lookup(Class clazz, DiffRepository baseRepos, DiffRepository.Filter filter)
              Finds the repository instance for a given class.
    private static void removeAllChildren(Element element)
               
     void set(String resourceName, String value)
              Sets the value of a given resource of the current testcase.
    private  void update(String testCaseName, String resourceName, String value)
              Creates a new document with a given resource.
    private static void write(Document doc, Writer w)
              Serializes an XML document as text.
    private static void writeNode(Node node, org.eigenbase.xom.XMLOutput out)
               
     
    Methods inherited from class java.lang.Object
    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
     

    Field Detail

    RootTag

    private static final String RootTag
    See Also:
    Constant Field Values

    TestCaseTag

    private static final String TestCaseTag
    See Also:
    Constant Field Values

    TestCaseNameAttr

    private static final String TestCaseNameAttr
    See Also:
    Constant Field Values

    TestCaseOverridesAttr

    private static final String TestCaseOverridesAttr
    See Also:
    Constant Field Values

    ResourceTag

    private static final String ResourceTag
    See Also:
    Constant Field Values

    ResourceNameAttr

    private static final String ResourceNameAttr
    See Also:
    Constant Field Values

    mapClassToRepos

    private static final Map<Class,DiffRepository> mapClassToRepos
    Holds one diff-repository per class. It is necessary for all testcases in the same class to share the same diff-repository: if the repos gets loaded once per testcase, then only one diff is recorded.


    baseRepos

    private final DiffRepository baseRepos

    docBuilder

    private final DocumentBuilder docBuilder

    doc

    private Document doc

    root

    private final Element root

    refFile

    private final File refFile

    logFile

    private final File logFile

    filter

    private final DiffRepository.Filter filter
    Constructor Detail

    DiffRepository

    private DiffRepository(File refFile,
                           File logFile,
                           DiffRepository baseRepos,
                           DiffRepository.Filter filter)
    Creates a DiffRepository.

    Parameters:
    refFile - Reference file
    logFile - Log file
    baseRepos - Parent repository or null
    filter - Filter or null
    Method Detail

    findFile

    private static File findFile(Class clazz,
                                 String suffix)

    getFileBase

    private static File getFileBase(Class clazz)
    Returns the base directory relative to which test logs are stored. If environment variable EIGEN_HOME is set, attempts to use that; otherwise, attempts to use working directory and then its ancestors.


    getFileBaseGivenRoot

    private static File getFileBaseGivenRoot(Class clazz,
                                             String root,
                                             boolean searchParent)

    expand

    public String expand(String tag,
                         String text)
    Expands a string containing one or more variables. (Currently only works if there is one variable.)


    set

    public void set(String resourceName,
                    String value)
    Sets the value of a given resource of the current testcase.

    Parameters:
    resourceName - Name of the resource, e.g. "sql"
    value - Value of the resource

    amend

    public void amend(String expected,
                      String actual)

    get

    private String get(String testCaseName,
                       String resourceName)
    Returns a given resource from a given testcase.

    Parameters:
    testCaseName - Name of test case, e.g. "testFoo"
    resourceName - Name of resource, e.g. "sql", "plan"
    Returns:
    The value of the resource, or null if not found

    getText

    private static String getText(Element element)
    Returns the text under an element.


    getTestCaseElement

    private Element getTestCaseElement(String testCaseName,
                                       boolean checkOverride)
    Returns the <TestCase> element corresponding to the current test case.

    Parameters:
    testCaseName - Name of test case
    checkOverride - Make sure that if an element overrides an element in a base repository, it has overrides="true"
    Returns:
    TestCase element, or null if not found

    getCurrentTestCaseName

    private String getCurrentTestCaseName(boolean fail)
    Returns the name of the current testcase by looking up the call stack for a method whose name starts with "test", for example "testFoo".

    Parameters:
    fail - Whether to fail if no method is found
    Returns:
    Name of current testcase, or null if not found

    assertEquals

    public void assertEquals(String tag,
                             String expected,
                             String actual)

    assertEqualsMulti

    public void assertEqualsMulti(String[] tags,
                                  String[] expecteds,
                                  String[] actuals,
                                  boolean ignoreNulls)
    As assertEquals(String, String, String), but checks multiple values in parallel.

    If any of the values do not match, throws an AssertFailure, but still updates the other values. This is convenient, because if a unit test needs to check N values, you can correct the logfile in 1 pass through the test rather than N.

    Parameters:
    tags - Array of tags
    expecteds - Array of expected values
    actuals - Array of actual values
    ignoreNulls - Whether to ignore entries for which expected[i] == null

    update

    private void update(String testCaseName,
                        String resourceName,
                        String value)
    Creates a new document with a given resource.

    This method is synchronized, in case two threads are running test cases of this test at the same time.

    Parameters:
    testCaseName - Test case name
    resourceName - Resource name
    value - New value of resource

    flushDoc

    private void flushDoc()
    Flush the reference document to the file system.


    getResourceElement

    private static Element getResourceElement(Element testCaseElement,
                                              String resourceName)
    Returns a given resource from a given testcase.

    Parameters:
    testCaseElement - The enclosing TestCase element, e.g. <TestCase name="testFoo">.
    resourceName - Name of resource, e.g. "sql", "plan"
    Returns:
    The value of the resource, or null if not found

    removeAllChildren

    private static void removeAllChildren(Element element)

    write

    private static void write(Document doc,
                              Writer w)
    Serializes an XML document as text.

    FIXME: I'm sure there's a library call to do this, but I'm danged if I can find it. -- jhyde, 2006/2/9.


    writeNode

    private static void writeNode(Node node,
                                  org.eigenbase.xom.XMLOutput out)

    isWhitespace

    private static boolean isWhitespace(String text)

    lookup

    public static DiffRepository lookup(Class clazz)
    Finds the repository instance for a given class, with no base repository or filter.

    Parameters:
    clazz - Testcase class
    Returns:
    The diff repository shared between testcases in this class.

    lookup

    public static DiffRepository lookup(Class clazz,
                                        DiffRepository baseRepos)
    Finds the repository instance for a given class and inheriting from a given repository.

    Parameters:
    clazz - Testcase class
    baseRepos - Base class of test class
    Returns:
    The diff repository shared between testcases in this class.

    lookup

    public static DiffRepository lookup(Class clazz,
                                        DiffRepository baseRepos,
                                        DiffRepository.Filter filter)
    Finds the repository instance for a given class.

    It is important that all testcases in a class share the same repository instance. This ensures that, if two or more testcases fail, the log file will contains the actual results of both testcases.

    The baseRepos parameter is useful if the test is an extension to a previous test. If the test class has a base class which also has a repository, specify the repository here. DiffRepository will look for resources in the base class if it cannot find them in this repository. If test resources from testcases in the base class are missing or incorrect, it will not write them to the log file -- you probably need to fix the base test.

    Use the filter parameter if you expect the test to return results slightly different than in the repository. This happens if the behavior of a derived test is slightly different than a base test. If you do not specify a filter, no filtering will happen.

    Parameters:
    clazz - Testcase class
    baseRepos - Base repository
    filter - Filters each string returned by the repository
    Returns:
    The diff repository shared between testcases in this class.