/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.internal.test; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.FileFileFilter; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.flex.compiler.clients.MXMLJSC; import org.apache.flex.compiler.codegen.as.IASEmitter; import org.apache.flex.compiler.codegen.mxml.IMXMLEmitter; import org.apache.flex.compiler.config.Configurator; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.driver.IBackend; import org.apache.flex.compiler.internal.codegen.as.ASEmitterTokens; import org.apache.flex.compiler.internal.codegen.as.ASFilterWriter; import org.apache.flex.compiler.internal.codegen.js.flexjs.JSFlexJSEmitterTokens; import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; import org.apache.flex.compiler.internal.projects.FlexJSProject; import org.apache.flex.compiler.internal.projects.FlexProject; import org.apache.flex.compiler.internal.projects.FlexProjectConfigurator; import org.apache.flex.compiler.internal.projects.ISourceFileHandler; import org.apache.flex.compiler.internal.targets.JSTarget; import org.apache.flex.compiler.internal.tree.as.FunctionNode; import org.apache.flex.compiler.internal.tree.as.ImportNode; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.mxml.IMXMLNamespaceMapping; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IFileNode; import org.apache.flex.compiler.tree.mxml.IMXMLFileNode; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.compiler.units.requests.ISyntaxTreeRequestResult; import org.apache.flex.utils.EnvProperties; import org.apache.flex.compiler.visitor.as.IASBlockWalker; import org.apache.flex.compiler.visitor.mxml.IMXMLBlockWalker; import org.apache.flex.utils.FilenameNormalization; import org.apache.flex.utils.ITestAdapter; import org.apache.flex.utils.TestAdapterFactory; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @Ignore public class TestBase implements ITestBase { private static ITestAdapter testAdapter = TestAdapterFactory.getTestAdapter(); protected List<ICompilerProblem> errors; protected static EnvProperties env = EnvProperties.initiate(); protected static Workspace workspace = new Workspace(); protected FlexJSProject project; protected IBackend backend; protected ASFilterWriter writer; protected IASEmitter asEmitter; protected IMXMLEmitter mxmlEmitter; protected IASBlockWalker asBlockWalker; protected IMXMLBlockWalker mxmlBlockWalker; protected String inputFileExtension; protected String mCode; protected File tempDir; private List<File> sourcePaths = new ArrayList<File>(); private List<File> libraries = new ArrayList<File>(); private List<IMXMLNamespaceMapping> namespaceMappings = new ArrayList<IMXMLNamespaceMapping>(); @Before public void setUp() { assertNotNull("Environment variable FLEX_HOME is not set", env.SDK); assertNotNull("Environment variable PLAYERGLOBAL_HOME is not set", env.FPSDK); errors = new ArrayList<ICompilerProblem>(); if (project == null) { backend = createBackend(); project = new FlexJSProject(workspace, backend); project.setProxyBaseClass("flash.utils.Proxy"); } project.setProblems(errors); FlexProjectConfigurator.configure(project); try { Configurator projectConfigurator = backend.createConfigurator(); project.setTargetSettings(projectConfigurator.getTargetSettings(null)); } catch (UnsupportedOperationException e) { } writer = backend.createWriterBuffer(project); try { ISourceFileHandler sfh = backend.getSourceFileHandlerInstance(); inputFileExtension = "." + sfh.getExtensions()[0]; } catch (Exception e) { inputFileExtension = ".as"; } sourcePaths = new ArrayList<File>(); libraries = new ArrayList<File>(); namespaceMappings = new ArrayList<IMXMLNamespaceMapping>(); tempDir = new File(TestAdapterFactory.getTestAdapter().getTempDir()); // ensure this exists } @After public void tearDown() { backend = null; writer = null; } protected IBackend createBackend() { return null; } protected void assertErrors(String errorReport) { StringBuilder actualErrors = new StringBuilder(); for (ICompilerProblem problem : errors) { if (problem.toString().equals("An externally-visible definition with the name 'Object' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'String' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'Boolean' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'Number' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'Error' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'RangeError' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'ReferenceError' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'TypeError' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'int' was unexpectedly found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'uint' was unexpectedly found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.Object' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.String' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.Number' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.Boolean' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.Error' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.RangeError' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.ReferenceError' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.TypeError' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.int' was found.")) continue; if (problem.toString().equals("No externally-visible definition with the name 'externals.as.classes.uint' was found.")) continue; if (problem.toString().equals("An externally-visible definition with the name 'FalconTest_A' was unexpectedly found.")) continue; if (problem.toString().startsWith("No externally-visible definition with the name 'TestFlexJSGlobalFunctions")) continue; actualErrors.append(problem.toString()); } assertThat(actualErrors.toString(), is(errorReport)); } protected void assertOut(String code, boolean keepMetadata) { mCode = removeGeneratedString(writer.toString()); if (!keepMetadata) mCode = removeMetadata(mCode); //System.out.println(mCode); /*if (!code.equals(mCode)) { System.out.println("mCode:\n"+mCode); System.out.println("code:\n"+code); }*/ assertThat(mCode, is(code)); } protected void assertOutPostProcess(String code, boolean keepMetadata) { mCode = removeGeneratedString(asEmitter.postProcess(writer.toString())); if (!keepMetadata) mCode = removeMetadata(mCode); //System.out.println(mCode); /*if (!code.equals(mCode)) { System.out.println("mCode:\n"+mCode); System.out.println("code:\n"+code); }*/ assertThat(mCode, is(code)); } protected void assertOutMXMLPostProcess(String code, boolean keepMetadata) { mCode = removeGeneratedString(mxmlEmitter.postProcess(writer.toString())); if (!keepMetadata) mCode = removeMetadata(mCode); //System.out.println(mCode); /*if (!code.equals(mCode)) { System.out.println("mCode:\n"+mCode); System.out.println("code:\n"+code); }*/ assertThat(mCode, is(code)); } protected void assertOut(String code) { assertOut(code, false); } protected void assertOutWithMetadata(String code) { assertOut(code, true); } protected String removeMetadata(String code) { int c = code.indexOf("\n\n\n/**\n * Metadata"); if (c != -1) return code.substring(0, c); return code; } protected String removeGeneratedString(String code) { int c = code.indexOf(" * Generated by Apache Flex Cross-Compiler"); if (c != -1) { int c2 = code.indexOf("\n", c); String newString = code.substring(0, c); newString += code.substring(c2 + 1); return newString; } return code; } @Override public String toString() { return writer.toString(); } protected IFileNode compileAS(String input) { return compileAS(input, false, ""); } protected IFileNode compileAS(String input, boolean isFileName, String inputDir) { return compileAS(input, isFileName, inputDir, true); } protected IFileNode compileAS(String input, boolean isFileName, String inputDir, boolean useTempFile) { return (IFileNode) compile(input, isFileName, inputDir, useTempFile); } protected IASNode compile(String input, boolean isFileName, String inputDir, boolean useTempFile) { File tempFile = (useTempFile) ? writeCodeToTempFile(input, isFileName, inputDir) : new File(inputDir + File.separator + input + inputFileExtension); addDependencies(); String normalizedMainFileName = FilenameNormalization .normalize(tempFile.getAbsolutePath()); Collection<ICompilationUnit> mainFileCompilationUnits = workspace .getCompilationUnits(normalizedMainFileName, project); ICompilationUnit cu = null; for (ICompilationUnit cu2 : mainFileCompilationUnits) { if (cu2 != null) cu = cu2; } IASNode fileNode = null; try { ISyntaxTreeRequestResult result = cu.getSyntaxTreeRequest().get(); ICompilerProblem[] problems = result.getProblems(); if (problems.length > 0) { for (ICompilerProblem problem : problems) System.out.println(problem.toString()); return null; } fileNode = result.getAST(); project.getDependencies(cu); } catch (InterruptedException e) { e.printStackTrace(); } return fileNode; } protected List<String> compileProject(String inputFileName, String inputDirName) { return compileProject(inputFileName, inputDirName, new StringBuilder(), true); } protected List<String> compileProject(String inputFileName, String inputDirName, StringBuilder sb, boolean ignoreErrors) { List<String> compiledFileNames = new ArrayList<String>(); String mainFileName = new File(TestAdapterFactory.getTestAdapter().getUnitTestBaseDir(), inputDirName + "/" + inputFileName + inputFileExtension).getPath(); addDependencies(); ICompilationUnit mainCU = Iterables .getOnlyElement(workspace.getCompilationUnits( FilenameNormalization.normalize(mainFileName), project)); project.mainCU = mainCU; Configurator projectConfigurator = backend.createConfigurator(); JSTarget target = (JSTarget) backend.createTarget(project, projectConfigurator.getTargetSettings(null), null); ArrayList<ICompilerProblem> errors = new ArrayList<ICompilerProblem>(); target.build(mainCU, errors); if (!ignoreErrors && errors.size() > 0) { for (ICompilerProblem error : errors) { String fn = error.getSourcePath(); if(fn != null) { int c = fn.indexOf(testAdapter.getUnitTestBaseDir().getPath()); fn = fn.substring(c); sb.append(fn); sb.append("(" + error.getLine() + ":" + error.getColumn() + ")\n"); sb.append(error.toString() + "\n"); } } System.out.println(sb.toString()); return compiledFileNames; } List<ICompilationUnit> reachableCompilationUnits = project .getReachableCompilationUnitsInSWFOrder(ImmutableSet.of(mainCU)); for (final ICompilationUnit cu : reachableCompilationUnits) { try { ICompilationUnit.UnitType cuType = cu.getCompilationUnitType(); if (cuType == ICompilationUnit.UnitType.AS_UNIT || cuType == ICompilationUnit.UnitType.MXML_UNIT) { File outputRootDir = new File( FilenameNormalization.normalize(tempDir + File.separator + inputDirName)); String qname = cu.getQualifiedNames().get(0); compiledFileNames.add(qname.replace(".", "/")); final File outputClassFile = getOutputClassFile(qname + "_output", outputRootDir); ASFilterWriter writer = backend.createWriterBuffer(project); IASEmitter emitter = backend.createEmitter(writer); IASBlockWalker walker = backend.createWalker(project, errors, emitter); walker.visitCompilationUnit(cu); //System.out.println(writer.toString()); BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(outputClassFile)); out.write(emitter.postProcess(writer.toString()).getBytes()); out.flush(); out.close(); } } catch (Exception e) { //System.out.println(e.getMessage()); } } File outputRootDir = new File( FilenameNormalization.normalize(tempDir + File.separator + inputDirName)); String qname; try { qname = mainCU.getQualifiedNames().get(0); final File outputClassFile = getOutputClassFile(qname + "_output", outputRootDir); appendLanguageAndXML(outputClassFile.getAbsolutePath(), qname); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return compiledFileNames; } protected void writeFile(String path, String content, boolean append) throws IOException { File tgtFile = new File(path); if (!tgtFile.exists()) tgtFile.createNewFile(); FileWriter fw = new FileWriter(tgtFile, append); fw.write(content); fw.close(); } private void appendLanguageAndXML(String path, String projectName) throws IOException { StringBuilder appendString = new StringBuilder(); appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); appendString.append(ASEmitterTokens.PAREN_OPEN.getToken()); appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); appendString.append(JSFlexJSEmitterTokens.LANGUAGE_QNAME.getToken()); appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken()); appendString.append(ASEmitterTokens.SEMICOLON.getToken()); appendString.append("\n"); String fileData = readCode(new File(path)); int reqidx = fileData.indexOf(appendString.toString()); if (reqidx == -1 && project instanceof FlexJSProject && ((FlexJSProject)project).needLanguage) { boolean afterProvide = false; reqidx = fileData.lastIndexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); if (reqidx == -1) { afterProvide = true; reqidx = fileData.lastIndexOf(JSGoogEmitterTokens.GOOG_PROVIDE.getToken()); } reqidx = fileData.indexOf(";", reqidx); String after = fileData.substring(reqidx + 1); String before = fileData.substring(0, reqidx + 1); if (afterProvide) before += "\n"; String s = before + "\n" + appendString.toString() + after; writeFile(path, s, false); } StringBuilder appendStringXML = new StringBuilder(); appendStringXML.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); appendStringXML.append(ASEmitterTokens.PAREN_OPEN.getToken()); appendStringXML.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); appendStringXML.append(IASLanguageConstants.XML); appendStringXML.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); appendStringXML.append(ASEmitterTokens.PAREN_CLOSE.getToken()); appendStringXML.append(ASEmitterTokens.SEMICOLON.getToken()); appendStringXML.append("\n"); if (project instanceof FlexJSProject && ((FlexJSProject)project).needXML) { fileData = readCode(new File(path)); reqidx = fileData.indexOf(appendStringXML.toString()); if (reqidx == -1) { boolean afterProvide = false; reqidx = fileData.lastIndexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); if (reqidx == -1) { afterProvide = true; reqidx = fileData.lastIndexOf(JSGoogEmitterTokens.GOOG_PROVIDE.getToken()); } reqidx = fileData.lastIndexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); if (reqidx == -1) reqidx = fileData.lastIndexOf(JSGoogEmitterTokens.GOOG_PROVIDE.getToken()); reqidx = fileData.indexOf(";", reqidx); String after = fileData.substring(reqidx + 1); String before = fileData.substring(0, reqidx + 1); if (afterProvide) before += "\n"; String s = before + "\n" + appendStringXML.toString() + after; writeFile(path, s, false); } } } protected String readCode(File file) { String code = ""; try { BufferedReader in = new BufferedReader(new InputStreamReader( new FileInputStream(file), "UTF8")); String line = in.readLine(); while (line != null) { code += line + "\n"; line = in.readLine(); } code = code.substring(0, code.length() - 1); in.close(); } catch (Exception e) { // nothing to see, move along... } return code; } protected File getOutputClassFile(String qname, File outputFolder) { String[] cname = qname.split("\\."); String sdirPath = outputFolder + File.separator; if (cname.length > 0) { for (int i = 0, n = cname.length - 1; i < n; i++) { sdirPath += cname[i] + File.separator; } File sdir = new File(sdirPath); if (!sdir.exists()) sdir.mkdirs(); qname = cname[cname.length - 1]; } return new File(sdirPath + qname + "." + backend.getOutputExtension()); } protected IMXMLFileNode compileMXML(String input) { return compileMXML(input, false, ""); } protected IMXMLFileNode compileMXML(String input, boolean isFileName, String inputDir) { return compileMXML(input, isFileName, inputDir, true); } protected IMXMLFileNode compileMXML(String input, boolean isFileName, String inputDir, boolean useTempFile) { return (IMXMLFileNode) compile(input, isFileName, inputDir, useTempFile); } protected File writeCodeToTempFile(String input, boolean isFileName, String inputDir) { File tempASFile = null; try { String tempFileName = (isFileName) ? input : getClass() .getSimpleName(); tempASFile = File.createTempFile(tempFileName, inputFileExtension, new File(TestAdapterFactory.getTestAdapter().getTempDir())); tempASFile.deleteOnExit(); String code = ""; if (!isFileName) { code = input; } else { code = getCodeFromFile(input, false, inputDir); } BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tempASFile),"UTF-8")); out.write(code); out.close(); } catch (IOException e1) { e1.printStackTrace(); } return tempASFile; } protected void writeResultToFile(String result, String fileName) { BufferedWriter writer = null; try { writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(new File(tempDir, fileName + ".js")), "utf-8")); writer.write(result); } catch (IOException e1) { e1.printStackTrace(); } finally { try { writer.close(); } catch (Exception ex) { } } } /** * Overridable setup of dependencies, default adds source, libraries and * namepsaces. * <p> * The test will then set the dependencies on the current * {@link ICompilerProject}. */ protected void addDependencies() { addSourcePaths(sourcePaths); addLibraries(libraries); addNamespaceMappings(namespaceMappings); project.setSourcePath(sourcePaths); project.setLibraries(libraries); project.setNamespaceMappings(namespaceMappings); } protected void addLibraries(List<File> libraries) { } protected void addSourcePaths(List<File> sourcePaths) { sourcePaths.add(tempDir); } protected void addNamespaceMappings( List<IMXMLNamespaceMapping> namespaceMappings) { } protected String getCodeFromFile(String fileName, boolean isJS, String sourceDir) { File testFile = new File(TestAdapterFactory.getTestAdapter().getUnitTestBaseDir(), sourceDir + "/" + fileName + (isJS ? ".js" : inputFileExtension)); return readCodeFile(testFile); } protected String getCodeFromFile(String fileName, String sourceDir) { File testFile = new File(TestAdapterFactory.getTestAdapter().getUnitTestBaseDir(), sourceDir + "/" + fileName); return readCodeFile(testFile); } protected String readCodeFile(File file) { boolean isResult = file.getName().contains("_result") || file.getName().equals("output.js"); String code = ""; try { BufferedReader in = new BufferedReader(new InputStreamReader( new FileInputStream(file), "UTF8")); String line = in.readLine(); if (line.contains("/**") && isResult) { // eat opening comment which should be apache header while (line != null) { line = in.readLine(); if (line.contains("*/")) { line = in.readLine(); break; } } } else if (line.contains("<!--") && isResult) { // eat opening comment which should be apache header while (line != null) { line = in.readLine(); if (line.contains("-->")) { line = in.readLine(); break; } } } while (line != null) { code += line + "\n"; line = in.readLine(); } code = code.substring(0, code.length() - 1); code = removeGeneratedString(code); in.close(); } catch (Exception e) { } return code; } protected IASNode findFirstDescendantOfType(IASNode node, Class<? extends IASNode> nodeType) { int n = node.getChildCount(); for (int i = 0; i < n; i++) { IASNode child = node.getChild(i); if (child instanceof ImportNode) continue; // not interested in these and they have BinaryOps inside if (child instanceof FunctionNode) { ((FunctionNode) child).parseFunctionBody(errors); } if (nodeType.isInstance(child)) return child; IASNode found = findFirstDescendantOfType(child, nodeType); if (found != null) return found; } return null; } protected int compileAndPublishProject(String projectFolderPath, String projectName, String mainFileName) { String sourceFolderName = tempDir + "/" + projectName + "/src"; IOFileFilter asFilter = FileFilterUtils.and(FileFileFilter.FILE, FileFilterUtils.suffixFileFilter(".as")); File sourceFolder = new File(sourceFolderName); File projectFolder = new File(TestAdapterFactory.getTestAdapter().getUnitTestBaseDir(), projectFolderPath); try { FileUtils.copyDirectory(projectFolder, sourceFolder, asFilter); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } int numArgs = 3; if (env.GOOG != null) numArgs++; String[] args = new String[numArgs]; File mainFile = new File(sourceFolder, mainFileName); //args[0] = "-compiler.targets=JS"; int index = 0; args[index++] = "-external-library-path=" + env.ASJS + "/js/libs/js.swc"; args[index++] = "-remove-circulars"; if (env.GOOG != null) args[index++] = "-closure-lib=" + env.GOOG; args[index++] = mainFile.getAbsolutePath(); int exitCode = MXMLJSC.staticMainNoExit(args); return exitCode; } }