001: package org.andromda.translation.ocl.testsuite;
002:
003: import java.util.Iterator;
004: import java.util.Map;
005:
006: import junit.framework.TestCase;
007: import junit.framework.TestResult;
008: import junit.framework.TestSuite;
009:
010: import org.andromda.core.AndroMDA;
011: import org.andromda.core.configuration.Configuration;
012: import org.andromda.core.configuration.Model;
013: import org.andromda.core.configuration.Namespaces;
014: import org.andromda.core.configuration.Repository;
015: import org.andromda.core.metafacade.MetafacadeFactory;
016: import org.andromda.core.metafacade.ModelAccessFacade;
017: import org.andromda.core.repository.Repositories;
018: import org.andromda.core.repository.RepositoryFacade;
019: import org.andromda.core.translation.Expression;
020: import org.andromda.core.translation.ExpressionTranslator;
021: import org.andromda.core.translation.TranslationUtils;
022: import org.apache.commons.lang.StringUtils;
023: import org.apache.log4j.Logger;
024:
025: /**
026: * This object is used to test Translations during development.
027: *
028: * @author Chad Brandon
029: */
030: public class TranslationTestProcessor extends TestCase {
031: private static Logger logger = Logger
032: .getLogger(TranslationTestProcessor.class);
033:
034: /**
035: * The shared instance of this class.
036: */
037: private static TranslationTestProcessor instance;
038:
039: /**
040: * Gets the shared instance of this class.
041: *
042: * @return the shared instance of this class.
043: */
044: public static final TranslationTestProcessor instance() {
045: if (instance == null) {
046: instance = new TranslationTestProcessor();
047: }
048: return instance;
049: }
050:
051: private TranslationTestProcessor() {
052: super ();
053: }
054:
055: /**
056: * Sets whether or not to use the trace translator.
057: *
058: * @param useTraceTranslator true/false
059: */
060: public void setUseTraceTranslator(final boolean useTraceTranslator) {
061: this .useTraceTranslator = useTraceTranslator;
062: }
063:
064: /**
065: * Indicates whether or not the TraceTranslator will run instead
066: * of the specified translator. This is helpful, in allowing us to see which
067: * expressions are being parsed in what order, etc.
068: */
069: private boolean useTraceTranslator;
070:
071: /**
072: * Thbe name of the translation to test.
073: */
074: private String translationName;
075:
076: /**
077: * Sets the name of the translation to test.
078: *
079: * @param translationName the name of the translation to test.
080: */
081: public void setTranslationName(final String translationName) {
082: this .translationName = translationName;
083: }
084:
085: /**
086: * The location of the directory that contains the test source.
087: */
088: private String testSourceDirectory;
089:
090: /**
091: * Sets the location of the directory that contains the test souce.
092: *
093: * @param testSourceDirectory
094: */
095: public void setTestSourceDirectory(final String testSourceDirectory) {
096: this .testSourceDirectory = testSourceDirectory;
097: }
098:
099: /**
100: * Handles the discovering of the translation tests.
101: */
102: private static final TranslationTestDiscoverer testDiscoverer = TranslationTestDiscoverer
103: .instance();
104:
105: /**
106: * The translation that is currently being tested.
107: */
108: private String testTranslation = null;
109:
110: /**
111: * Basic constructor - called by the test runners.
112: */
113: private TranslationTestProcessor(String testName) {
114: super (testName);
115: }
116:
117: /**
118: * The test result
119: */
120: private TestResult testResult;
121:
122: /**
123: * Sets the test result in which the result of the run will be stored.
124: *
125: * @param testResult the test result instance.
126: */
127: public void setResult(final TestResult testResult) {
128: this .testResult = testResult;
129: }
130:
131: /**
132: * Runs the test suite.
133: *
134: * @see junit.framework.TestCase#run()
135: */
136: public void runSuite() {
137: if (this .testResult == null) {
138: throw new TranslationTestProcessorException(
139: "You must set the test result before attempting to run the suite");
140: }
141: final AndroMDA andromda = AndroMDA.newInstance();
142: MetafacadeFactory factory = MetafacadeFactory.getInstance();
143: andromda.initialize(this .configuration);
144: factory.setNamespace(Namespaces.DEFAULT);
145: if (this .model == null) {
146: final Repositories repositoriesContainer = Repositories
147: .instance();
148: final Repository[] repositories = this .configuration
149: .getRepositories();
150: if (repositories != null && repositories.length > 0) {
151: final int numberOfRepositories = repositories.length;
152: for (int ctr = 0; ctr < numberOfRepositories; ctr++) {
153: final Repository repository = repositories[ctr];
154: final Model[] models = repository.getModels();
155: if (models != null) {
156: // - we just load only the first model (since it doesn't
157: // make sense
158: // to test with more than one model)
159: final Model model = models[0];
160: repositoriesContainer.loadModel(model);
161: final RepositoryFacade repositoryImplementation = repositoriesContainer
162: .getImplementation(repository.getName());
163: this .model = repositoryImplementation
164: .getModel();
165:
166: // - make sure the factory has access to the model
167: factory.setModel(this .model, model.getType());
168: }
169: }
170: }
171: }
172: this .getSuite().run(this .testResult);
173: andromda.shutdown();
174: }
175:
176: /**
177: * Assembles and retrieves the test suite of all known transation-library tests.
178: *
179: * @return non-null test suite
180: */
181: private TestSuite getSuite() {
182: testDiscoverer.discoverTests(this .testSourceDirectory);
183: final Map tests = testDiscoverer.getTests();
184: final TestSuite suite = new TestSuite();
185: for (final Iterator iterator = tests.keySet().iterator(); iterator
186: .hasNext();) {
187: final TranslationTestProcessor unitTest = new TranslationTestProcessor(
188: "testTranslation");
189:
190: // - pass on the variables to each test
191: unitTest.setConfiguration(this .configuration);
192: unitTest.setTestTranslation((String) iterator.next());
193: unitTest.model = this .model;
194: suite.addTest(unitTest);
195: }
196: return suite;
197: }
198:
199: private Configuration configuration;
200:
201: /**
202: * Sets AndroMDA configuration instance.
203: *
204: * @param configuration the AndroMDA configuration instance.
205: */
206: public void setConfiguration(final Configuration configuration) {
207: this .configuration = configuration;
208: }
209:
210: /**
211: * Sets the value for the test translation which is the translation that
212: * will be tested.
213: *
214: * @param testTranslation
215: */
216: private void setTestTranslation(String testTranslation) {
217: this .testTranslation = testTranslation;
218: }
219:
220: /**
221: * The model that was loaded.
222: */
223: private ModelAccessFacade model;
224:
225: /**
226: * Finds the classifier having <code>fullyQualifiedName</code> in the
227: * model.
228: *
229: * @param translation the translation we're using
230: * @param expression the expression from which we'll find the model element.
231: * @return Object the found model element.
232: */
233: protected Object findModelElement(String translation,
234: String expression) {
235: final String methodName = "TranslationTestProcessor.findClassifier";
236: Object element = null;
237: if (StringUtils.isNotEmpty(expression)) {
238: if (this .model == null) {
239: throw new RuntimeException(methodName
240: + " could not retrieve model from repository");
241: }
242:
243: ContextElementFinder finder = new ContextElementFinder(
244: model);
245: finder.translate(translation, expression, null);
246: element = finder.getContextElement();
247:
248: if (element == null) {
249: final String message = "No element found in model in expression --> '"
250: + expression
251: + "', please check your model or your TranslationTest file";
252: logger.error("ERROR! " + message);
253: TestCase.fail(message);
254: }
255: }
256: return element;
257: }
258:
259: /**
260: * Tests the current translation set in the currentTestTranslation property.
261: */
262: public void testTranslation() {
263: String translation = this .testTranslation;
264:
265: if (this .shouldTest(translation)) {
266: if (logger.isInfoEnabled()) {
267: logger.info("testing translation --> '" + translation
268: + "'");
269: }
270:
271: TranslationTest test = testDiscoverer.getTest(translation);
272:
273: Map expressions = test.getExpressionConfigs();
274:
275: if (expressions != null) {
276: Iterator expressionIt = expressions.keySet().iterator();
277: while (expressionIt.hasNext()) {
278: String fromExpression = (String) expressionIt
279: .next();
280:
281: // if the fromExpression body isn't defined, skip expression
282: // test
283: if (StringUtils.isEmpty(fromExpression)) {
284: if (logger.isInfoEnabled()) {
285: logger
286: .info("No body for the 'from' element was defined "
287: + "within translation test --> '"
288: + test.getUri()
289: + "', please define the body of this element with "
290: + "the expression you want to translate from");
291: }
292: continue;
293: }
294:
295: Expression translated;
296: if (useTraceTranslator) {
297: translated = TraceTranslator.getInstance()
298: .translate(translation, fromExpression,
299: null);
300: } else {
301: final ExpressionTest expressionConfig = (ExpressionTest) expressions
302: .get(fromExpression);
303: String toExpression = expressionConfig.getTo();
304:
305: Object modelElement = null;
306:
307: // - only find the model element if we have a model
308: // defined in our AndroMDA configuration
309: final Repository[] repositories = this .configuration
310: .getRepositories();
311: if (repositories != null
312: && repositories.length > 0) {
313: modelElement = this .findModelElement(
314: translation, fromExpression);
315: } else {
316: logger
317: .info("No repositories defined in configuration, not finding for model elements");
318: }
319:
320: translated = ExpressionTranslator.instance()
321: .translate(translation, fromExpression,
322: modelElement);
323:
324: if (translated != null) {
325: // remove the extra whitespace from both so as to
326: // have an accurrate comarison
327: toExpression = TranslationUtils
328: .removeExtraWhitespace(toExpression);
329: if (logger.isInfoEnabled()) {
330: logger
331: .info("translated: --> '"
332: + translated
333: .getTranslatedExpression()
334: + "'");
335: logger.info("expected: --> '"
336: + toExpression + "'");
337: }
338: TestCase.assertEquals(toExpression,
339: translated
340: .getTranslatedExpression());
341: }
342: }
343: }
344: }
345: } else {
346: if (logger.isInfoEnabled()) {
347: logger.info("skipping translation --> '" + translation
348: + "'");
349: }
350: }
351: }
352:
353: /**
354: * This method returns true if we should allow the translation to be tested.
355: * This is so we can specify on the command line, the translation to be
356: * tested, if we don't want all to be tested.
357: *
358: * @param translation
359: * @return boolean
360: */
361: private boolean shouldTest(String translation) {
362: translation = StringUtils.trimToEmpty(translation);
363: return StringUtils.isEmpty(this .translationName)
364: || (StringUtils.isNotEmpty(this .translationName) && this .translationName
365: .equals(translation));
366: }
367:
368: /**
369: * Shuts down this instance.
370: */
371: public void shutdown() {
372: testDiscoverer.shutdown();
373: instance = null;
374: }
375: }
|