package aQute.lib.utf8properties; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.Properties; import aQute.lib.io.IO; import aQute.libg.reporter.ReporterAdapter; import aQute.service.reporter.Report.Location; import junit.framework.TestCase; /** * Test if we properly can read * * @author aqute */ public class UTF8PropertiesTest extends TestCase { public void testMissingDelimeterAfterQuotedString() throws IOException { assertError("-foo: bar='abc' ' ' ;", "-foo", 0, "Found a quote ''' while expecting a delimeter. You should quote the whole values, you can use both single and double quotes:"); assertError("-foo: bar='abc', baz='def' goo='hji'", "-foo", 0, "Expected a delimeter, like comma or semicolon, after a quoted string but found 'g':"); assertError("-foo: bar='abc' , baz='def'", "-foo", 0); assertError("-foo: bar='abc' ;", "-foo", 0); assertError("-foo: bar='\\'abc\\' foo' ;", "-foo", 0); assertError("-foo: bar=\"\\\"abc\\\" foo\" ;", "-foo", 0); assertError("foo: bar='abc' ' ' ;", "foo", 0); assertError("foo: abc \" \" ' '", "foo", 0); } String trickypart = "\u00A0\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF" + "\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF" + "\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF" + "\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF" + "\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF" + "\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF"; /* * Entries are generally expected to be a single line of the form, one of * the following: propertyName=propertyValue propertyName:propertyValue} */ public void testSpecificationAssignment() throws IOException { testProperty("propertyName=propertyValue\n", "propertyName", "propertyValue"); testProperty("propertyName:propertyValue\n", "propertyName", "propertyValue"); testProperty("propertyName propertyValue\n", "propertyName", "propertyValue"); } /* * White space that appears between the property name and property value is * ignored, so the following are equivalent. */ public void testSpecificationValueSpaces() throws IOException { testProperty("name=Stephen\n", "name", "Stephen"); testProperty("name = Stephen\n", "name", "Stephen"); testProperty("name=Stephen", "name", "Stephen"); testProperty("name = Stephen", "name", "Stephen"); } /* * White space at the beginning of the line is also ignored. */ public void testSpecificationKeySpaces() throws IOException { testProperty(" name = Stephen\n", "name", "Stephen"); testProperty(" name = Stephen", "name", "Stephen"); } /* * Lines that start with the comment characters ! or # are ignored. Blank * lines are also ignored. */ public void testSpecificationComments() throws IOException { testProperty("\n\n# comment\n!comment\n\nfoo=bar", "foo", "bar"); testProperty("foo=bar\n# comment", "foo", "bar"); testProperty("foo=bar\n! comment", "foo", "bar"); testProperty("# comment\nfoo=bar\n# comment", "foo", "bar"); testProperty("! comment\nfoo=bar\n! comment", "foo", "bar"); testProperty("# comment\nfoo=bar\n# comment\n", "foo", "bar"); testProperty("! comment\nfoo=bar\n! comment\n", "foo", "bar"); } /* * Lines that start with the comment characters ! or # are ignored. Blank * lines are also ignored. Comments which end with \ must not continue to * next line. */ public void testSpecificationCommentContinuation() throws IOException { testProperty("\n\n# comment\\\n!comment\\\n\nfoo=bar", "foo", "bar"); testProperty("foo=bar\n# comment\\", "foo", "bar"); testProperty("foo=bar\n! comment\\", "foo", "bar"); testProperty("# comment\\\nfoo=bar\n# comment\\", "foo", "bar"); testProperty("! comment\\\nfoo=bar\n! comment\\", "foo", "bar"); testProperty("# comment\\\nfoo=bar\n# comment\\\n", "foo", "bar"); testProperty("! comment\\\nfoo=bar\n! comment\\\n", "foo", "bar"); } /* * The property value is generally terminated by the end of the line. White * space following the property value is not ignored, and is treated as part * of the property value. */ public void testSpecificationWhitespaceValue() throws IOException { testProperty("foo=bar ", "foo", "bar "); testProperty("foo=bar \n", "foo", "bar "); } /* * A property value can span several lines if each line is terminated by a * backslash (‘\’) character. For example: */ public void testSpecificationContinutation() throws IOException { testProperty("targetCities=\\\n" // + " Detroit,\\\n" // + " Chicago,\\\n" // + " Los Angeles\n", "targetCities", "Detroit,Chicago,Los Angeles"); } /* * The characters newline, carriage return, and tab can be inserted with * characters \n, \r, and \t, respectively. */ public void testSpecificationControlCharacters() throws IOException { testProperty("control= \\t\\n\\r\n", "control", "\t\n\r"); } /* * The backslash character must be escaped as a double backslash. For * example: */ public void testSpecificationBackslashes() throws IOException { testProperty("path=c:\\\\docs\\\\doc1", "path", "c:\\docs\\doc1"); } /* * UNICODE characters can be entered as they are in a Java program, using * the \\u prefix. For example, \u002c. */ public void testSpecificationUnicode() throws IOException { testProperty("unicode=\\u002c\n", "unicode", ","); testProperty("unicode=\\uFEF0\n", "unicode", "\uFEF0"); } /* * You can have control characters in keys */ public void testControlCharactersInKeys() throws IOException { testProperty("key\\ key = value\n", "key key", "value", "Found |Invalid"); testProperty("key\\u002Ckey = value\n", "key,key", "value", "Found |Invalid"); testProperty("key\\:key = value\n", "key:key", "value", "Found |Invalid"); } public void testEmptyContinuations() throws IOException { testProperty("-plugin: \\\n\\\n\\\nabc", "-plugin", "abc"); testProperty("-plugin: \\\n\\\n\\\nabc\n", "-plugin", "abc"); testProperty("-plugin: \\\n\\\n\\\n abc\n", "-plugin", "abc"); testProperty("-plugin: \\\n \\\n \\\n abc\n", "-plugin", "abc"); } public void testEmptyKey() throws IOException { UTF8Properties p = new UTF8Properties(); ReporterAdapter ra = new ReporterAdapter(); p.load(" -runvm: \n" // + "#\"-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1044\"", null, ra); assertEquals("", p.get("-runvm")); assertEquals(1, p.size()); } public void testEmptySpace() throws IOException { UTF8Properties p = new UTF8Properties(); ReporterAdapter ra = new ReporterAdapter(); p.load("version 1.1\n ", null, ra); assertEquals("1.1", p.get("version")); assertEquals(1, p.size()); } public void testNewlineCr() throws IOException { UTF8Properties p = new UTF8Properties(); ReporterAdapter ra = new ReporterAdapter(); p.load("a=2\n\rb=3\n\r", null, ra); assertEquals("2", p.get("a")); assertEquals("3", p.get("b")); assertEquals(2, p.size()); } public void testEscapedNewlinEmpty() throws IOException { UTF8Properties p = new UTF8Properties(); ReporterAdapter ra = new ReporterAdapter(); p.load("a=x\\\n\n", null, ra); assertEquals("x", p.get("a")); assertEquals(1, p.size()); } public void testPackageinfo() throws IOException { UTF8Properties p = new UTF8Properties(); ReporterAdapter ra = new ReporterAdapter(); p.load("version 1.0.1", null, ra); assertEquals("1.0.1", p.get("version")); assertEquals(1, p.size()); } public void testPackageinfoWithCrLf() throws IOException { UTF8Properties p = new UTF8Properties(); ReporterAdapter ra = new ReporterAdapter(); p.load("version 1.0.1\r\n", null, ra); assertEquals("1.0.1", p.get("version")); assertEquals(1, p.size()); } public void testErrorsInParsing() throws IOException { assertError("\n\n\n\n\n\n\n" // + "a;b=9", "a;b", 7, "Invalid property key: `a;b`:"); assertError("\n" // + "a=\\ \n a;v=4", "a", 1, "Found \\\\<whitespace>", "Invalid property key: `a;v`"); assertError("\n\n\n\n\n\n\n" // + "a", "a", 7, "No value specified for key"); assertError("\npropertyName=property\0Value\n", "propertyName", 1, "Invalid character in properties: 0 at pos 21:"); assertError("\nproperty\0Name=propertyValue\n", "property?Name", 1, "Invalid character in properties: 0 at pos 8:"); } private void assertError(String string, String key, int line, String... check) throws IOException { ReporterAdapter ra = new ReporterAdapter(); UTF8Properties up = new UTF8Properties(); up.load(string, IO.getFile("foo"), ra); assertNotNull("No '" + key + "' property found", up.getProperty(key)); if (check.length == 0) assertEquals(0, ra.getErrors().size()); else { assertTrue(ra.getErrors().size() > 0); Location location = ra.getLocation(ra.getErrors().get(0)); assertEquals("Faulty line number", line, location.line); } assertTrue(ra.check(check)); } public void testBackslashEncodingWithReader() throws IOException { Properties p = new UTF8Properties(); p.load(new StringReader("x=abc \\\\ def\n")); assertEquals("abc \\ def", p.get("x")); } public void testISO8859Encoding() throws IOException { Properties p = new UTF8Properties(); p.load(new ByteArrayInputStream(("x=" + trickypart + "\n").getBytes("ISO-8859-1"))); assertEquals(trickypart, p.get("x")); } public void testUTF8Encoding() throws IOException { Properties p = new UTF8Properties(); p.load(new ByteArrayInputStream(("x=" + trickypart + "\n").getBytes("UTF-8"))); assertEquals(trickypart, p.get("x")); } public void testShowUTF8PropertiesDoNotSkipBackslash() throws IOException { Properties p = new UTF8Properties(); p.load(new ByteArrayInputStream("x=abc \\ def\n".getBytes("UTF-8"))); assertEquals("abc def", p.get("x")); } public void testShowPropertiesSkipBackslash() throws IOException { Properties p = new Properties(); p.load(new StringReader("x=abc \\ def\n")); assertEquals("abc def", p.get("x")); } public void testContinuation() throws IOException { Properties p = new Properties(); p.load(new StringReader("x=abc \\\n def\n")); assertEquals("abc def", p.get("x")); } public void testWriteWithoutComment() throws IOException { UTF8Properties p = new UTF8Properties(); p.put("Foo", "Foo"); ByteArrayOutputStream bout = new ByteArrayOutputStream(); p.store(bout); String s = new String(bout.toByteArray(), "UTF-8"); assertFalse(s.startsWith("#")); assertTrue("Foo should be in there", s.indexOf("Foo") >= 0); } public void testWrite() throws IOException { UTF8Properties p = new UTF8Properties(); p.put("Foo", "Foo"); p.put("Bar", "Bar\n#comment"); StringWriter sw = new StringWriter(); p.store(sw, null); String s = sw.toString(); assertNotNull(s); assertTrue(s.contains("#comment")); UTF8Properties p1 = new UTF8Properties(); p1.load(new StringReader(s)); assertEquals(p, p1); } private void testProperty(String content, String key, String value) throws IOException { testProperty(content, key, value, null); } private void testProperty(String content, String key, String value, String check) throws IOException { testProperty0(content, key, value, check); testProperty0(content.replaceAll("\n", "\r\n"), key, value, check); testProperty0(content.replaceAll("\n", "\r"), key, value, check); } private void testProperty0(String content, String key, String value, String check) throws IOException { UTF8Properties p = new UTF8Properties(); ReporterAdapter ra = new ReporterAdapter(); p.load(content, null, ra); if (check == null) assertTrue(ra.check()); else assertTrue(ra.check(check)); assertEquals(value, p.get(key)); assertEquals(1, p.size()); Properties pp = new Properties(); pp.load(new StringReader(content)); assertEquals(value, pp.get(key)); assertEquals(1, pp.size()); assertEquals(pp, p); } }