/* * Copyright 2014, Google Inc. * 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 Google Inc. 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. */ package org.jf.smalidea; import com.intellij.lexer.Lexer; import com.intellij.testFramework.LexerTestCase; import java.util.Random; /** * This is mostly just a smoke test to make sure the lexer is working. The lexer itself has its * own tests in the smali module */ public class SmaliLexerTest extends LexerTestCase { public void testHelloWorld() { String text = ".class public LHelloWorld;\n" + ".super Ljava/lang/Object;\n" + ".method public static main([Ljava/lang/String;)V\n" + " .registers 2\n" + " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" + " const-string v1, \"Hello World!\"\n" + " invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" + " return-void\n" + ".end method"; doTest(text, "CLASS_DIRECTIVE ('.class')\n" + "WHITE_SPACE (' ')\n" + "ACCESS_SPEC ('public')\n" + "WHITE_SPACE (' ')\n" + "CLASS_DESCRIPTOR ('LHelloWorld;')\n" + "WHITE_SPACE ('\\n')\n" + "SUPER_DIRECTIVE ('.super')\n" + "WHITE_SPACE (' ')\n" + "CLASS_DESCRIPTOR ('Ljava/lang/Object;')\n" + "WHITE_SPACE ('\\n')\n" + "METHOD_DIRECTIVE ('.method')\n" + "WHITE_SPACE (' ')\n" + "ACCESS_SPEC ('public')\n" + "WHITE_SPACE (' ')\n" + "ACCESS_SPEC ('static')\n" + "WHITE_SPACE (' ')\n" + "SIMPLE_NAME ('main')\n" + "OPEN_PAREN ('(')\n" + "ARRAY_TYPE_PREFIX ('[')\n" + "CLASS_DESCRIPTOR ('Ljava/lang/String;')\n" + "CLOSE_PAREN (')')\n" + "VOID_TYPE ('V')\n" + "WHITE_SPACE ('\\n ')\n" + "REGISTERS_DIRECTIVE ('.registers')\n" + "WHITE_SPACE (' ')\n" + "POSITIVE_INTEGER_LITERAL ('2')\n" + "WHITE_SPACE ('\\n ')\n" + "INSTRUCTION_FORMAT21c_FIELD ('sget-object')\n" + "WHITE_SPACE (' ')\n" + "REGISTER ('v0')\n" + "COMMA (',')\n" + "WHITE_SPACE (' ')\n" + "CLASS_DESCRIPTOR ('Ljava/lang/System;')\n" + "ARROW ('->')\n" + "SIMPLE_NAME ('out')\n" + "COLON (':')\n" + "CLASS_DESCRIPTOR ('Ljava/io/PrintStream;')\n" + "WHITE_SPACE ('\\n ')\n" + "INSTRUCTION_FORMAT21c_STRING ('const-string')\n" + "WHITE_SPACE (' ')\n" + "REGISTER ('v1')\n" + "COMMA (',')\n" + "WHITE_SPACE (' ')\n" + "STRING_LITERAL ('\"Hello World!\"')\n" + "WHITE_SPACE ('\\n ')\n" + "INSTRUCTION_FORMAT35c_METHOD ('invoke-virtual')\n" + "WHITE_SPACE (' ')\n" + "OPEN_BRACE ('{')\n" + "REGISTER ('v0')\n" + "COMMA (',')\n" + "WHITE_SPACE (' ')\n" + "REGISTER ('v1')\n" + "CLOSE_BRACE ('}')\n" + "COMMA (',')\n" + "WHITE_SPACE (' ')\n" + "CLASS_DESCRIPTOR ('Ljava/io/PrintStream;')\n" + "ARROW ('->')\n" + "SIMPLE_NAME ('println')\n" + "OPEN_PAREN ('(')\n" + "CLASS_DESCRIPTOR ('Ljava/lang/String;')\n" + "CLOSE_PAREN (')')\n" + "VOID_TYPE ('V')\n" + "WHITE_SPACE ('\\n ')\n" + "INSTRUCTION_FORMAT10x ('return-void')\n" + "WHITE_SPACE ('\\n')\n" + "END_METHOD_DIRECTIVE ('.end method')" ); } @Override protected Lexer createLexer() { return new SmaliLexer(); } @Override protected String getDirPath() { return ""; } public void testErrorToken() { String text = ".class public .blah"; doTest(text, "CLASS_DIRECTIVE ('.class')\n" + "WHITE_SPACE (' ')\n" + "ACCESS_SPEC ('public')\n" + "WHITE_SPACE (' ')\n" + "BAD_CHARACTER ('.blah')\n"); } /** * Type out an example smali file character by character, ensuring that no exceptions are thrown */ public void testPartialText() { String text = ".class public LHelloWorld;\n" + ".super Ljava/lang/Object;\n" + ".method public static main([Ljava/lang/String;)V\n" + " .registers 2\n" + " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" + " const-string v1, \"Hello World!\"\n" + " invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" + " return-void\n" + ".end method"; for (int i=1; i<text.length(); i++) { printTokens(text.substring(i), 0); } } /** * Generate some random text and make sure the lexer doesn't throw any exceptions */ public void testRandomText() { for (int i=0; i<100; i++) { String randomString = randomString(1000); printTokens(randomString, 0); } } private Random random = new Random(123456789); private String randomString(int length) { StringBuilder sb = new StringBuilder(); for (int i=0; i<length; i++) { int type = random.nextInt(10); if (type == 9) { int randomCodepoint; do { randomCodepoint = random.nextInt(); } while(!Character.isValidCodePoint(randomCodepoint)); sb.appendCodePoint(randomCodepoint); } else if (type == 8) { char randomChar; do { randomChar = (char)random.nextInt(2^16); } while(!Character.isValidCodePoint(randomChar)); sb.append(randomChar); } else if (type > 4) { sb.append((char)random.nextInt(256)); } else if (type == 4) { sb.append(' '); } else { sb.append((char)random.nextInt(128)); } } return sb.toString(); } }