1 /* 2 * Copyright 2016, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.smali;import org.antlr.runtime.ANTLRInputStream; 33 import org.antlr.runtime.CommonToken; 34 import org.antlr.runtime.CommonTokenStream; 35 import org.antlr.runtime.RecognitionException; 36 import org.junit.Assert; 37 import org.junit.Test; 38 39 import java.io.File; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.InputStreamReader; 43 import java.util.HashMap; 44 import java.util.List; 45 46 import static org.jf.smali.expectedTokensTestGrammarParser.ExpectedToken; 47 48 public class LexerTest { 49 private static final HashMap<String, Integer> tokenTypesByName; 50 51 static { 52 tokenTypesByName = new HashMap<String, Integer>(); 53 54 for (int i=0; i<smaliParser.tokenNames.length; i++) { 55 tokenTypesByName.put(smaliParser.tokenNames[i], i); 56 } 57 } 58 59 @Test 60 public void DirectiveTest() { 61 runTest("DirectiveTest"); 62 } 63 64 @Test 65 public void ByteLiteralTest() { 66 runTest("ByteLiteralTest"); 67 } 68 69 @Test 70 public void ShortLiteralTest() { 71 runTest("ShortLiteralTest"); 72 } 73 74 @Test 75 public void IntegerLiteralTest() { 76 runTest("IntegerLiteralTest"); 77 } 78 79 @Test 80 public void LongLiteralTest() { 81 runTest("LongLiteralTest"); 82 } 83 84 @Test 85 public void FloatLiteralTest() { 86 runTest("FloatLiteralTest"); 87 } 88 89 @Test 90 public void CharLiteralTest() { 91 runTest("CharLiteralTest"); 92 } 93 94 @Test 95 public void StringLiteralTest() { 96 runTest("StringLiteralTest"); 97 } 98 99 @Test 100 public void MiscTest() { 101 runTest("MiscTest"); 102 } 103 104 @Test 105 public void CommentTest() { 106 runTest("CommentTest", false); 107 } 108 109 @Test 110 public void InstructionTest() { 111 runTest("InstructionTest", true); 112 } 113 114 @Test 115 public void TypeAndIdentifierTest() { 116 runTest("TypeAndIdentifierTest"); 117 } 118 119 @Test 120 public void SymbolTest() { 121 runTest("SymbolTest", false); 122 } 123 124 @Test 125 public void RealSmaliFileTest() { 126 runTest("RealSmaliFileTest", true); 127 } 128 129 public void runTest(String test) { 130 runTest(test, true); 131 } 132 133 public void runTest(String test, boolean discardHiddenTokens) { 134 String smaliFile = String.format("LexerTest%s%s.smali", File.separatorChar, test); 135 String tokensFile = String.format("LexerTest%s%s.tokens", File.separatorChar, test); 136 137 org.jf.smali.expectedTokensTestGrammarLexer expectedTokensLexer = null; 138 try { 139 expectedTokensLexer = new org.jf.smali.expectedTokensTestGrammarLexer(new ANTLRInputStream( 140 LexerTest.class.getClassLoader().getResourceAsStream(tokensFile))); 141 } catch (IOException ex) { 142 throw new RuntimeException(ex); 143 } 144 145 CommonTokenStream expectedTokensStream = new CommonTokenStream(expectedTokensLexer); 146 147 org.jf.smali.expectedTokensTestGrammarParser expectedTokensParser = 148 new org.jf.smali.expectedTokensTestGrammarParser(expectedTokensStream); 149 try { 150 expectedTokensParser.top(); 151 } catch (RecognitionException ex) { 152 throw new RuntimeException(ex); 153 } 154 155 List<ExpectedToken> expectedTokens = expectedTokensParser.getExpectedTokens(); 156 157 InputStream smaliStream = LexerTest.class.getClassLoader().getResourceAsStream(smaliFile); 158 if (smaliStream == null) { 159 Assert.fail("Could not load " + smaliFile); 160 } 161 smaliFlexLexer lexer = new smaliFlexLexer(new InputStreamReader(smaliStream)); 162 lexer.setSourceFile(new File(test + ".smali")); 163 lexer.setSuppressErrors(true); 164 165 CommonTokenStream tokenStream = new CommonTokenStream(lexer); 166 tokenStream.fill(); 167 List tokens = tokenStream.getTokens(); 168 169 int expectedTokenIndex = 0; 170 CommonToken token; 171 for (int i=0; i<tokens.size()-1; i++) { 172 token = (CommonToken)tokens.get(i); 173 174 if (discardHiddenTokens && token.getChannel() == smaliParser.HIDDEN) { 175 continue; 176 } 177 178 if (expectedTokenIndex >= expectedTokens.size()) { 179 Assert.fail("Too many tokens"); 180 } 181 182 if (token.getType() == smaliParser.INVALID_TOKEN) { 183 Assert.assertTrue("Encountered an INVALID_TOKEN not on the error channel", 184 token.getChannel() == smaliParser.ERROR_CHANNEL); 185 } 186 187 ExpectedToken expectedToken = expectedTokens.get(expectedTokenIndex++); 188 if (!tokenTypesByName.containsKey(expectedToken.tokenName)) { 189 Assert.fail("Unknown token: " + expectedToken.tokenName); 190 } 191 int expectedTokenType = tokenTypesByName.get(expectedToken.tokenName); 192 193 if (token.getType() != expectedTokenType) { 194 Assert.fail(String.format("Invalid token at index %d. Expecting %s, got %s(%s)", 195 expectedTokenIndex-1, expectedToken.tokenName, getTokenName(token.getType()), token.getText())); 196 } 197 198 if (expectedToken.tokenText != null) { 199 if (!expectedToken.tokenText.equals(token.getText())) { 200 Assert.fail( 201 String.format("Invalid token text at index %d. Expecting text \"%s\", got \"%s\"", 202 expectedTokenIndex - 1, expectedToken.tokenText, token.getText())); 203 } 204 } 205 } 206 207 if (expectedTokenIndex < expectedTokens.size()) { 208 Assert.fail(String.format("Not enough tokens. Expecting %d tokens, but got %d", expectedTokens.size(), 209 expectedTokenIndex)); 210 } 211 } 212 213 214 215 private static String getTokenName(int tokenType) { 216 return smaliParser.tokenNames[tokenType]; 217 } 218 } 219