/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.android.tools.lint;
import com.android.tools.lint.checks.AbstractCheckTest;
import com.android.tools.lint.checks.AccessibilityDetector;
import com.android.tools.lint.detector.api.Detector;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.security.Permission;
import java.util.List;
@SuppressWarnings("javadoc")
public class MainTest extends AbstractCheckTest {
protected String checkLint(String[] args, List<File> files) throws Exception {
PrintStream previousOut = System.out;
try {
final ByteArrayOutputStream output = new ByteArrayOutputStream();
System.setOut(new PrintStream(output));
Main.main(args);
return output.toString();
} finally {
System.setOut(previousOut);
}
}
private void checkDriver(String expectedOutput, String expectedError, String[] args)
throws Exception {
PrintStream previousOut = System.out;
PrintStream previousErr = System.err;
try {
// Trap System.exit calls:
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm)
{
// allow anything.
}
@Override
public void checkPermission(Permission perm, Object context)
{
// allow anything.
}
@Override
public void checkExit(int status) {
throw new ExitException();
}
});
final ByteArrayOutputStream output = new ByteArrayOutputStream();
System.setOut(new PrintStream(output));
final ByteArrayOutputStream error = new ByteArrayOutputStream();
System.setErr(new PrintStream(error));
try {
Main.main(args);
} catch (ExitException e) {
// Allow
}
assertEquals(expectedError, cleanup(error.toString()));
assertEquals(expectedOutput, cleanup(output.toString()));
} finally {
// Re-enable system exit for unit test
System.setSecurityManager(null);
System.setOut(previousOut);
System.setErr(previousErr);
}
}
public void testArguments() throws Exception {
checkDriver(
// Expected output
"\n" +
"Scanning MainTest_testArguments: .\n" +
"res/layout/accessibility.xml:4: Warning: [Accessibility] Missing contentDescription attribute on image [ContentDescription]\n" +
" <ImageView android:id=\"@+id/android_logo\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
"res/layout/accessibility.xml:5: Warning: [Accessibility] Missing contentDescription attribute on image [ContentDescription]\n" +
" <ImageButton android:importantForAccessibility=\"yes\" android:id=\"@+id/android_logo2\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
"0 errors, 2 warnings\n",
// Expected error
"",
// Args
new String[] {
"--check",
"ContentDescription",
"--disable",
"LintError",
getProjectDir(null, "res/layout/accessibility.xml").getPath()
});
}
public void testShowDescription() throws Exception {
checkDriver(
// Expected output
"NewApi\n" +
"------\n" +
"Summary: Finds API accesses to APIs that are not supported in all targeted API\n" +
"versions\n" +
"\n" +
"Priority: 6 / 10\n" +
"Severity: Error\n" +
"Category: Correctness\n" +
"\n" +
"This check scans through all the Android API calls in the application and\n" +
"warns about any calls that are not available on all versions targeted by this\n" +
"application (according to its minimum SDK attribute in the manifest).\n" +
"\n" +
"If you really want to use this API and don't need to support older devices\n" +
"just set the minSdkVersion in your AndroidManifest.xml file.\n" +
"If your code is deliberately accessing newer APIs, and you have ensured (e.g.\n" +
"with conditional execution) that this code will only ever be called on a\n" +
"supported platform, then you can annotate your class or method with the\n" +
"@TargetApi annotation specifying the local minimum SDK to apply, such as\n" +
"@TargetApi(11), such that this check considers 11 rather than your manifest\n" +
"file's minimum SDK as the required API level.\n" +
"\n" +
"\n",
// Expected error
"",
// Args
new String[] {
"--show",
"NewApi"
});
}
public void testNonexistentLibrary() throws Exception {
checkDriver(
"",
"Library foo.jar does not exist.\n",
// Args
new String[] {
"--libraries",
"foo.jar",
"prj"
});
}
public void testMultipleProjects() throws Exception {
File project = getProjectDir(null, "bytecode/classes.jar=>libs/classes.jar");
checkDriver(
"",
"The --sources, --classpath and --libraries arguments can only be used with a single project\n",
// Args
new String[] {
"--libraries",
new File(project, "libs/classes.jar").getPath(),
"--disable",
"LintError",
project.getPath(),
project.getPath()
});
}
public void testClassPath() throws Exception {
File project = getProjectDir(null,
"apicheck/minsdk1.xml=>AndroidManifest.xml",
"bytecode/GetterTest.java.txt=>src/test/bytecode/GetterTest.java",
"bytecode/GetterTest.jar.data=>bin/classes.jar"
);
checkDriver(
"\n" +
"Scanning MainTest_testClassPath: \n" +
"src/test/bytecode/GetterTest.java:47: Warning: Calling getter method getFoo1() on self is slower than field access (mFoo1) [FieldGetter]\n" +
" getFoo1();\n" +
" ~~~~~~~\n" +
"src/test/bytecode/GetterTest.java:48: Warning: Calling getter method getFoo2() on self is slower than field access (mFoo2) [FieldGetter]\n" +
" getFoo2();\n" +
" ~~~~~~~\n" +
"src/test/bytecode/GetterTest.java:52: Warning: Calling getter method isBar1() on self is slower than field access (mBar1) [FieldGetter]\n" +
" isBar1();\n" +
" ~~~~~~\n" +
"src/test/bytecode/GetterTest.java:54: Warning: Calling getter method getFoo1() on self is slower than field access (mFoo1) [FieldGetter]\n" +
" this.getFoo1();\n" +
" ~~~~~~~\n" +
"src/test/bytecode/GetterTest.java:55: Warning: Calling getter method getFoo2() on self is slower than field access (mFoo2) [FieldGetter]\n" +
" this.getFoo2();\n" +
" ~~~~~~~\n" +
"0 errors, 5 warnings\n",
"",
// Args
new String[] {
"--check",
"FieldGetter",
"--classpath",
new File(project, "bin/classes.jar").getPath(),
"--disable",
"LintError",
project.getPath()
});
}
public void testLibraries() throws Exception {
File project = getProjectDir(null,
"apicheck/minsdk1.xml=>AndroidManifest.xml",
"bytecode/GetterTest.java.txt=>src/test/bytecode/GetterTest.java",
"bytecode/GetterTest.jar.data=>bin/classes.jar"
);
checkDriver(
"\n" +
"Scanning MainTest_testLibraries: \n" +
"\n" +
"No issues found.\n",
"",
// Args
new String[] {
"--check",
"FieldGetter",
"--libraries",
new File(project, "bin/classes.jar").getPath(),
"--disable",
"LintError",
project.getPath()
});
}
@Override
protected Detector getDetector() {
// Sample issue to check by the main driver
return new AccessibilityDetector();
}
private static class ExitException extends SecurityException {
private static final long serialVersionUID = 1L;
private ExitException() {
super("Unit test");
}
}
public void test_getCleanPath() throws Exception {
assertEquals("foo", Main.getCleanPath(new File("foo")));
String sep = File.separator;
assertEquals("foo" + sep + "bar",
Main.getCleanPath(new File("foo" + sep + "bar")));
assertEquals(sep,
Main.getCleanPath(new File(sep)));
assertEquals("foo" + sep + "bar",
Main.getCleanPath(new File("foo" + sep + "." + sep + "bar")));
assertEquals("bar",
Main.getCleanPath(new File("foo" + sep + ".." + sep + "bar")));
assertEquals("",
Main.getCleanPath(new File("foo" + sep + "..")));
assertEquals("foo",
Main.getCleanPath(new File("foo" + sep + "bar" + sep + "..")));
assertEquals("foo" + sep + ".foo" + sep + "bar",
Main.getCleanPath(new File("foo" + sep + ".foo" + sep + "bar")));
assertEquals("foo" + sep + "bar",
Main.getCleanPath(new File("foo" + sep + "bar" + sep + ".")));
assertEquals("foo" + sep + "...",
Main.getCleanPath(new File("foo" + sep + "...")));
assertEquals(".." + sep + "foo",
Main.getCleanPath(new File(".." + sep + "foo")));
assertEquals(sep + "foo",
Main.getCleanPath(new File(sep + "foo")));
assertEquals(sep,
Main.getCleanPath(new File(sep + "foo" + sep + "..")));
assertEquals(sep + "foo",
Main.getCleanPath(new File(sep + "foo" + sep + "bar " + sep + "..")));
assertEquals(sep + "c:",
Main.getCleanPath(new File(sep + "c:")));
assertEquals(sep + "c:" + sep + "foo",
Main.getCleanPath(new File(sep + "c:" + sep + "foo")));
}
}