| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to you 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 org.apache.calcite.avatica.test; |
| |
| import org.apache.calcite.avatica.ConnectStringParser; |
| |
| import org.junit.Test; |
| |
| import java.sql.SQLException; |
| import java.util.Properties; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.fail; |
| |
| /** |
| * Unit test for JDBC connect string parser, {@link ConnectStringParser}. The |
| * ConnectStringParser is adapted from code in Mondrian, but most of the tests |
| * below were unfortunately "reinvented" prior to having the Mondrian unit tests |
| * in hand. |
| */ |
| public class ConnectStringParserTest { |
| /** |
| * Tests simple connect string. Adapted from Mondrian tests. |
| */ |
| @Test public void testSimpleStrings() throws Throwable { |
| Properties props = ConnectStringParser.parse("foo=x;bar=y;foo=z"); |
| assertEquals( |
| "bar", |
| "y", |
| props.get("bar")); |
| assertNull( |
| "BAR", |
| props.get("BAR")); // case-sensitive, unlike Mondrian |
| assertEquals( |
| "last foo", |
| "z", |
| props.get("foo")); |
| assertNull( |
| "key=\" bar\"", |
| props.get(" bar")); |
| assertNull( |
| "bogus key", |
| props.get("kipper")); |
| assertEquals( |
| "param count", |
| 2, |
| props.size()); |
| |
| String synth = ConnectStringParser.getParamString(props); |
| Properties synthProps = ConnectStringParser.parse(synth); |
| assertEquals("reversible", props, synthProps); |
| } |
| |
| /** |
| * Tests complex connect strings. Adapted directly from Mondrian tests. |
| */ |
| @Test public void testComplexStrings() throws Throwable { |
| Properties props = |
| ConnectStringParser.parse("normalProp=value;" |
| + "emptyValue=;" |
| + " spaceBeforeProp=abc;" |
| + " spaceBeforeAndAfterProp =def;" |
| + " space in prop = foo bar ;" |
| + "equalsInValue=foo=bar;" |
| + "semiInProp;Name=value;" |
| + " singleQuotedValue = 'single quoted value ending in space ' ;" |
| + " doubleQuotedValue = " |
| + "\"=double quoted value preceded by equals\" ;" |
| + " singleQuotedValueWithSemi = 'one; two';" |
| + " singleQuotedValueWithSpecials = 'one; two \"three''four=five'"); |
| |
| assertEquals( |
| "param count", |
| 11, |
| props.size()); |
| |
| String value; |
| value = (String) props.get("normalProp"); |
| assertEquals("value", value); |
| value = (String) props.get("emptyValue"); |
| assertEquals("", value); // empty string, not null! |
| value = (String) props.get("spaceBeforeProp"); |
| assertEquals("abc", value); |
| value = (String) props.get("spaceBeforeAndAfterProp"); |
| assertEquals("def", value); |
| value = (String) props.get("space in prop"); |
| assertEquals(value, "foo bar"); |
| value = (String) props.get("equalsInValue"); |
| assertEquals("foo=bar", value); |
| value = (String) props.get("semiInProp;Name"); |
| assertEquals("value", value); |
| value = (String) props.get("singleQuotedValue"); |
| assertEquals("single quoted value ending in space ", value); |
| value = (String) props.get("doubleQuotedValue"); |
| assertEquals("=double quoted value preceded by equals", value); |
| value = (String) props.get("singleQuotedValueWithSemi"); |
| assertEquals(value, "one; two"); |
| value = (String) props.get("singleQuotedValueWithSpecials"); |
| assertEquals(value, "one; two \"three'four=five"); |
| } |
| |
| /** |
| * Tests for specific errors thrown by the parser. |
| */ |
| @Test public void testConnectStringErrors() throws Throwable { |
| // force some parsing errors |
| try { |
| ConnectStringParser.parse("key='can't parse'"); |
| fail("quoted value ended too soon"); |
| } catch (SQLException e) { |
| assertExceptionMatches(e, ".*quoted value ended.*position 9.*"); |
| } |
| |
| try { |
| ConnectStringParser.parse("key='\"can''t parse\""); |
| fail("unterminated quoted value"); |
| } catch (SQLException e) { |
| assertExceptionMatches(e, ".*unterminated quoted value.*"); |
| } |
| } |
| |
| /** |
| * Tests most of the examples from the <a |
| * href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbconnectionstringsyntax.asp"> |
| * OLE DB spec</a>. Omitted are cases for Window handles, returning multiple |
| * values, and special handling of "Provider" keyword. |
| */ |
| @Test public void testOleDbExamples() throws Throwable { |
| // test the parser with examples from OLE DB documentation |
| Quad[] quads = { |
| // {reason for test, key, val, string to parse}, |
| new Quad( |
| "printable chars", |
| "Jet OLE DB:System Database", "c:\\system.mda", |
| "Jet OLE DB:System Database=c:\\system.mda"), |
| new Quad( |
| "key embedded semi", |
| "Authentication;Info", "Column 5", |
| "Authentication;Info=Column 5"), |
| new Quad( |
| "key embedded equal", |
| "Verification=Security", "True", |
| "Verification==Security=True"), |
| new Quad( |
| "key many equals", |
| "Many==One", "Valid", |
| "Many====One=Valid"), |
| new Quad( |
| "key too many equal", |
| "TooMany=", "False", |
| "TooMany===False"), |
| new Quad( |
| "value embedded quote and semi", |
| "ExtProps", "Data Source='localhost';Key Two='value 2'", |
| "ExtProps=\"Data Source='localhost';Key Two='value 2'\""), |
| new Quad( |
| "value embedded double quote and semi", |
| "ExtProps", "Integrated Security=\"SSPI\";Key Two=\"value 2\"", |
| "ExtProps='Integrated Security=\"SSPI\";Key Two=\"value 2\"'"), |
| new Quad( |
| "value double quoted", |
| "DataSchema", "\"MyCustTable\"", |
| "DataSchema='\"MyCustTable\"'"), |
| new Quad( |
| "value single quoted", |
| "DataSchema", "'MyCustTable'", |
| "DataSchema=\"'MyCustTable'\""), |
| new Quad( |
| "value double quoted double trouble", |
| "Caption", "\"Company's \"new\" customer\"", |
| "Caption=\"\"\"Company's \"\"new\"\" customer\"\"\""), |
| new Quad( |
| "value single quoted double trouble", |
| "Caption", "\"Company's \"new\" customer\"", |
| "Caption='\"Company''s \"new\" customer\"'"), |
| new Quad( |
| "embedded blanks and trim", |
| "My Keyword", "My Value", |
| " My Keyword = My Value ;MyNextValue=Value"), |
| new Quad( |
| "value single quotes preserve blanks", |
| "My Keyword", " My Value ", |
| " My Keyword =' My Value ';MyNextValue=Value"), |
| new Quad( |
| "value double quotes preserve blanks", |
| "My Keyword", " My Value ", |
| " My Keyword =\" My Value \";MyNextValue=Value"), |
| new Quad( |
| "last redundant key wins", |
| "SomeKey", "NextValue", |
| "SomeKey=FirstValue;SomeKey=NextValue"), |
| }; |
| for (Quad quad : quads) { |
| Properties props = ConnectStringParser.parse(quad.str); |
| |
| assertEquals(quad.why, quad.val, props.get(quad.key)); |
| String synth = ConnectStringParser.getParamString(props); |
| |
| try { |
| assertEquals("reversible " + quad.why, quad.str, synth); |
| } catch (Throwable e) { |
| // it's OK that the strings don't match as long as the |
| // two strings parse out the same way and are thus |
| // "semantically reversible" |
| Properties synthProps = ConnectStringParser.parse(synth); |
| assertEquals("equivalent " + quad.why, props, synthProps); |
| } |
| } |
| } |
| |
| static void assertExceptionMatches( |
| Throwable e, |
| String expectedPattern) { |
| if (e == null) { |
| fail("Expected an error which matches pattern '" + expectedPattern + "'"); |
| } |
| String msg = e.toString(); |
| if (!msg.matches(expectedPattern)) { |
| fail("Got a different error '" + msg + "' than expected '" |
| + expectedPattern + "'"); |
| } |
| } |
| |
| /** Collection of values comprising a test. */ |
| static class Quad { |
| private final String why; |
| private final String key; |
| private final String val; |
| private final String str; |
| |
| Quad(String why, String key, String val, String str) { |
| this.why = why; |
| this.key = key; |
| this.val = val; |
| this.str = str; |
| } |
| } |
| } |
| |
| // End ConnectStringParserTest.java |