View Javadoc
1   package org.andromda.translation.ocl.testsuite;
2   
3   import java.util.Iterator;
4   import java.util.Map;
5   import junit.framework.TestCase;
6   import junit.framework.TestResult;
7   import junit.framework.TestSuite;
8   import org.andromda.core.AndroMDA;
9   import org.andromda.core.configuration.Configuration;
10  import org.andromda.core.configuration.Model;
11  import org.andromda.core.configuration.Namespaces;
12  import org.andromda.core.configuration.Repository;
13  import org.andromda.core.metafacade.MetafacadeFactory;
14  import org.andromda.core.metafacade.ModelAccessFacade;
15  import org.andromda.core.repository.Repositories;
16  import org.andromda.core.repository.RepositoryFacade;
17  import org.andromda.core.translation.Expression;
18  import org.andromda.core.translation.ExpressionTranslator;
19  import org.andromda.core.translation.TranslationUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.log4j.Logger;
22  
23  /**
24   * This object is used to test Translations during development.
25   *
26   * @author Chad Brandon
27   */
28  public final class TranslationTestProcessor
29          extends TestCase
30  {
31      private static final Logger logger = Logger.getLogger(TranslationTestProcessor.class);
32  
33      /**
34       * The shared instance of this class.
35       */
36      private static TranslationTestProcessor instance;
37  
38      /**
39       * Gets the shared instance of this class.
40       *
41       * @return the shared instance of this class.
42       */
43      public static final TranslationTestProcessor instance()
44      {
45          if (instance == null)
46          {
47              instance = new TranslationTestProcessor();
48          }
49          return instance;
50      }
51  
52      private TranslationTestProcessor()
53      {
54          super();
55      }
56  
57      /**
58       * Sets whether or not to use the trace translator.
59       *
60       * @param useTraceTranslator true/false
61       */
62      public void setUseTraceTranslator(final boolean useTraceTranslator)
63      {
64          this.useTraceTranslator = useTraceTranslator;
65      }
66  
67      /**
68       * Indicates whether or not the TraceTranslator will run instead
69       * of the specified translator. This is helpful, in allowing us to see which
70       * expressions are being parsed in what order, etc.
71       */
72      private boolean useTraceTranslator;
73  
74      /**
75       * Thbe name of the translation to test.
76       */
77      private String translationName;
78  
79      /**
80       * Sets the name of the translation to test.
81       *
82       * @param translationName the name of the translation to test.
83       */
84      public void setTranslationName(final String translationName)
85      {
86          this.translationName = translationName;
87      }
88  
89      /**
90       * The location of the directory that contains the test source.
91       */
92      private String testSourceDirectory;
93  
94      /**
95       * Sets the location of the directory that contains the test souce.
96       *
97       * @param testSourceDirectory
98       */
99      public void setTestSourceDirectory(final String testSourceDirectory)
100     {
101         this.testSourceDirectory = testSourceDirectory;
102     }
103 
104     /**
105      * Handles the discovering of the translation tests.
106      */
107     private static final TranslationTestDiscoverer testDiscoverer = TranslationTestDiscoverer.instance();
108 
109     /**
110      * The translation that is currently being tested.
111      */
112     private String testTranslation = null;
113 
114     /**
115      * Basic constructor - called by the test runners.
116      */
117     private TranslationTestProcessor(String testName)
118     {
119         super(testName);
120     }
121 
122     /**
123      * The test result
124      */
125     private TestResult testResult;
126 
127     /**
128      * Sets the test result in which the result of the run will be stored.
129      *
130      * @param testResult the test result instance.
131      */
132     public void setResult(final TestResult testResult)
133     {
134         this.testResult = testResult;
135     }
136 
137     /**
138      * Runs the test suite.
139      *
140      * @see junit.framework.TestCase#run()
141      */
142     public void runSuite()
143     {
144         if (this.testResult == null)
145         {
146             throw new TranslationTestProcessorException(
147                     "You must set the test result before attempting to run the suite");
148         }
149         final AndroMDA andromda = AndroMDA.newInstance();
150         MetafacadeFactory factory = MetafacadeFactory.getInstance();
151         andromda.initialize(this.configuration);
152         factory.setNamespace(Namespaces.DEFAULT);
153         if (this.model == null)
154         {
155             final Repositories repositoriesContainer = Repositories.instance();
156             final Repository[] repositories = this.configuration.getRepositories();
157             if (repositories != null && repositories.length > 0)
158             {
159                 final int numberOfRepositories = repositories.length;
160                 for (int ctr = 0; ctr < numberOfRepositories; ctr++)
161                 {
162                     final Repository repository = repositories[ctr];
163                     final Model[] models = repository.getModels();
164                     if (models != null)
165                     {
166                         // - we just load only the first model (since it doesn't
167                         // make sense
168                         // to test with more than one model)
169                         final Model model = models[0];
170                         repositoriesContainer.loadModel(model);
171                         final RepositoryFacade repositoryImplementation =
172                                 repositoriesContainer.getImplementation(repository.getName());
173                         this.model = repositoryImplementation.getModel();
174 
175                         // - make sure the factory has access to the model
176                         factory.setModel(this.model, model.getType());
177                     }
178                 }
179             }
180         }
181         this.getSuite().run(this.testResult);
182         andromda.shutdown();
183     }
184 
185     /**
186      * Assembles and retrieves the test suite of all known transation-library tests.
187      *
188      * @return non-null test suite
189      */
190     private TestSuite getSuite()
191     {
192         testDiscoverer.discoverTests(this.testSourceDirectory);
193         final Map tests = testDiscoverer.getTests();
194         final TestSuite suite = new TestSuite();
195         for (final Iterator iterator = tests.keySet().iterator(); iterator.hasNext();)
196         {
197             final TranslationTestProcessor unitTest = new TranslationTestProcessor("testTranslation");
198 
199             // - pass on the variables to each test
200             unitTest.setConfiguration(this.configuration);
201             unitTest.setTestTranslation((String) iterator.next());
202             unitTest.model = this.model;
203             suite.addTest(unitTest);
204         }
205         return suite;
206     }
207 
208     private Configuration configuration;
209 
210     /**
211      * Sets AndroMDA configuration instance.
212      *
213      * @param configuration the AndroMDA configuration instance.
214      */
215     public void setConfiguration(final Configuration configuration)
216     {
217         this.configuration = configuration;
218     }
219 
220     /**
221      * Sets the value for the test translation which is the translation that
222      * will be tested.
223      *
224      * @param testTranslation
225      */
226     private void setTestTranslation(String testTranslation)
227     {
228         this.testTranslation = testTranslation;
229     }
230 
231     /**
232      * The model that was loaded.
233      */
234     private ModelAccessFacade model;
235 
236     /**
237      * Finds the classifier having <code>fullyQualifiedName</code> in the
238      * model.
239      *
240      * @param translation the translation we're using
241      * @param expression  the expression from which we'll find the model element.
242      * @return Object the found model element.
243      */
244     protected Object findModelElement(
245             String translation,
246             String expression)
247     {
248         final String methodName = "TranslationTestProcessor.findClassifier";
249         Object element = null;
250         if (StringUtils.isNotBlank(expression))
251         {
252             if (this.model == null)
253             {
254                 throw new RuntimeException(methodName + " could not retrieve model from repository");
255             }
256 
257             ContextElementFinder finder = new ContextElementFinder(model);
258             finder.translate(
259                     translation,
260                     expression,
261                     null);
262             element = finder.getContextElement();
263 
264             if (element == null)
265             {
266                 final String message =
267                         "No element found in model in expression --> '" + expression +
268                                 "' for translation ' " + translation + "', please check your model or your TranslationTest file";
269                 logger.error("ERROR! " + message);
270                 TestCase.fail(message);
271             }
272         }
273         return element;
274     }
275 
276     /**
277      * Tests the current translation set in the currentTestTranslation property.
278      */
279     public void testTranslation()
280     {
281         String translation = this.testTranslation;
282 
283         if (this.shouldTest(translation))
284         {
285             if (logger.isInfoEnabled())
286             {
287                 logger.info("testing translation --> '" + translation + '\'');
288             }
289 
290             TranslationTest test = testDiscoverer.getTest(translation);
291 
292             Map<String, ExpressionTest> expressions = test.getExpressionConfigs();
293 
294             if (expressions != null)
295             {
296                 for (String fromExpression : expressions.keySet())
297                 {
298                     // if the fromExpression body isn't defined, skip expression
299                     // test
300                     if (StringUtils.isEmpty(fromExpression))
301                     {
302                         if (logger.isInfoEnabled())
303                         {
304                             logger.info(
305                                     "No body for the 'from' element was defined " + "within translation test --> '" +
306                                             test.getUri() + "', please define the body of this element with " +
307                                             "the expression you want to translate from");
308                         }
309                         continue;
310                     }
311 
312                     Expression translated;
313                     if (useTraceTranslator)
314                     {
315                         translated = TraceTranslator.getInstance().translate(
316                                 translation,
317                                 fromExpression,
318                                 null);
319                     }
320                     else
321                     {
322                         final ExpressionTest expressionConfig = expressions.get(fromExpression);
323                         String toExpression = expressionConfig.getTo();
324 
325                         Object modelElement = null;
326 
327                         // - only find the model element if we have a model
328                         // defined in our AndroMDA configuration
329                         final Repository[] repositories = this.configuration.getRepositories();
330                         if (repositories != null && repositories.length > 0)
331                         {
332                             modelElement = this.findModelElement(
333                                     translation,
334                                     fromExpression);
335                         }
336                         else
337                         {
338                             logger.info("No repositories defined in configuration, not finding for model elements");
339                         }
340 
341                         translated =
342                                 ExpressionTranslator.instance().translate(
343                                         translation,
344                                         fromExpression,
345                                         modelElement);
346 
347                         if (translated != null)
348                         {
349                             // remove the extra whitespace from both so as to
350                             // have an accurrate comarison
351                             toExpression = TranslationUtils.removeExtraWhitespace(toExpression);
352                             if (logger.isInfoEnabled())
353                             {
354                                 logger.info("translated: --> '" + translated.getTranslatedExpression() + '\'');
355                                 logger.info("expected:   --> '" + toExpression + '\'');
356                             }
357                             TestCase.assertEquals(
358                                     toExpression,
359                                     translated.getTranslatedExpression());
360                         }
361                     }
362                 }
363             }
364         } else
365         {
366             if (logger.isInfoEnabled())
367             {
368                 logger.info("skipping translation --> '" + translation + '\'');
369             }
370         }
371     }
372 
373     /**
374      * This method returns true if we should allow the translation to be tested.
375      * This is so we can specify on the command line, the translation to be
376      * tested, if we don't want all to be tested.
377      *
378      * @param translation
379      * @return boolean
380      */
381     private boolean shouldTest(String translation)
382     {
383         translation = StringUtils.trimToEmpty(translation);
384         return StringUtils.isEmpty(this.translationName) ||
385                 (StringUtils.isNotBlank(this.translationName) && this.translationName.equals(translation));
386     }
387 
388     /**
389      * Shuts down this instance.
390      */
391     public void shutdown()
392     {
393         testDiscoverer.shutdown();
394         TranslationTestProcessor.instance = null;
395     }
396 }