/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * CALRenaming_DeepTest.java * Created: Apr 26, 2005 * By: Peter Cardwell */ package org.openquark.cal.compiler; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.ArrayList; import java.util.List; import junit.framework.TestCase; import org.openquark.cal.CALPlatformTestModuleNames; import org.openquark.cal.compiler.CompilerMessage.Severity; import org.openquark.cal.compiler.SourceIdentifier.Category; import org.openquark.cal.runtime.MachineType; import org.openquark.cal.services.BasicCALServices; import org.openquark.cal.services.CALFeatureName; import org.openquark.cal.services.CALServicesTestUtilities; import org.openquark.cal.services.CALWorkspace; import org.openquark.cal.services.ResourceName; import org.openquark.cal.services.ResourceStore; import org.openquark.cal.services.Status; import org.openquark.cal.services.StringModuleSourceDefinition; import org.openquark.util.FileSystemHelper; /** * This class contains an exhaustive test of the renaming functionality, which works by renaming * all symbols in the M2 module, recompiling, then running M2. * @author Peter Cardwell */ public class CALRenaming_DeepTest extends TestCase { /** * Set this flag to true if debugging output is desired regardless of * whether a test fails or succeeds. */ private static final boolean SHOW_DEBUGGING_OUTPUT = false; /** * Constructor for CALRenaming_Test. * * @param name * the name of the test. */ public CALRenaming_DeepTest(String name) { super(name); } /** * A helper class for the testRenameAllM2Symbols method that contains the information needed to * perform a single renaming operation. * @author Peter Cardwell */ private static final class Renaming { private final String oldName; private final String newName; private final Category category; private Renaming(String oldName, String newName, SourceIdentifier.Category category) { this.oldName = oldName; this.newName = newName; this.category = category; } } /** * This method will rename all symbols that can be renamed in the given module, recompile, * and then run the specified function and compare its return value to the expected outcome. * * The method will fail if compilation fails or if the return value of the function that is run does not * match the expected value. * * @param moduleName * @param workspaceFile * @param targetMethod * @param expectedReturnVal */ private void renameAllSymbolsInModule(final ModuleName moduleName, final String workspaceFile, final String targetMethod, boolean renameModule, final Object expectedReturnVal) { try { String renamedTargetMethod = targetMethod; BasicCALServices privateCopyLeccServices = CALServicesTestUtilities.makeUnsharedUnitTestCALServices( MachineType.LECC, "org.openquark.cal.test.workspace.CALRenaming_DeepTest.renameAllSymbolsInModule", workspaceFile, null, "CALRenaming_DeepTest.renameAllSymbolsInModule", false); try { privateCopyLeccServices.compileWorkspace(null, new MessageLogger()); ModuleSourceDefinition sourceDef = makeSourceDefFromBuiltIn(moduleName, privateCopyLeccServices.getCALWorkspace()); String moduleString = getSourceString(sourceDef); List <Renaming> renamings = new ArrayList<Renaming>(); // Convert the source into a source model representation, then use this model to obtain a list of symbols to rename. SourceModel.ModuleDefn moduleDefn = SourceModelUtilities.TextParsing.parseModuleDefnIntoSourceModel(moduleString); SourceModel.TopLevelSourceElement[] sourceElements = moduleDefn.getTopLevelDefns(); for (int i = 0, n = sourceElements.length; i < n; i++) { if (sourceElements[i] instanceof SourceModel.TypeConstructorDefn) { SourceModel.TypeConstructorDefn typeConstructorDefn = (SourceModel.TypeConstructorDefn)sourceElements[i]; renamings.add(new Renaming(typeConstructorDefn.getTypeConsName(), "RenamedTypeCons" + i, SourceIdentifier.Category.TYPE_CONSTRUCTOR)); if (typeConstructorDefn instanceof SourceModel.TypeConstructorDefn.AlgebraicType) { SourceModel.TypeConstructorDefn.AlgebraicType algebraicType = (SourceModel.TypeConstructorDefn.AlgebraicType) typeConstructorDefn; SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn[] dataConsDefns = algebraicType.getDataConstructors(); for (int j = 0, m = dataConsDefns.length; j < m; j++) { renamings.add(new Renaming(dataConsDefns[j].getDataConsName(), ("RenamedDataCons" + i + "_" + j), SourceIdentifier.Category.DATA_CONSTRUCTOR)); } } } else if (sourceElements[i] instanceof SourceModel.TypeClassDefn) { SourceModel.TypeClassDefn typeClassDefn = (SourceModel.TypeClassDefn)sourceElements[i]; renamings.add(new Renaming(typeClassDefn.getTypeClassName(), "RenamedTypeClass" + i, SourceIdentifier.Category.TYPE_CLASS)); SourceModel.TypeClassDefn.ClassMethodDefn[] classMethodDefns = typeClassDefn.getClassMethodDefns(); for (int j = 0, m = classMethodDefns.length; j < m; j++) { renamings.add(new Renaming(classMethodDefns[j].getMethodName(), ("renamedClassMethod" + i + "_" + j), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD)); } } else if (sourceElements[i] instanceof SourceModel.FunctionDefn) { SourceModel.FunctionDefn functionDefn = (SourceModel.FunctionDefn)sourceElements[i]; if (functionDefn.getName().equals(targetMethod)) { renamedTargetMethod = "renamedFunction" + i; } renamings.add(new Renaming(functionDefn.getName(), "renamedFunction" + i, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD)); } else if (sourceElements[i] instanceof SourceModel.InstanceDefn) { } else if (sourceElements[i] instanceof SourceModel.FunctionTypeDeclaration) { } else { fail("Invalid top level source element type."); } } if(renameModule) { renamings.add(new Renaming(moduleName.toSourceText(), ("ModuleMangledByRenaming_" + moduleName), SourceIdentifier.Category.MODULE_NAME)); } ModuleTypeInfo moduleTypeInfo = privateCopyLeccServices.getWorkspaceManager().getModuleTypeInfo(moduleName); // Now run through the list and perform all the renamings. ModuleName recompiledModuleName = moduleName; for (int n = renamings.size(), i = 0; i < n; i++) { Renaming renaming = renamings.get(i); QualifiedName oldQualifiedName; QualifiedName newQualifiedName; if (renaming.category == SourceIdentifier.Category.MODULE_NAME) { oldQualifiedName = QualifiedName.make(ModuleName.make(renaming.oldName), Refactorer.Rename.UNQUALIFIED_NAME_FOR_MODULE_RENAMING); newQualifiedName = QualifiedName.make(ModuleName.make(renaming.newName), Refactorer.Rename.UNQUALIFIED_NAME_FOR_MODULE_RENAMING); } else { oldQualifiedName = QualifiedName.make(moduleName, renaming.oldName); newQualifiedName = QualifiedName.make(moduleName, renaming.newName); } CompilerMessageLogger logger = new MessageLogger(); SourceModifier renamer = IdentifierRenamer.getSourceModifier(moduleTypeInfo, moduleString, oldQualifiedName, newQualifiedName, renaming.category, logger); assertTrue(logger.getMaxSeverity().compareTo(Severity.ERROR) < 0); moduleString = renamer.apply(moduleString); if (renaming.category == SourceIdentifier.Category.MODULE_NAME && recompiledModuleName.equals(ModuleName.make(renaming.oldName))) { recompiledModuleName = ModuleName.make(renaming.newName); } } sourceDef = new StringModuleSourceDefinition(recompiledModuleName, moduleString); // Remove the old module from the workspace and add the new one. This will also force a recompilation of the relevant modules. CompilerMessageLogger logger = new MessageLogger(); privateCopyLeccServices.getWorkspaceManager().removeModule(moduleName, new Status("")); privateCopyLeccServices.getWorkspaceManager().makeModule(sourceDef, logger); if (logger.getMaxSeverity().compareTo(CompilerMessage.Severity.ERROR) >= 0) { List<CompilerMessage> errors = logger.getCompilerMessages(CompilerMessage.Severity.ERROR); for (int i = 0, n = errors.size(); i < n; i++) { System.out.println(errors.get(i).toString()); } fail(); } // Run the specified function and compare its value to the expected result assertEquals(expectedReturnVal, CALServicesTestUtilities.runNamedFunction(QualifiedName.make(recompiledModuleName, renamedTargetMethod), privateCopyLeccServices)); } finally { File rootDir = CALServicesTestUtilities.getWorkspaceRoot(null, "CALRenaming_DeepTest.renameAllSymbolsInModule"); boolean deleted = FileSystemHelper.delTree(rootDir); if (!deleted && SHOW_DEBUGGING_OUTPUT) { System.err.println("Workspace directory not deleted: " + rootDir); } } } catch (Exception ex) { ex.printStackTrace(); fail("An exception was thrown: " + ex.toString()); } } /** * Reads in the contents of a ModuleSourceDefinition and returns it as a string. * @param sourceDef * @return the module source definition as a string * @throws IOException */ private static String getSourceString(ModuleSourceDefinition sourceDef) throws IOException { Reader reader = sourceDef.getSourceReader(new Status("Get source reader status")); reader = new BufferedReader(reader); char buff[] = new char[1024]; StringBuilder moduleTextStringBuilder = new StringBuilder(); int br = reader.read(buff); while (br != -1) { moduleTextStringBuilder.append(new String(buff, 0, br)); br = reader.read(buff); } reader.close(); return moduleTextStringBuilder.toString(); } /** * Creates a ModuleSourceDefinition from a module in the standard vault. * @param builtInModuleName The name of the built in module. * @return A ModuleSourceDefinition object */ private static ModuleSourceDefinition makeSourceDefFromBuiltIn (ModuleName builtInModuleName, CALWorkspace workspace) { final ResourceStore sourceStore = workspace.getSourceManager(builtInModuleName).getResourceStore(); final CALFeatureName moduleFeatureName = CALFeatureName.getModuleFeatureName(builtInModuleName); final ResourceName moduleResourceName = new ResourceName(moduleFeatureName); return new ModuleSourceDefinition(builtInModuleName) { @Override public InputStream getInputStream(Status status) { return sourceStore.getInputStream(moduleResourceName); } @Override public long getTimeStamp() { return sourceStore.getTimeStamp(moduleResourceName); } @Override public String getDebugInfo() { return sourceStore.getDebugInfo(moduleResourceName); } }; } /** * Renames all symbols in the M2 module and then runs the mainM2 function. */ public void testRenameAllSymbolsInM2() { // We cannot rename the module itself, as M2 contains a number of foreign functions // implemented in the org.openquark.cal.foreignsupport.module.M2 package. // As such the compiler will complain that the package of these foreign classes do // not correspond to the name of the renamed module. renameAllSymbolsInModule(CALPlatformTestModuleNames.M2, "cal.platform.test.cws", "mainM2", false, Boolean.TRUE); } /** * Renames all symbols in the CALDocTest module and then runs the returnTrue function. * (this helps us verify that we're dealing properly with CALDoc comments) */ public void testRenameAllSymbolsInCALDocTest() { // Go ahead and rename the module itself, since there are no foreignsupport functions in this // module. renameAllSymbolsInModule(CALPlatformTestModuleNames.CALDocTest, "cal.platform.test.cws", "returnTrue", true, Boolean.TRUE); } }