/* // Licensed to Julian Hyde under one or more contributor license // agreements. See the NOTICE file distributed with this work for // additional information regarding copyright ownership. // // Julian Hyde licenses this file to you under the Modified BSD License // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/BSD-3-Clause */ package sqlline; import java.util.BitSet; import java.util.HashMap; import java.util.Map; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStyle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import mockit.Mock; import mockit.MockUp; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static sqlline.SqlLineHighlighterLowLevelTest.ExpectedHighlightStyle; import static sqlline.SqlLineHighlighterLowLevelTest.getSqlLine; /** * Tests for sql and command syntax highlighting in sqlline. */ public class SqlLineHighlighterTest { private Map<SqlLine, SqlLineHighlighter> sqlLine2Highlighter; private SqlLine sqlLineWithDefaultColorScheme; /** * To add your color scheme to tests just put sqlline object * with corresponding highlighter into the map like below. * @throws Exception if error while sqlline initialization happens */ @BeforeEach public void setUp() throws Exception { sqlLine2Highlighter = new HashMap<>(); sqlLineWithDefaultColorScheme = getSqlLine(SqlLineProperty.DEFAULT); SqlLine darkSqlLine = getSqlLine("dark"); SqlLine lightSqlLine = getSqlLine("light"); sqlLine2Highlighter .put(sqlLineWithDefaultColorScheme, new SqlLineHighlighter(sqlLineWithDefaultColorScheme)); sqlLine2Highlighter.put(darkSqlLine, new SqlLineHighlighter(darkSqlLine)); sqlLine2Highlighter.put(lightSqlLine, new SqlLineHighlighter(lightSqlLine)); } @AfterEach public void tearDown() { sqlLine2Highlighter = null; } @Test public void testCommands() { String[] linesRequiredToBeCommands = { "!set", "!commandhandler", "!quit", "!isolation", "!dbinfo", "!help", "!connect" }; for (String line : linesRequiredToBeCommands) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testKeywords() { String[] linesRequiredToBeKeywords = { "from", "outer", "select", "values", "where", "join", "cross" }; for (String line : linesRequiredToBeKeywords) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testSingleQuotedStrings() { String[] linesRequiredToBeSingleQuoted = { "'from'", "''''", "''", "'", "'test '' \n''select'", "'/* \n'", "'-- \n--'", "'\"'" }; for (String line : linesRequiredToBeSingleQuoted) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.singleQuotes.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testSqlIdentifierQuotes() { // default sql identifier is a double quote // {@code SqlLineHighlighter#DEFAULT_SQL_IDENTIFIER_QUOTE}. String[] linesRequiredToBeDoubleQuoted = { "\"", "\"\"", "\"from\"", "\"''\"", "\"test '' \n''select\"", "\"/* \\\"kjh\"", "\"/* \"", "\"-- \"", "\"\n \n\"" }; for (String line : linesRequiredToBeDoubleQuoted) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.sqlIdentifierQuotes.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testCommentedStrings() { String[] linesRequiredToBeComments = { "-- 'asdasd'asd", "--select", "/* \"''\"", "/*", "/*/ should be a comment", "--", "--\n/*", "/* kh\n'asd'ad*/", "/*\"-- \"values*/" }; for (String line : linesRequiredToBeComments) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.comments.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testMySqlCommentedStrings() { checkMySqlCommentedStrings(DialectImpl.create(null, null, "MySQL")); checkMySqlCommentedStrings(BuiltInDialect.MYSQL); } private void checkMySqlCommentedStrings(final Dialect dialect) { new MockUp<DialectImpl>() { @Mock Dialect getDefault() { return dialect; } }; String[] linesRequiredToBeComments = { "-- 'asdasd'asd", "--\n", "/* \"''\"", "/*", "--\t", "#", "--\n/*", "/* kh\n'asd'ad*/", "/*\"-- \"values*/" }; for (String line : linesRequiredToBeComments) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.comments.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testPhoenixCommentedStrings() { checkPhoenixCommentedStrings(DialectImpl.create(null, null, "Phoenix")); checkPhoenixCommentedStrings(BuiltInDialect.PHOENIX); } private void checkPhoenixCommentedStrings(final Dialect dialect) { new MockUp<DialectImpl>() { @Mock Dialect getDefault() { return dialect; } }; String[] linesRequiredToBeComments = { "--'asdasd'asd", "--\tselect", "// \"''\"", "//", "--", "--\n/*", "/* kh\n'asd'ad*/", "/*\"-- \"values*/" }; for (String line : linesRequiredToBeComments) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.comments.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testNumberStrings() { String[] linesRequiredToBeNumbers = { "123456789", "0123", "1" }; for (String line : linesRequiredToBeNumbers) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.numbers.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } } @Test public void testComplexStrings() { // comments String line = "#!set version"; ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.comments.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // spaces before comments line = " # !set"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.defaults.set(0, line.indexOf("#")); expectedStyle.comments.set(line.indexOf("#"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); line = " -- select 1 from dual;"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.defaults.set(0, line.indexOf("--")); expectedStyle.comments.set(line.indexOf("--"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // odd number of quotes inside comments line = " -- select '1 as \" from dual;"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.defaults.set(0, line.indexOf("--")); expectedStyle.comments.set(line.indexOf("--"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // odd number of quotes inside comments line = " # '` \" '2 as \";"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.defaults.set(0, line.indexOf("#")); expectedStyle.comments.set(line.indexOf("#"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // command with argument line = "!set version"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!set".length()); expectedStyle.defaults.set("!set".length(), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // sqlline comments inside commands should be treated as default text line = "!set # -- version"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!set".length()); expectedStyle.defaults.set("!set".length(), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // command with quoted argument line = "!set csvdelimiter '\"'"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!set".length()); expectedStyle.defaults.set("!set".length(), line.indexOf("'\"'")); expectedStyle.singleQuotes.set(line.indexOf("'\"'"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // command with double quoted argument line = "!set csvdelimiter \"'\""; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!set".length()); expectedStyle.defaults.set("!set".length(), line.indexOf("\"'\"")); expectedStyle.sqlIdentifierQuotes.set(line.indexOf("\"'\""), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // command with double quoted argument and \n line = "!set csvdelimiter \"'\n\""; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!set".length()); expectedStyle.defaults.set("!set".length(), line.indexOf("\"'\n\"")); expectedStyle .sqlIdentifierQuotes.set(line.indexOf("\"'\n\""), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // !connect command with quoted arguments line = "!connect \"jdbc:string\" admin 'pass \"word' driver"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!connect".length()); expectedStyle.defaults.set(line.indexOf(" \"jdbc:string")); expectedStyle.sqlIdentifierQuotes.set( line.indexOf("\"jdbc:string"), line.indexOf(" admin")); expectedStyle.defaults.set( line.indexOf(" admin"), line.indexOf("'pass \"word'")); expectedStyle.singleQuotes.set( line.indexOf("'pass \"word'"), line.indexOf(" driver")); expectedStyle.defaults.set(line.indexOf(" driver"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // sql with !sql command line = "!sql select '1'"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!sql".length()); expectedStyle.defaults.set("!sql".length()); expectedStyle.keywords.set(line.indexOf("select"), line.indexOf(" '1'")); expectedStyle.defaults.set(line.indexOf(" '1'")); expectedStyle.singleQuotes.set(line.indexOf("'1'"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // sql with !sql command started not from the first symbol line = " !sql select '1' /* comment*/"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.defaults.set(0, line.indexOf("!sql")); expectedStyle.commands.set(line.indexOf("!sql"), line.indexOf(" select")); expectedStyle.defaults.set(line.indexOf(" select")); expectedStyle.keywords.set(line.indexOf("select"), line.indexOf(" '1'")); expectedStyle.defaults.set(line.indexOf(" '1'")); expectedStyle.singleQuotes .set(line.indexOf("'1'"), line.indexOf(" /* comment*/")); expectedStyle.defaults.set(line.indexOf(" /* comment*/")); expectedStyle.comments.set(line.indexOf("/* comment*/"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // sql with !all command line = "!all select '2' as \"two\"; -- comment"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.commands.set(0, "!all".length()); expectedStyle.defaults.set("!all".length()); expectedStyle.keywords.set(line.indexOf("select"), line.indexOf(" '2'")); expectedStyle.defaults.set(line.indexOf(" '2'")); expectedStyle.singleQuotes.set(line.indexOf("'2'"), line.indexOf(" as")); expectedStyle.defaults.set(line.indexOf(" as")); expectedStyle.keywords.set(line.indexOf("as"), line.indexOf(" \"two")); expectedStyle.defaults.set(line.indexOf(" \"two")); expectedStyle.sqlIdentifierQuotes .set(line.indexOf("\"two\""), line.indexOf(";")); expectedStyle.defaults.set(line.indexOf(";"), line.indexOf("--")); expectedStyle.comments.set(line.indexOf("--"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); // sqlline comments for default dialect // inside sql should be treated as default text line = "select #'1'"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.defaults.set("select".length(), line.indexOf('#') + 1); expectedStyle.singleQuotes.set(line.indexOf('#') + 1, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); line = "select '1'"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.defaults.set("select".length(), line.indexOf(' ') + 1); expectedStyle.singleQuotes.set(line.indexOf(' ') + 1, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); line = "select map[1*5,2],1|2,1^2,1!=0"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.defaults.set("select".length(), line.indexOf("1*")); expectedStyle.numbers.set(line.indexOf("1*")); expectedStyle.defaults.set(line.indexOf('*')); expectedStyle.numbers.set(line.indexOf('5')); expectedStyle.defaults.set(line.indexOf(',')); expectedStyle.numbers.set(line.indexOf('2')); expectedStyle.defaults.set(line.indexOf(']'), line.indexOf("1|")); expectedStyle.numbers.set(line.indexOf("1|")); expectedStyle.defaults.set(line.indexOf('|')); expectedStyle.numbers.set(line.indexOf("2,1")); expectedStyle.defaults.set(line.indexOf(",1^")); expectedStyle.numbers.set(line.indexOf("1^")); expectedStyle.defaults.set(line.indexOf("^2")); expectedStyle.numbers.set(line.indexOf("2,1!")); expectedStyle.defaults.set(line.indexOf(",1!")); expectedStyle.numbers.set(line.indexOf("1!")); expectedStyle.defaults.set(line.indexOf("!="), line.indexOf('0')); expectedStyle.numbers.set(line.indexOf('0')); checkLineAgainstAllHighlighters(line, expectedStyle); //no spaces line = "select'1'as\"21\""; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.singleQuotes.set(line.indexOf('\''), line.indexOf("as")); expectedStyle.keywords.set(line.indexOf("as"), line.indexOf("\"21\"")); expectedStyle .sqlIdentifierQuotes.set(line.indexOf("\"21\""), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); //escaped sql identifiers line = "select '1' as \"\\\"value\n\\\"\""; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.defaults.set(line.indexOf(" '")); expectedStyle.singleQuotes.set(line.indexOf('\''), line.indexOf(" as")); expectedStyle.defaults.set(line.indexOf(" as")); expectedStyle .keywords.set(line.indexOf("as"), line.indexOf(" \"\\\"value")); expectedStyle .sqlIdentifierQuotes.set(line.indexOf("\"\\\"value"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); //not valid sql with comments /**/ and not ended quoted line line = "select/*123'1'*/'as\"21\""; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.comments .set(line.indexOf("/*123'1'*/"), line.indexOf("'as\"21\"")); expectedStyle.singleQuotes.set(line.indexOf("'as\"21\""), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); //not valid sql with comments /**/ and not ended sql identifier quoted line line = "select/*comment*/ as \"21\\\""; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.comments .set(line.indexOf("/*"), line.indexOf(" as")); expectedStyle.defaults.set(line.indexOf(" as")); expectedStyle.keywords.set(line.indexOf("as"), line.indexOf(" \"21")); expectedStyle.defaults.set(line.indexOf(" \"21")); expectedStyle.sqlIdentifierQuotes.set(line.indexOf("\"21"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); //not valid sql with not ended multiline comment line = "select /*\n * / \n 123 as \"q\" \nfrom dual\n where\n 1 = 1"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.defaults.set("select".length()); expectedStyle.comments .set(line.indexOf("/*\n"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); //multiline sql with comments line = "select/*multiline\ncomment\n*/0 as \"0\"," + "'qwe'\n--comment\nas\"21\"from t\n where 1=1"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.comments.set("select".length(), line.indexOf("0 as")); expectedStyle.numbers.set(line.indexOf("0 as")); expectedStyle.defaults.set(line.indexOf(" as \"0\",")); expectedStyle.keywords .set(line.indexOf("as \"0\","), line.indexOf(" \"0\",")); expectedStyle.defaults.set(line.indexOf(" \"0\",")); expectedStyle.sqlIdentifierQuotes .set(line.indexOf("\"0\","), line.indexOf(",'qwe")); expectedStyle.defaults.set(line.indexOf(",'qwe")); expectedStyle.singleQuotes .set(line.indexOf("'qwe'"), line.indexOf("\n--comment\nas")); expectedStyle.defaults.set(line.indexOf("\n--comment")); expectedStyle.comments .set(line.indexOf("--comment\n"), line.indexOf("as\"21\"")); expectedStyle.keywords .set(line.indexOf("as\"21\""), line.indexOf("\"21\"from")); expectedStyle.sqlIdentifierQuotes .set(line.indexOf("\"21\""), line.indexOf("from")); expectedStyle.keywords.set(line.indexOf("from"), line.indexOf(" t\n")); expectedStyle.defaults.set(line.indexOf(" t\n"), line.indexOf("where")); expectedStyle.keywords.set(line.indexOf("where"), line.indexOf(" 1=1")); expectedStyle.defaults.set(line.indexOf(" 1=1")); expectedStyle.numbers.set(line.indexOf("1=1")); expectedStyle.defaults.set(line.indexOf("=1")); expectedStyle.numbers.set(line.indexOf("=1") + 1); checkLineAgainstAllHighlighters(line, expectedStyle); //not valid sql with wrong symbols at the and line = "select 1 as \"one\" from dual //"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, "select".length()); expectedStyle.defaults.set("select".length()); expectedStyle.numbers.set(line.indexOf("1")); expectedStyle.defaults.set("select 1".length()); expectedStyle.keywords.set(line.indexOf("as"), line.indexOf(" \"one")); expectedStyle.defaults.set(line.indexOf(" \"one")); expectedStyle.sqlIdentifierQuotes .set(line.indexOf("\"one\""), line.indexOf(" from")); expectedStyle.defaults.set(line.indexOf(" from")); expectedStyle.keywords.set(line.indexOf("from"), line.indexOf(" dual")); expectedStyle.defaults.set(line.indexOf(" dual"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); //one line comment first line = "-- \nselect 1;"; expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.comments.set(0, line.indexOf("select")); expectedStyle.keywords.set(line.indexOf("select"), line.indexOf(" 1")); expectedStyle.defaults.set(line.indexOf(" 1"), line.indexOf("1;")); expectedStyle.numbers.set(line.indexOf("1")); expectedStyle.defaults.set(line.length() - 1); checkLineAgainstAllHighlighters(line, expectedStyle); } /** * The test checks additional highlighting while having connection to db. * 1) if keywords from getSQLKeywords are highlighted * 2) if a connection is cleared from sqlhighlighter * in case the connection is closing */ @Test public void testH2SqlKeywordsFromDatabase() { // The list is taken from H2 1.4.197 getSQLKeywords output String[] linesRequiredToBeConnectionSpecificKeyWords = { "LIMIT", "MINUS", "OFFSET", "ROWNUM", "SYSDATE", "SYSTIME", "SYSTIMESTAMP", "TODAY", }; for (String line : linesRequiredToBeConnectionSpecificKeyWords) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.defaults.set(0, line.length()); checkLineAgainstAllHighlighters(line, expectedStyle); } DispatchCallback dc = new DispatchCallback(); for (Map.Entry<SqlLine, SqlLineHighlighter> sqlLine2HighLighterEntry : sqlLine2Highlighter.entrySet()) { SqlLine sqlLine = sqlLine2HighLighterEntry.getKey(); sqlLine.runCommands(dc, "!connect " + SqlLineArgsTest.ConnectionSpec.H2.url + " " + SqlLineArgsTest.ConnectionSpec.H2.username + " \"\""); for (String line : linesRequiredToBeConnectionSpecificKeyWords) { ExpectedHighlightStyle expectedStyle = new ExpectedHighlightStyle(line.length()); expectedStyle.keywords.set(0, line.length()); checkLineAgainstHighlighter( line, expectedStyle, sqlLine, sqlLine2HighLighterEntry.getValue()); } sqlLine.getDatabaseConnection().close(); } } /** * The test mocks default sql identifier to square bracket * and then checks that after connection done sql * identifier quote will be taken from driver */ @Test public void testBracketsAsSqlIdentifier() { new MockUp<DialectImpl>() { @Mock Dialect getDefault() { return DialectImpl.create(null, "[", null); } }; String[] linesWithSquareBracketsSqlIdentifiers = { "select 1 as [one] from dual", "select 1 as [one two one] from dual", }; String[] linesWithDoubleQuoteSqlIdentifiers = { "select 1 as \"one\" from dual", "select 1 as \"one two one\" from dual", }; ExpectedHighlightStyle[] expectedStyle = new ExpectedHighlightStyle[ linesWithSquareBracketsSqlIdentifiers.length]; for (int i = 0; i < expectedStyle.length; i++) { String line = linesWithSquareBracketsSqlIdentifiers[i]; expectedStyle[i] = new ExpectedHighlightStyle(line.length()); expectedStyle[i].keywords.set(0, "select".length()); expectedStyle[i].defaults.set(line.indexOf(" 1")); expectedStyle[i].numbers.set(line.indexOf("1 as")); expectedStyle[i].defaults.set(line.indexOf(" as")); expectedStyle[i].keywords.set(line.indexOf("as"), line.indexOf(" [one")); expectedStyle[i].defaults.set(line.indexOf(" [one")); expectedStyle[i].sqlIdentifierQuotes. set(line.indexOf("[one"), line.indexOf(" from")); expectedStyle[i].defaults.set(line.indexOf(" from")); expectedStyle[i].keywords .set(line.indexOf("from"), line.indexOf(" dual")); expectedStyle[i].defaults.set(line.indexOf(" dual"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle[i]); } DispatchCallback dc = new DispatchCallback(); for (Map.Entry<SqlLine, SqlLineHighlighter> sqlLine2HighLighterEntry : sqlLine2Highlighter.entrySet()) { SqlLine sqlLine = sqlLine2HighLighterEntry.getKey(); sqlLine.runCommands(dc, "!connect " + SqlLineArgsTest.ConnectionSpec.H2.url + " " + SqlLineArgsTest.ConnectionSpec.H2.username + " \"\""); for (int i = 0; i < linesWithDoubleQuoteSqlIdentifiers.length; i++) { checkLineAgainstHighlighter( linesWithDoubleQuoteSqlIdentifiers[i], expectedStyle[i], sqlLine, sqlLine2HighLighterEntry.getValue()); } sqlLine.getDatabaseConnection().close(); } } /** * The test mocks default sql identifier to back tick * and then checks that after connection done sql * identifier quote will be taken from driver */ @Test public void testH2SqlIdentifierFromDatabase() { new MockUp<DialectImpl>() { @Mock Dialect getDefault() { return DialectImpl.create(null, "`", null); } }; String[] linesWithBackTickSqlIdentifiers = { "select 1 as `one` from dual", "select 1 as `on\\`e` from dual", "select 1 as `on\\`\ne` from dual", }; String[] linesWithDoubleQuoteSqlIdentifiers = { "select 1 as \"one\" from dual", "select 1 as \"on\\\"e\" from dual", "select 1 as \"on\\\"\ne\" from dual", }; ExpectedHighlightStyle[] expectedStyle = new ExpectedHighlightStyle[linesWithBackTickSqlIdentifiers.length]; for (int i = 0; i < expectedStyle.length; i++) { String line = linesWithBackTickSqlIdentifiers[i]; expectedStyle[i] = new ExpectedHighlightStyle(line.length()); expectedStyle[i].keywords.set(0, "select".length()); expectedStyle[i].defaults.set(line.indexOf(" 1")); expectedStyle[i].numbers.set(line.indexOf("1 as")); expectedStyle[i].defaults.set(line.indexOf(" as")); expectedStyle[i].keywords.set(line.indexOf("as"), line.indexOf(" `on")); expectedStyle[i].defaults.set(line.indexOf(" `on")); expectedStyle[i].sqlIdentifierQuotes. set(line.indexOf("`on"), line.indexOf(" from")); expectedStyle[i].defaults.set(line.indexOf(" from")); expectedStyle[i].keywords .set(line.indexOf("from"), line.indexOf(" dual")); expectedStyle[i].defaults.set(line.indexOf(" dual"), line.length()); checkLineAgainstAllHighlighters(line, expectedStyle[i]); } DispatchCallback dc = new DispatchCallback(); for (Map.Entry<SqlLine, SqlLineHighlighter> sqlLine2HighLighterEntry : sqlLine2Highlighter.entrySet()) { SqlLine sqlLine = sqlLine2HighLighterEntry.getKey(); sqlLine.runCommands(dc, "!connect " + SqlLineArgsTest.ConnectionSpec.H2.url + " " + SqlLineArgsTest.ConnectionSpec.H2.username + " \"\""); for (int i = 0; i < linesWithDoubleQuoteSqlIdentifiers.length; i++) { checkLineAgainstHighlighter( linesWithDoubleQuoteSqlIdentifiers[i], expectedStyle[i], sqlLine, sqlLine2HighLighterEntry.getValue()); } sqlLine.getDatabaseConnection().close(); } } /** * Check if there is an exception while highlight processing * then only the default style is applied */ @Test public void testHighlightWithException() { new MockUp<SqlLineHighlighter>() { @Mock void handleSqlSyntax(String buffer, BitSet keywordBitSet, BitSet quoteBitSet, BitSet sqlIdentifierQuotesBitSet, BitSet commentBitSet, BitSet numberBitSet, boolean isCommandPresent) { throw new RuntimeException("Highlight exception"); } }; String[] linesWithDoubleQuoteSqlIdentifiers = { "select 1 as \"one\" from dual", "select 1 as \"on\\\"e\" from dual", "select 1 as \"on\\\"\ne\" from dual", }; ExpectedHighlightStyle[] expectedStyle = new ExpectedHighlightStyle[linesWithDoubleQuoteSqlIdentifiers.length]; for (int i = 0; i < expectedStyle.length; i++) { String line = linesWithDoubleQuoteSqlIdentifiers[i]; expectedStyle[i] = new ExpectedHighlightStyle(line.length()); expectedStyle[i].defaults.set(0, line.length()); checkLineAgainstHighlighter(line, expectedStyle[i], sqlLineWithDefaultColorScheme, sqlLine2Highlighter.get(sqlLineWithDefaultColorScheme)); } DispatchCallback dc = new DispatchCallback(); for (Map.Entry<SqlLine, SqlLineHighlighter> sqlLine2HighLighterEntry : sqlLine2Highlighter.entrySet()) { SqlLine sqlLine = sqlLine2HighLighterEntry.getKey(); sqlLine.runCommands(dc, "!connect " + SqlLineArgsTest.ConnectionSpec.H2.url + " " + SqlLineArgsTest.ConnectionSpec.H2.username + " \"\""); for (int i = 0; i < linesWithDoubleQuoteSqlIdentifiers.length; i++) { checkLineAgainstHighlighter( linesWithDoubleQuoteSqlIdentifiers[i], expectedStyle[i], sqlLineWithDefaultColorScheme, sqlLine2Highlighter.get(sqlLineWithDefaultColorScheme)); } sqlLine.getDatabaseConnection().close(); } } private void checkHighlightedLine( SqlLine sqlLine, String line, ExpectedHighlightStyle expectedHighlightStyle, SqlLineHighlighter highlighter) { final AttributedString attributedString = highlighter.highlight(sqlLine.getLineReader(), line); final HighlightStyle highlightStyle = sqlLine.getHighlightStyle(); int commandStyle = highlightStyle.getCommandStyle().getStyle(); int keywordStyle = highlightStyle.getKeywordStyle().getStyle(); int singleQuoteStyle = highlightStyle.getQuotedStyle().getStyle(); int identifierStyle = highlightStyle.getIdentifierStyle().getStyle(); int commentStyle = highlightStyle.getCommentStyle().getStyle(); int numberStyle = highlightStyle.getNumberStyle().getStyle(); int defaultStyle = highlightStyle.getDefaultStyle().getStyle(); for (int i = 0; i < line.length(); i++) { checkSymbolStyle(line, i, expectedHighlightStyle.commands, attributedString, commandStyle, "command"); checkSymbolStyle(line, i, expectedHighlightStyle.keywords, attributedString, keywordStyle, "key word"); checkSymbolStyle(line, i, expectedHighlightStyle.singleQuotes, attributedString, singleQuoteStyle, "single quote"); checkSymbolStyle(line, i, expectedHighlightStyle.sqlIdentifierQuotes, attributedString, identifierStyle, "sql identifier quote"); checkSymbolStyle(line, i, expectedHighlightStyle.numbers, attributedString, numberStyle, "number"); checkSymbolStyle(line, i, expectedHighlightStyle.comments, attributedString, commentStyle, "comment"); checkSymbolStyle(line, i, expectedHighlightStyle.defaults, attributedString, defaultStyle, "default"); } } private void checkDefaultLine( SqlLine sqlLine, String line, SqlLineHighlighter defaultHighlighter) { final AttributedString attributedString = defaultHighlighter.highlight(sqlLine.getLineReader(), line); int defaultStyle = AttributedStyle.DEFAULT.getStyle(); for (int i = 0; i < line.length(); i++) { if (Character.isWhitespace(line.charAt(i))) { continue; } assertEquals(i == 0 ? defaultStyle + 32 : defaultStyle, attributedString.styleAt(i).getStyle(), getFailedStyleMessage(line, i, "default")); } } private void checkLineAgainstAllHighlighters( String line, ExpectedHighlightStyle expectedHighlightStyle) { for (Map.Entry<SqlLine, SqlLineHighlighter> mapEntry : sqlLine2Highlighter.entrySet()) { checkLineAgainstHighlighter( line, expectedHighlightStyle, mapEntry.getKey(), mapEntry.getValue()); } } private void checkLineAgainstHighlighter( String line, ExpectedHighlightStyle expectedHighlightStyle, SqlLine sqlLine, SqlLineHighlighter sqlLineHighlighter) { if (SqlLineProperty.DEFAULT.equals(sqlLine.getOpts().getColorScheme())) { checkDefaultLine(sqlLine, line, sqlLineHighlighter); } else { checkHighlightedLine(sqlLine, line, expectedHighlightStyle, sqlLineHighlighter); } } private void checkSymbolStyle( String line, int i, BitSet styleBitSet, AttributedString highlightedLine, int style, String styleName) { if (styleBitSet.get(i)) { assertEquals(i == 0 ? style + 32 : style, highlightedLine.styleAt(i).getStyle(), getFailedStyleMessage(line, i, styleName)); } else { if (!Character.isWhitespace(line.charAt(i))) { assertNotEquals(i == 0 ? style + 32 : style, highlightedLine.styleAt(i).getStyle(), getNegativeFailedStyleMessage(line, i, styleName)); } } } private String getFailedStyleMessage(String line, int i, String style) { return getFailedStyleMessage(line, i, style, true); } private String getNegativeFailedStyleMessage( String line, int i, String style) { return getFailedStyleMessage(line, i, style, false); } private String getFailedStyleMessage( String line, int i, String style, boolean positive) { return "String '" + line + "', symbol '" + line.charAt(i) + "' at (" + i + ") " + "position should " + (positive ? "" : "not ") + "be " + style + " style"; } } // End SqlLineHighlighterTest.java