Makes part of the Compiler Report (SOYC) smaller by
replacing a flat HTML output with output
that is generated with JavaScript from a dictionary of
strings. This decreases the size of the dependency
reports by a factor of 5 and the overall report by a
factor of 4. There is a difference in the time
to display for a report of a large app when running in
Chrome for what was a 5MB report. (It takes longer to
build the report using javascript)
Review at http://gwt-code-reviews.appspot.com/1123801
Review by: kprobst@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9265 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
index 51a7949..509f3ec 100644
--- a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
+++ b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -34,6 +34,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -154,6 +155,52 @@
outFile.close();
}
+ /**
+ * @return given "com.foo.myClass" or "com.foo.myClass::myMethod" returns
+ * "myClass"
+ */
+ static String getClassSubstring(String fullMethodName) {
+ if (fullMethodName.length() == 0) {
+ return "";
+ }
+ int startIndex = getPackageSubstring(fullMethodName).length() + 1;
+ int endIndex = fullMethodName.indexOf("::");
+ if (endIndex == -1) {
+ endIndex = fullMethodName.length();
+ }
+ if (startIndex > endIndex || startIndex > fullMethodName.length()) {
+ return "";
+ }
+ return fullMethodName.substring(startIndex, endIndex);
+ }
+
+ /**
+ * @return given "com.foo.myClass::myMethod" returns "myMethod"
+ */
+ static String getMethodSubstring(String fullMethodName) {
+ int index = fullMethodName.indexOf("::");
+ if (index == -1) {
+ return "";
+ }
+ index += 2;
+ if (index >= fullMethodName.length()) {
+ return "";
+ }
+ return fullMethodName.substring(index);
+ }
+
+ /**
+ * @return given "com.foo.myClass" or "com.foo.myClass::myMethod" returns
+ * "com.foo"
+ */
+ static String getPackageSubstring(String fullMethodName) {
+ int endIndex = fullMethodName.lastIndexOf('.');
+ if (endIndex == -1) {
+ endIndex = fullMethodName.length();
+ }
+ return fullMethodName.substring(0, endIndex);
+ }
+
private static void addSmallHtmlProlog(final PrintWriter outFile, String title) {
outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
@@ -287,11 +334,11 @@
String popupName = "packageBreakdownPopup";
String popupTitle = "Package breakdown";
- String popupBody = "The package breakdown blames pieces of JavaScript "
- + "code on Java packages wherever possible. Note that this is not possible for all code, so the sizes "
- + "of the packages here will not normally add up to the full code size. More specifically, the sum will "
- + "exclude strings, whitespace, and a few pieces of JavaScript code that are produced during compilation "
- + "but cannot be attributed to any Java package.";
+ String popupBody = "The package breakdown blames pieces of JavaScript "
+ + "code on Java packages wherever possible. Note that this is not possible for all code, so the sizes "
+ + "of the packages here will not normally add up to the full code size. More specifically, the sum will "
+ + "exclude strings, whitespace, and a few pieces of JavaScript code that are produced during compilation "
+ + "but cannot be attributed to any Java package.";
outFile.println("<h2>");
addPopupLink(outFile, popupName, popupTitle, null);
@@ -303,9 +350,9 @@
popupName = "codeTypeBreakdownPopup";
popupTitle = "Code Type Breakdown";
popupBody = "The code type breakdown breaks down the JavaScript code according to its "
- + "type or function. For example, it tells you how much of your code can be attributed to "
- + "JRE, GWT-RPC, etc. As above, strings and some other JavaScript snippets are not included "
- + "in the breakdown.";
+ + "type or function. For example, it tells you how much of your code can be attributed to "
+ + "JRE, GWT-RPC, etc. As above, strings and some other JavaScript snippets are not included "
+ + "in the breakdown.";
outFile.println("<h2>");
addPopupLink(outFile, popupName, popupTitle, null);
outFile.println("</h2>");
@@ -390,10 +437,9 @@
}
}
- public void makeCompilerMetricsPermFiles (
- ModuleMetricsArtifact moduleMetrics,
+ public void makeCompilerMetricsPermFiles(ModuleMetricsArtifact moduleMetrics,
PrecompilationMetricsArtifact precompilationMetrics,
- CompilationMetricsArtifact compilationMetrics) throws IOException {
+ CompilationMetricsArtifact compilationMetrics) throws IOException {
String outFileName = "CompilerMetrics-"
+ precompilationMetrics.getPermuationBase() + "-index.html";
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
@@ -427,7 +473,8 @@
outFile.println("<td>");
outFile.println("Precompile (may include Module Analysis)");
outFile.println("</td>");
- outFile.println("<td>" + precompilationMetrics.getElapsedMilliseconds() + " ms");
+ outFile.println("<td>" + precompilationMetrics.getElapsedMilliseconds()
+ + " ms");
outFile.println("</td>");
outFile.println("</tr>");
@@ -435,7 +482,8 @@
outFile.println("<td>");
outFile.println("Compile");
outFile.println("</td>");
- outFile.println("<td>" + compilationMetrics.getElapsedMilliseconds() + " ms");
+ outFile.println("<td>" + compilationMetrics.getElapsedMilliseconds()
+ + " ms");
outFile.println("</td>");
outFile.println("</tr>");
outFile.println("</table>");
@@ -468,7 +516,8 @@
outFile.println("</tr>");
makeCompilerMetricsSources(sourcesFileName, moduleMetrics, popupBody);
- String initialTypesFileName = "CompilerMetrics-initialTypes-" + permutationId + ".html";
+ String initialTypesFileName = "CompilerMetrics-initialTypes-"
+ + permutationId + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsInitialTypes";
@@ -481,9 +530,11 @@
outFile.println("" + moduleMetrics.getInitialTypes().length);
outFile.println("</td>");
outFile.println("</tr>");
- makeCompilerMetricsInitialTypeOracleTypes(initialTypesFileName, moduleMetrics, popupBody);
+ makeCompilerMetricsInitialTypeOracleTypes(initialTypesFileName,
+ moduleMetrics, popupBody);
- String finalTypesFileName = "CompilerMetrics-finalTypes-" + permutationId + ".html";
+ String finalTypesFileName = "CompilerMetrics-finalTypes-" + permutationId
+ + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsFinalTypes";
@@ -496,10 +547,13 @@
outFile.println("" + precompilationMetrics.getFinalTypeOracleTypes().length);
outFile.println("</td>");
outFile.println("</tr>");
- makeCompilerMetricsFinalTypeOracleTypes(finalTypesFileName, precompilationMetrics, popupBody);
+ makeCompilerMetricsFinalTypeOracleTypes(finalTypesFileName,
+ precompilationMetrics, popupBody);
- String[] generatedTypes = getGeneratedTypes(moduleMetrics, precompilationMetrics);
- String generatedTypesFileName = "CompilerMetrics-generatedTypes-" + permutationId + ".html";
+ String[] generatedTypes = getGeneratedTypes(moduleMetrics,
+ precompilationMetrics);
+ String generatedTypesFileName = "CompilerMetrics-generatedTypes-"
+ + permutationId + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsGeneratedTypes";
@@ -512,7 +566,8 @@
outFile.println("" + generatedTypes.length);
outFile.println("</td>");
outFile.println("</tr>");
- makeCompilerMetricsGeneratedTypes(generatedTypesFileName, generatedTypes, popupBody);
+ makeCompilerMetricsGeneratedTypes(generatedTypesFileName, generatedTypes,
+ popupBody);
String astFileName = "CompilerMetrics-ast-" + permutationId + ".html";
outFile.println("<tr>");
@@ -531,7 +586,8 @@
makeCompilerMetricsAstTypes(astFileName, precompilationMetrics, popupBody);
String[] unreferencedTypes = getUnreferencedTypes(precompilationMetrics);
- String unreferencedFileName = "CompilerMetrics-unreferencedTypes-" + permutationId + ".html";
+ String unreferencedFileName = "CompilerMetrics-unreferencedTypes-"
+ + permutationId + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsUnreferenceTypes";
@@ -545,7 +601,8 @@
outFile.println("" + unreferencedTypes.length);
outFile.println("</td>");
outFile.println("</tr>");
- makeCompilerMetricsUnreferencedTypes(unreferencedFileName, unreferencedTypes, popupBody);
+ makeCompilerMetricsUnreferencedTypes(unreferencedFileName,
+ unreferencedTypes, popupBody);
outFile.println("</table>");
addStandardHtmlEnding(outFile);
@@ -797,7 +854,8 @@
outFile.println("</ul>");
}
- private void addPopup(PrintWriter outFile, String popupName, String popupTitle, String popupBody) {
+ private void addPopup(PrintWriter outFile, String popupName,
+ String popupTitle, String popupBody) {
outFile.println("<div class=\"soyc-popup\" id=\"" + popupName + "\">");
outFile.println("<table>");
outFile.println("<tr><th><b>" + popupTitle + "</b></th></tr>");
@@ -812,8 +870,9 @@
if (href != null) {
outFile.println("href=\"" + href + "\"");
}
- outFile.println("style=\"cursor:default;\" onMouseOver=\"show('" + popupName + "');\" "
- + "onMouseOut=\"hide('" + popupName + "');\">" + popupTitle + "</a>");
+ outFile.println("style=\"cursor:default;\" onMouseOver=\"show('"
+ + popupName + "');\" " + "onMouseOut=\"hide('" + popupName + "');\">"
+ + popupTitle + "</a>");
}
/**
@@ -846,8 +905,8 @@
}
/**
- * Return a {@link java.io.File} object for a file to be emitted into the output
- * directory.
+ * Return a {@link java.io.File} object for a file to be emitted into the
+ * output directory.
*/
private OutputStream getOutFile(String localFileName) throws IOException {
return outDir.getOutputStream(localFileName);
@@ -860,7 +919,8 @@
return globalInformation.getPermutationId();
}
- private String[] getUnreferencedTypes(PrecompilationMetricsArtifact precompilationMetrics) {
+ private String[] getUnreferencedTypes(
+ PrecompilationMetricsArtifact precompilationMetrics) {
List<String> astTypes = Lists.create(precompilationMetrics.getAstTypes());
Set<String> unreferencedTypes = Sets.create(precompilationMetrics.getFinalTypeOracleTypes());
unreferencedTypes.removeAll(astTypes);
@@ -1005,7 +1065,8 @@
}
private void makeCompilerMetricsAstTypes(String outFileName,
- PrecompilationMetricsArtifact precompilationMetrics, String helpText) throws IOException {
+ PrecompilationMetricsArtifact precompilationMetrics, String helpText)
+ throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "AST Types";
addStandardHtmlProlog(outFile, title, title, "");
@@ -1024,7 +1085,8 @@
}
private void makeCompilerMetricsFinalTypeOracleTypes(String outFileName,
- PrecompilationMetricsArtifact precompilationMetrics, String helpText) throws IOException {
+ PrecompilationMetricsArtifact precompilationMetrics, String helpText)
+ throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Final Type Oracle Types";
addStandardHtmlProlog(outFile, title, title, "");
@@ -1078,8 +1140,8 @@
outFile.close();
}
- private void makeCompilerMetricsSources(String outFileName, ModuleMetricsArtifact moduleMetrics,
- String helpText) throws IOException {
+ private void makeCompilerMetricsSources(String outFileName,
+ ModuleMetricsArtifact moduleMetrics, String helpText) throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Sources on Source Path";
addStandardHtmlProlog(outFile, title, title, "");
@@ -1123,65 +1185,148 @@
*/
private void makeDependenciesHtml(String depGraphName,
Map<String, String> dependencies) throws IOException {
- String depGraphDescription = inferDepGraphDescription(depGraphName);
- PrintWriter outFile = null;
String curPackageName = "";
- String curClassName = "";
+ // Separate out the packages to write them into different HTML files.
+ String packageName = "";
+ List<String> classesInPackage = new ArrayList<String>();
for (String method : dependencies.keySet()) {
// this key set is already in alphabetical order
// get the package of this method, i.e., everything up to .[A-Z]
+ packageName = method.replaceAll("\\.\\p{Upper}.*", "");
- String packageName = method;
- packageName = packageName.replaceAll("\\.\\p{Upper}.*", "");
-
- String className = method;
- className = className.replaceAll("::.*", "");
-
- if ((curPackageName.compareTo("") == 0)
- || (curPackageName.compareTo(packageName) != 0)) {
-
+ if (curPackageName.compareTo("") == 0) {
curPackageName = packageName;
- if (outFile != null) {
- // finish up the current file
- addStandardHtmlEnding(outFile);
- outFile.close();
+ } else if (curPackageName.compareTo(packageName) != 0) {
+ makeDependenciesInternedHtml(depGraphName, curPackageName,
+ classesInPackage, dependencies);
+ classesInPackage = new ArrayList<String>();
+ curPackageName = packageName;
+ }
+ classesInPackage.add(method);
+ }
+ if (classesInPackage.size() > 0) {
+ makeDependenciesInternedHtml(depGraphName, curPackageName,
+ classesInPackage, dependencies);
+ }
+ }
+
+ /**
+ * Produces an HTML file that displays dependencies.
+ *
+ * @param depGraphName name of dependency graph
+ * @param dependencies map of dependencies
+ * @throws IOException
+ */
+ private void makeDependenciesInternedHtml(String depGraphName,
+ String packageName, List<String> classesInPackage,
+ Map<String, String> dependencies) throws IOException {
+ String depGraphDescription = inferDepGraphDescription(depGraphName);
+ PrintWriter outFile = null;
+ String curClassName = "";
+
+ // To save space, create a JS array of all possible methods in this report.
+ class HtmlInterner extends LinkedHashMap<String, Integer> {
+ int index = 0;
+
+ public void intern(String key) {
+ if (!containsKey(key)) {
+ put(key, index++);
}
-
- String outFileName = dependenciesFileName(depGraphName, curPackageName);
- outFile = new PrintWriter(getOutFile(outFileName));
-
- String packageDescription = packageName.length() == 0
- ? "the default package" : packageName;
- addStandardHtmlProlog(outFile, "Method Dependencies for "
- + depGraphDescription, "Method Dependencies for "
- + depGraphDescription, "Showing Package: " + packageDescription);
}
- String name = method;
- if (curClassName.compareTo(className) != 0) {
- name = className;
- curClassName = className;
- outFile.println("<a name=\"" + curClassName
- + "\"><h3 class=\"soyc-class-header\">Class: " + curClassName
- + "</a></h3>");
+ }
+ HtmlInterner interner = new HtmlInterner();
+
+ for (String reportMethod : classesInPackage) {
+ interner.intern(getPackageSubstring(reportMethod));
+ interner.intern(getClassSubstring(reportMethod));
+ interner.intern(getMethodSubstring(reportMethod));
+
+ String depMethod = dependencies.get(reportMethod);
+ while (depMethod != null) {
+ interner.intern(getPackageSubstring(depMethod));
+ interner.intern(getClassSubstring(depMethod));
+ interner.intern(getMethodSubstring(depMethod));
+ depMethod = dependencies.get(depMethod);
}
+ }
- outFile.println("<div class='main'>");
- outFile.println("<a class='toggle soyc-call-stack-link' onclick='toggle.call(this)'><span class='calledBy'> Call stack: </span>"
- + name + "</a>");
- outFile.println("<ul class=\"soyc-call-stack-list\">");
+ String outFileName = dependenciesFileName(depGraphName, packageName);
+ outFile = new PrintWriter(getOutFile(outFileName));
+ String packageDescription = packageName.length() == 0
+ ? "the default package" : packageName;
+ addStandardHtmlProlog(outFile, "Method Dependencies for "
+ + depGraphDescription,
+ "Method Dependencies for " + depGraphDescription, "Showing Package: "
+ + packageDescription);
+ // Write out the data values in the script
+ outFile.println("<script language=\"javascript\">");
+ outFile.println(" var internedStrings = [");
+ for (String key : interner.keySet()) {
+ outFile.println("\"" + key + "\",");
+ }
+ outFile.println(" ];");
+ // function to print a class header
+ outFile.println(" function showC(packageRef, classRef) {");
+ outFile.println(" var className = internedStrings[packageRef] + \".\" + internedStrings[classRef];");
+ outFile.println(" document.write(\"<a name='\" + className + \"'>\");");
+ outFile.println(" document.write(\"<h3 class='soyc-class-header'>Class: \" + className + \"</a></h3>\");");
+ outFile.println(" }");
+ // function to print a dependency
+ outFile.println(" function showD(c, deps) {");
+ outFile.println(" document.write(\"<div class='main'><a class='toggle soyc-call-stack-link' "
+ + "onclick='toggle.call(this)'><span class='calledBy'> Call stack: </span>\");");
+ outFile.println(" document.write(internedStrings[c[0]] + \".\" + internedStrings[c[1]] + \"::\" + "
+ + "internedStrings[c[2]] + \"</a>\");");
+ outFile.println(" document.write(\"<ul class='soyc-call-stack-list'>\");");
+ outFile.println(" for (var i = 0; i < deps.length ; i++) {");
+ outFile.println(" var s = deps[i];");
+ outFile.println(" document.write(\"<li>\" + internedStrings[s[0]] + \".\" + internedStrings[s[1]] +"
+ + "\"::\" + internedStrings[s[2]] + \"</li>\");");
+ outFile.println(" }");
+ outFile.println(" document.write(\"</ul></div>\");");
+ outFile.println(" }");
+ outFile.println("</script>");
+
+ // Write out the HTML
+ outFile.print("<script>");
+ for (String method : classesInPackage) {
+ // this key set is already in alphabetical order
+ // get the package of this method, i.e., everything up to .[A-Z]
+
+ String className = method.replaceAll("::.*", "");
String depMethod = dependencies.get(method);
+ if (curClassName.compareTo(className) != 0) {
+ curClassName = className;
+ outFile.print("showC(" + interner.get(getPackageSubstring(className))
+ + "," + interner.get(getClassSubstring(className)) + ");");
+ }
+ String nameArray = "[" + interner.get(getPackageSubstring(method)) + ","
+ + interner.get(getClassSubstring(method)) + ","
+ + interner.get(getMethodSubstring(method)) + "]";
+ outFile.print("showD(" + nameArray + ",");
+ outFile.print(" [");
while (depMethod != null) {
String nextDep = dependencies.get(depMethod);
- if (nextDep != null) {
- outFile.println("<li>" + depMethod + "</li>");
+ // The bottom of the stack frame is not interesting.
+ if (nextDep != null) {
+ String packageString = getPackageSubstring(depMethod);
+ String classString = getClassSubstring(depMethod);
+ String methodString = getMethodSubstring(depMethod);
+ outFile.print("[" + interner.get(packageString) + ","
+ + interner.get(classString) + "," + interner.get(methodString)
+ + "]");
}
depMethod = nextDep;
+ if (nextDep != null) {
+ outFile.print(",");
+ }
}
- outFile.println("</ul>");
- outFile.println("</div>");
+ outFile.print(" ]);");
}
+ outFile.println("</script>");
+
addStandardHtmlEnding(outFile);
outFile.close();
}
@@ -1211,7 +1356,6 @@
*
* @param breakdown
* @return the name of the HTML file
- * @throws IOException
*/
private String makePackageHtml(SizeBreakdown breakdown) throws IOException {
String outFileName = breakdown.getId() + "-" + getPermutationId() + "-"
diff --git a/dev/core/test/com/google/gwt/soyc/MakeTopLevelHtmlForPermTest.java b/dev/core/test/com/google/gwt/soyc/MakeTopLevelHtmlForPermTest.java
new file mode 100644
index 0000000..21578dc
--- /dev/null
+++ b/dev/core/test/com/google/gwt/soyc/MakeTopLevelHtmlForPermTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed 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 com.google.gwt.soyc;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for {@link MakeTopLevelHtmlForPerm}.
+ */
+public class MakeTopLevelHtmlForPermTest extends TestCase {
+
+ public void testGetClassSubstring() {
+ assertEquals("myClass", MakeTopLevelHtmlForPerm.getClassSubstring("com.foo.myClass"));
+ assertEquals("myClass", MakeTopLevelHtmlForPerm.getClassSubstring("com.foo.myClass::myMethod"));
+
+ // We don't really expect these inputs, just testing to make sure they don't blow up
+ assertEquals("Empty string", "", MakeTopLevelHtmlForPerm.getClassSubstring(""));
+ assertEquals("", MakeTopLevelHtmlForPerm.getClassSubstring("::myMethod"));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring(":"));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("::"));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("..."));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring(".."));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("."));
+ }
+
+ public void testGetMethodSubstring() {
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("com.foo.myClass"));
+ assertEquals("myMethod", MakeTopLevelHtmlForPerm.getMethodSubstring("com.foo.myClass::myMethod"));
+
+ // We don't really expect these inputs, just testing to make sure they don't blow up
+ assertEquals("Empty string", "", MakeTopLevelHtmlForPerm.getMethodSubstring(""));
+ assertEquals("myMethod", MakeTopLevelHtmlForPerm.getMethodSubstring("::myMethod"));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("myMethod"));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring(":"));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("::"));
+ }
+
+ public void testGetPackageSubstring() {
+ assertEquals("com.foo", MakeTopLevelHtmlForPerm.getPackageSubstring("com.foo.myClass"));
+ assertEquals("com.foo", MakeTopLevelHtmlForPerm.getPackageSubstring("com.foo.myClass::myMethod"));
+
+ // We don't really expect these inputs, just testing to make sure they don't blow up
+ assertEquals("Empty string", "", MakeTopLevelHtmlForPerm.getPackageSubstring(""));
+ assertEquals("com.foo", MakeTopLevelHtmlForPerm.getPackageSubstring("com.foo.myClass::"));
+ assertEquals("com.foo", MakeTopLevelHtmlForPerm.getPackageSubstring("com.foo.myClass:"));
+ assertEquals("com", MakeTopLevelHtmlForPerm.getPackageSubstring("com"));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("..."));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring(".."));
+ assertEquals("", MakeTopLevelHtmlForPerm.getMethodSubstring("."));
+ }
+}