You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/08/01 07:03:08 UTC

svn commit: r427444 - /incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java

Author: pcl
Date: Mon Jul 31 22:03:08 2006
New Revision: 427444

URL: http://svn.apache.org/viewvc?rev=427444&view=rev
Log:
Brett Porter's patch to resolve OPENJPA-3

Modified:
    incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java

Modified: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java?rev=427444&r1=427443&r2=427444&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java (original)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java Mon Jul 31 22:03:08 2006
@@ -1,378 +1,394 @@
-/*
- * Copyright 2006 The Apache Software Foundation.
- *  Licensed 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.openjpa.lib.util;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.StringBufferInputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Properties;
-
-import junit.framework.TestCase;
-import org.apache.openjpa.lib.util.FormatPreservingProperties.DuplicateKeyException;
-
-// things to test:
-// - delimiters in keys
-// - escape chars, including \:, \= in files(as generated by Properties)
-// - unicode
-// - non-String keys / vals
-// - list() method behavior
-
-public class TestPropertiesParser extends TestCase {
-
-    public void testSimpleProperties() throws IOException {
-        StringBuffer buf = new StringBuffer();
-        buf.append("key: value\n");
-        buf.append("key2: value2"); // no EOL -- this is intentional
-        Properties p = toProperties(buf.toString());
-        assertProperties(new String[][]{
-            { "key", "value" }, { "key2", "value2" } }, p);
-    }
-
-    public void testComments() throws IOException {
-        StringBuffer buf = new StringBuffer();
-        buf.append("# this is a comment\n");
-        buf.append(" # another one, with leading whitespace	\n");
-        buf.append(" 	# 	and more with interesting whitespace	\n");
-        buf.append("! and with a ! delimiter\n");
-        buf.append("! and with escape \t chars\n");
-        buf.append("#and a comment with no whitespace\n");
-        Properties p = toProperties(buf.toString());
-        assertEquals(0, p.size());
-    }
-
-    public void testMixedContent() throws IOException {
-        StringBuffer buf = new StringBuffer();
-        buf.append("# this is a comment\n");
-        buf.append(" # another one, with leading whitespace	\n");
-        buf.append("foo: bar#baz\n");
-        buf.append("! and with a ! delimiter\n");
-        buf.append("! and with escape \t chars\n");
-        Properties p = toProperties(buf.toString());
-        assertProperties(new String[][]{ { "foo", "bar#baz" } }, p);
-    }
-
-    public void testMultiLineInput() throws IOException {
-        String s = "foo: bar\\\n" + "more line goes here";
-        Properties p = toProperties(s);
-        assertProperties(
-            new String[][]{ { "foo", "barmore line goes here" } }, p);
-    }
-
-    public void testEmptyLines() throws IOException {
-        Properties p = toProperties("\nfoo: bar\n\nbaz: val");
-        assertProperties(new String[][]{ { "foo", "bar" }, { "baz", "val" } },
-            p);
-    }
-
-    public void testAddProperties() throws IOException {
-        // intentionally left out the trailing end line
-        String s = "foo: bar\nbaz: val";
-        Properties p = toProperties(s);
-        assertProperties(new String[][]{ { "foo", "bar" }, { "baz", "val" } },
-            p);
-
-        p.put("new-key", "val1");
-        p.put("new-key-2", "val2");
-        p.put("another-new-key", "val3");
-        assertRoundTrip(s + "\nnew-key: val1\nnew-key-2: val2\n" +
-            "another-new-key: val3\n", p);
-    }
-
-    public void testAddAndMutateProperties() throws IOException {
-        // intentionally left out the trailing end line
-        Properties p = toProperties("foo: bar\nbaz: val");
-        assertProperties(new String[][]{ { "foo", "bar" }, { "baz", "val" } },
-            p);
-
-        p.put("new-key", "new value");
-        p.put("foo", "barbar");
-        assertRoundTrip("foo: barbar\nbaz: val\nnew-key: new value\n", p);
-    }
-
-    public void testEscapedEquals() throws IOException {
-        Properties p = toProperties("foo=bar\\=WARN,baz\\=TRACE");
-        assertProperties(new String[][]{ { "foo", "bar=WARN,baz=TRACE" } }, p);
-    }
-
-    public void testLineTypes() throws IOException {
-        StringBuffer buf = new StringBuffer();
-        buf.append("   !comment\n \t  \nname = no\n    "
-            + "#morec\tomm\\\nents\n\n  dog=no\\cat   \nburps    "
-            + ":\ntest=\ndate today\n\n\nlong\\\n   value=tryin \\\n "
-            + "gto\n4:vier\nvier     :4");
-        Properties p = toProperties(buf.toString());
-        assertProperties(new String[][]{
-            { "name", "no" }, { "ents", "" }, { "dog", "nocat   " },
-            { "burps", "" }, { "test", "" }, { "date", "today" },
-            { "longvalue", "tryin gto" }, { "4", "vier" }, { "vier", "4" },
-        }, p);
-    }
-
-    public void testSpecialChars() throws Throwable {
-        testSpecialChars(false, true);
-        testSpecialChars(true, true);
-        testSpecialChars(false, false);
-        testSpecialChars(true, false);
-    }
-
-    /**
-     * Test that special characters work.
-     *
-     * @param formattingProps if true, test against the
-     * FormatPreservingProperties, otherwise test
-     * against a normal Properties instance(for validation of the test case).
-     * @param value whether to test the key or the value
-     */
-    public void testSpecialChars(boolean formattingProps, boolean value)
-        throws Throwable {
-        List valueList = new ArrayList(Arrays.asList(new String[]{
-            "xxyy", "xx\\yy", "xx\nyy", "xx\\nyy", "xx\tyy", "xx\\tyy",
-            "xx\ryy", "xx\\ryy", "xx\fyy", "xx\\fyy", "xx\r\n\\\t\r\t\nyy",
-            "xx\\r\n\\\t\\r\t\\nyy",
-            "xx\r\n\\\\\\\\\\\\\\\\\\\\\\\\\\\t\r\t\nyy",
-            "C:\\Program Files\\Some Application\\OpenJPA\\My File.dat", }));
-
-        // also store every individual character
-        for (char c = 'a'; c < 'Z'; c++) {
-            valueList.add(new String(new char[]{ c }));
-            valueList.add(new String(new char[]{ c, '\\', c }));
-            valueList.add(new String(new char[]{ '\\', c }));
-        }
-
-        String[] values = (String[]) valueList.toArray(new String[0]);
-
-        final String dummy = "XXX";
-
-        for (int i = 0; i < values.length; i++) {
-            // test special characters in either keys or values
-            String val = value ? values[i] : dummy;
-            String key = value ? dummy : values[i];
-
-            Properties p = formattingProps ?
-                new FormatPreservingProperties() : new Properties();
-            if (p instanceof FormatPreservingProperties) {
-                // set these properties so we behave the same way as
-                // java.util.Properties
-                ((FormatPreservingProperties) p).setDefaultEntryDelimiter('=');
-                ((FormatPreservingProperties) p).
-                    setAddWhitespaceAfterDelimiter(false);
-            }
-
-            p.setProperty(key, val);
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            p.store(out, null);
-
-            Properties copy = new Properties();
-            copy.setProperty(key, val);
-            ByteArrayOutputStream copyOut = new ByteArrayOutputStream();
-            copy.store(copyOut, null);
-
-            p = formattingProps ?
-                new FormatPreservingProperties() : new Properties();
-
-            InputStream in = new BufferedInputStream
-                (new ByteArrayInputStream(out.toByteArray()));
-
-            try {
-                // make sure that the 2 properties serialized are the same
-                String copyOutString = stripComments(copyOut.toByteArray());
-                String outString = stripComments(out.toByteArray());
-                assertEquals(copyOutString, outString);
-
-                p.load(in);
-
-                assertNotNull("Property \"" + key + "\" was null",
-                    p.getProperty(key));
-                assertEquals(val.trim(), p.getProperty(key).trim());
-            } catch (Throwable ioe) {
-                if (!formattingProps)
-                    throw ioe;
-
-                // bug(1211, ioe,
-                // "Cannot store backslash in FormatPreservingProperties");
-                throw ioe;
-            }
-        }
-    }
-
-    static Character randomChar() {
-        char [] TEST_CHAR_ARRAY = new char []{
-            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
-            'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
-            's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1',
-            '2', '3', '4', '5', '6', '7', '8', '9' };
-
-        return new Character(TEST_CHAR_ARRAY[
-            (int) (Math.random() * TEST_CHAR_ARRAY.length)]);
-    }
-
-    static String randomString(int len) {
-        StringBuffer buf = new StringBuffer();
-        for (int i = 0; i < (int) (Math.random() * len) + 1; i++)
-            buf.append(randomChar());
-        return buf.toString();
-    }
-
-    public void testEquivalentStore() throws IOException {
-        Properties p1 = new Properties();
-        FormatPreservingProperties p2 = new FormatPreservingProperties();
-
-        ((FormatPreservingProperties) p2).setDefaultEntryDelimiter('=');
-        ((FormatPreservingProperties) p2).setAddWhitespaceAfterDelimiter(false);
-
-        String[] values = new String[]{
-            "x", "x\ny", "x\\ny", "x\ty", "x\\ty", "x\fy", "x\\fy", "x\ry",
-            "x\\ry", "C:\\Foo Bar\\Baz", randomString(5).replace('a', '\\'),
-            randomString(500).replace('a', '\\'),
-            randomString(5000).replace('a', '\\'), };
-
-        for (int i = 0; i < values.length; i++) {
-            p1.clear();
-            p2.clear();
-
-            p1.setProperty("xxx", values[i]);
-            p2.setProperty("xxx", values[i]);
-
-            ByteArrayOutputStream out1 = new ByteArrayOutputStream();
-            ByteArrayOutputStream out2 = new ByteArrayOutputStream();
-
-            p1.store(out1, null);
-            p2.store(out2, null);
-
-            String s1 = new String(out1.toByteArray());
-            String s2 = new String(out2.toByteArray());
-
-            assertTrue("Expected <" + s1 + "> but was <" + s2 + ">",
-                s1.indexOf(s2) != -1);
-        }
-    }
-
-    static String stripComments(byte[] bytes) throws IOException {
-        BufferedReader reader = new BufferedReader(new InputStreamReader
-            (new ByteArrayInputStream(bytes)));
-        StringBuffer sbuf = new StringBuffer();
-        String line;
-        while ((line = reader.readLine()) != null) {
-            // skip comments
-            if (line.trim().startsWith("#"))
-                continue;
-
-            sbuf.append(line);
-            sbuf.append('\n');
-        }
-
-        return sbuf.toString();
-    }
-
-    public void testDuplicateProperties() throws IOException {
-        FormatPreservingProperties p = new FormatPreservingProperties();
-        try {
-            toProperties("foo=bar\nfoo=baz", p);
-            fail("expected duplicate keys to cause exception");
-        } catch (DuplicateKeyException e) {
-            // expected
-        }
-
-        // now test the expected behavior when duplicates are allowed.
-        p = new FormatPreservingProperties();
-        p.setAllowDuplicates(true);
-        toProperties("foo=bar\nfoo=baz", p);
-        assertProperties(new String[][]{ { "foo", "baz" } }, p);
-    }
-
-    public void testMultipleLoads() throws IOException {
-        String props = "foo=bar\nbaz=quux";
-        String props2 = "a=b\nc=d";
-        Properties vanilla = new Properties();
-        vanilla.load(new BufferedInputStream
-            (new StringBufferInputStream(props)));
-        vanilla.load(new BufferedInputStream
-            (new StringBufferInputStream(props2)));
-
-        Properties p = new FormatPreservingProperties();
-        p.load(new BufferedInputStream(new StringBufferInputStream(props)));
-        p.load(new BufferedInputStream(new StringBufferInputStream(props2)));
-        assertPropertiesSame(vanilla, p);
-    }
-
-    protected FormatPreservingProperties toProperties(String s)
-        throws IOException {
-        return toProperties(s, new FormatPreservingProperties());
-    }
-
-    protected FormatPreservingProperties toProperties(String s,
-        FormatPreservingProperties p) throws IOException {
-        Properties vanilla = new Properties();
-        vanilla.load(new StringBufferInputStream(s));
-
-        p.load(new StringBufferInputStream(s));
-        assertRoundTrip(s, p);
-
-        assertPropertiesSame(vanilla, p);
-
-        return p;
-    }
-
-    private void assertRoundTrip(String s, Properties p) throws IOException {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        p.store(out, null);
-        assertEquals(s, out.toString());
-
-        // also check serializable
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        new ObjectOutputStream(bout).writeObject(p);
-
-        try {
-            FormatPreservingProperties deserialized =
-                (FormatPreservingProperties) new ObjectInputStream
-                    (new ByteArrayInputStream(bout.toByteArray())).
-                    readObject();
-            assertEquals(p, deserialized);
-
-            out = new ByteArrayOutputStream();
-            deserialized.store(out, null);
-            assertEquals(s, out.toString());
-        } catch (ClassNotFoundException cnfe) {
-            fail(cnfe + "");
-        }
-    }
-
-    public static void assertEquals(String expected, String actual) {
-        if (expected == actual)
-            return;
-
-        if (expected == null || !expected.equals(actual))
-            fail("Expected <" + expected + "> but was <" + actual + ">");
-    }
-
-    private void assertPropertiesSame(Properties vanilla, Properties p) {
-        assertEquals(vanilla, p);
-    }
-
-    protected void assertProperties(String[][] strings, Properties p) {
-        for (int i = 0; i < strings.length; i++)
-            assertEquals(strings[i][1], p.get(strings[i][0]));
-
-        assertEquals(strings.length, p.size());
-    }
-}
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.StringBufferInputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+import org.apache.openjpa.lib.util.FormatPreservingProperties.DuplicateKeyException;
+
+// things to test:
+// - delimiters in keys
+// - escape chars, including \:, \= in files(as generated by Properties)
+// - unicode
+// - non-String keys / vals
+// - list() method behavior
+
+public class TestPropertiesParser extends TestCase {
+
+    private static final String LS = System.getProperty( "line.separator" );
+
+    public void testSimpleProperties() throws IOException {
+        StringBuffer buf = new StringBuffer();
+        buf.append("key: value" + LS);
+        buf.append("key2: value2"); // no EOL -- this is intentional
+        Properties p = toProperties(buf.toString());
+        assertProperties(new String[][]{
+            { "key", "value" }, { "key2", "value2" } }, p);
+    }
+
+    public void testComments() throws IOException {
+        StringBuffer buf = new StringBuffer();
+        buf.append("# this is a comment" + LS);
+        buf.append(" # another one, with leading whitespace	" + LS);
+        buf.append(" 	# 	and more with interesting whitespace	" + LS);
+        buf.append("! and with a ! delimiter" + LS);
+        buf.append("! and with escape \t chars" + LS);
+        buf.append("#and a comment with no whitespace" + LS);
+        Properties p = toProperties(buf.toString());
+        assertEquals(0, p.size());
+    }
+
+    public void testMixedContent() throws IOException {
+        StringBuffer buf = new StringBuffer();
+        buf.append("# this is a comment" + LS);
+        buf.append(" # another one, with leading whitespace	" + LS);
+        buf.append("foo: bar#baz" + LS);
+        buf.append("! and with a ! delimiter" + LS);
+        buf.append("! and with escape \t chars" + LS);
+        Properties p = toProperties(buf.toString());
+        assertProperties(new String[][]{ { "foo", "bar#baz" } }, p);
+    }
+
+    public void testMultiLineInput() throws IOException {
+        String s = "foo: bar\\" + LS + "more line goes here";
+        Properties p = toProperties(s);
+        assertProperties(
+            new String[][]{ { "foo", "barmore line goes here" } }, p);
+    }
+
+    public void testEmptyLines() throws IOException {
+        Properties p = toProperties(LS + "foo: bar" + LS + LS + "baz: val");
+        assertProperties(new String[][]{ { "foo", "bar" }, { "baz", "val" } },
+            p);
+    }
+
+    public void testAddProperties() throws IOException {
+        // intentionally left out the trailing end line
+        String s = "foo: bar" + LS + "baz: val";
+        Properties p = toProperties(s);
+        assertProperties(new String[][]{ { "foo", "bar" }, { "baz", "val" } },
+            p);
+
+        p.put("new-key", "val1");
+        p.put("new-key-2", "val2");
+        p.put("another-new-key", "val3");
+        assertRoundTrip(s + LS + "new-key: val1" + LS + "new-key-2: val2" + LS +
+            "another-new-key: val3" + LS, p);
+    }
+
+    public void testAddAndMutateProperties() throws IOException {
+        // intentionally left out the trailing end line
+        Properties p = toProperties("foo: bar" + LS + "baz: val");
+        assertProperties(new String[][]{ { "foo", "bar" }, { "baz", "val" } },
+            p);
+
+        p.put("new-key", "new value");
+        p.put("foo", "barbar");
+        assertRoundTrip("foo: barbar" + LS + "baz: val" + LS 
+            + "new-key: new value" + LS, p);
+    }
+
+    public void testEscapedEquals() throws IOException {
+        Properties p = toProperties("foo=bar\\=WARN,baz\\=TRACE");
+        assertProperties(new String[][]{ { "foo", "bar=WARN,baz=TRACE" } }, p);
+    }
+
+    public void testLineTypes() throws IOException {
+        StringBuffer buf = new StringBuffer();
+        buf.append("   !comment" + LS + " \t  " + LS + "name = no" + LS + "    "
+            + "#morec\tomm\\" + LS + "ents" + LS + LS + "  dog=no\\cat   " + LS 
+            + "burps    :" + LS + "test=" + LS + "date today" + LS + LS + LS 
+            + "long\\" + LS + "   value=tryin \\" + LS + " "
+            + "gto" + LS + "4:vier" + LS + "vier     :4");
+        Properties p = toProperties(buf.toString());
+        assertProperties(new String[][]{
+            { "name", "no" }, { "ents", "" }, { "dog", "nocat   " },
+            { "burps", "" }, { "test", "" }, { "date", "today" },
+            { "longvalue", "tryin gto" }, { "4", "vier" }, { "vier", "4" },
+        }, p);
+    }
+
+    public void testSpecialChars() throws Throwable {
+        testSpecialChars(false, true);
+        testSpecialChars(true, true);
+        testSpecialChars(false, false);
+        testSpecialChars(true, false);
+    }
+
+    /**
+     * Test that special characters work.
+     *
+     * @param formattingProps if true, test against the
+     * FormatPreservingProperties, otherwise test
+     * against a normal Properties instance(for validation of the test case).
+     * @param value whether to test the key or the value
+     */
+    public void testSpecialChars(boolean formattingProps, boolean value)
+        throws Throwable {
+        List valueList = new ArrayList(Arrays.asList(new String[]{
+            "xxyy", "xx\\yy", "xx" + LS + "yy", "xx\\nyy", "xx\tyy", "xx\\tyy",
+            "xx\ryy", "xx\\ryy", "xx\fyy", "xx\\fyy", "xx\r" + LS + "\\\t\r\t" 
+            + LS + "yy",
+            "xx\\r" + LS + "\\\t\\r\t\\nyy",
+            "xx\r" + LS + "\\\\\\\\\\\\\\\\\\\\\\\\\\\t\r\t" + LS + "yy",
+            "C:\\Program Files\\Some Application\\OpenJPA\\My File.dat", }));
+
+        // also store every individual character
+        for (char c = 'a'; c < 'Z'; c++) {
+            valueList.add(new String(new char[]{ c }));
+            valueList.add(new String(new char[]{ c, '\\', c }));
+            valueList.add(new String(new char[]{ '\\', c }));
+        }
+
+        String[] values = (String[]) valueList.toArray(new String[0]);
+
+        final String dummy = "XXX";
+
+        for (int i = 0; i < values.length; i++) {
+            // test special characters in either keys or values
+            String val = value ? values[i] : dummy;
+            String key = value ? dummy : values[i];
+
+            Properties p = formattingProps ?
+                new FormatPreservingProperties() : new Properties();
+            if (p instanceof FormatPreservingProperties) {
+                // set these properties so we behave the same way as
+                // java.util.Properties
+                ((FormatPreservingProperties) p).setDefaultEntryDelimiter('=');
+                ((FormatPreservingProperties) p).
+                    setAddWhitespaceAfterDelimiter(false);
+            }
+
+            p.setProperty(key, val);
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            p.store(out, null);
+
+            Properties copy = new Properties();
+            copy.setProperty(key, val);
+            ByteArrayOutputStream copyOut = new ByteArrayOutputStream();
+            copy.store(copyOut, null);
+
+            p = formattingProps ?
+                new FormatPreservingProperties() : new Properties();
+
+            InputStream in = new BufferedInputStream
+                (new ByteArrayInputStream(out.toByteArray()));
+
+            try {
+                // make sure that the 2 properties serialized are the same
+                String copyOutString = stripComments(copyOut.toByteArray());
+                String outString = stripComments(out.toByteArray());
+                assertEquals(copyOutString, outString);
+
+                p.load(in);
+
+                assertNotNull("Property \"" + key + "\" was null",
+                    p.getProperty(key));
+                assertEquals(val.trim(), p.getProperty(key).trim());
+            } catch (Throwable ioe) {
+                if (!formattingProps)
+                    throw ioe;
+
+                // bug(1211, ioe,
+                // "Cannot store backslash in FormatPreservingProperties");
+                throw ioe;
+            }
+        }
+    }
+
+    static Character randomChar() {
+        char [] TEST_CHAR_ARRAY = new char []{
+            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+            'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+            's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1',
+            '2', '3', '4', '5', '6', '7', '8', '9' };
+
+        return new Character(TEST_CHAR_ARRAY[
+            (int) (Math.random() * TEST_CHAR_ARRAY.length)]);
+    }
+
+    static String randomString(int len) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < (int) (Math.random() * len) + 1; i++)
+            buf.append(randomChar());
+        return buf.toString();
+    }
+
+    public void testEquivalentStore() throws IOException {
+        Properties p1 = new Properties();
+        FormatPreservingProperties p2 = new FormatPreservingProperties();
+
+        ((FormatPreservingProperties) p2).setDefaultEntryDelimiter('=');
+        ((FormatPreservingProperties) p2).setAddWhitespaceAfterDelimiter(false);
+
+        String[] values =
+            new String[] { 
+                "x", 
+                "x" + LS + "y", 
+                "x\\ny", 
+                "x\ty", 
+                "x\\ty",
+                "x\fy", 
+                "x\\fy", 
+                "x\ry", 
+                "x\\ry", 
+                "C:\\Foo Bar\\Baz",
+                randomString(5).replace('a', '\\'),
+                randomString(500).replace('a', '\\'),
+                randomString(5000).replace('a', '\\'), 
+                };
+
+        for (int i = 0; i < values.length; i++) {
+            p1.clear();
+            p2.clear();
+
+            p1.setProperty("xxx", values[i]);
+            p2.setProperty("xxx", values[i]);
+
+            ByteArrayOutputStream out1 = new ByteArrayOutputStream();
+            ByteArrayOutputStream out2 = new ByteArrayOutputStream();
+
+            p1.store(out1, null);
+            p2.store(out2, null);
+
+            String s1 = new String(out1.toByteArray());
+            String s2 = new String(out2.toByteArray());
+
+            assertTrue("Expected <" + s1 + "> but was <" + s2 + ">",
+                s1.indexOf(s2) != -1);
+        }
+    }
+
+    static String stripComments(byte[] bytes) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader
+            (new ByteArrayInputStream(bytes)));
+        StringBuffer sbuf = new StringBuffer();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            // skip comments
+            if (line.trim().startsWith("#"))
+                continue;
+
+            sbuf.append(line);
+            sbuf.append(LS);
+        }
+
+        return sbuf.toString();
+    }
+
+    public void testDuplicateProperties() throws IOException {
+        FormatPreservingProperties p = new FormatPreservingProperties();
+        try {
+            toProperties("foo=bar" + LS + "foo=baz", p);
+            fail("expected duplicate keys to cause exception");
+        } catch (DuplicateKeyException e) {
+            // expected
+        }
+
+        // now test the expected behavior when duplicates are allowed.
+        p = new FormatPreservingProperties();
+        p.setAllowDuplicates(true);
+        toProperties("foo=bar" + LS + "foo=baz", p);
+        assertProperties(new String[][]{ { "foo", "baz" } }, p);
+    }
+
+    public void testMultipleLoads() throws IOException {
+        String props = "foo=bar" + LS + "baz=quux";
+        String props2 = "a=b" + LS + "c=d";
+        Properties vanilla = new Properties();
+        vanilla.load(new BufferedInputStream
+            (new StringBufferInputStream(props)));
+        vanilla.load(new BufferedInputStream
+            (new StringBufferInputStream(props2)));
+
+        Properties p = new FormatPreservingProperties();
+        p.load(new BufferedInputStream(new StringBufferInputStream(props)));
+        p.load(new BufferedInputStream(new StringBufferInputStream(props2)));
+        assertPropertiesSame(vanilla, p);
+    }
+
+    protected FormatPreservingProperties toProperties(String s)
+        throws IOException {
+        return toProperties(s, new FormatPreservingProperties());
+    }
+
+    protected FormatPreservingProperties toProperties(String s,
+        FormatPreservingProperties p) throws IOException {
+        Properties vanilla = new Properties();
+        vanilla.load(new StringBufferInputStream(s));
+
+        p.load(new StringBufferInputStream(s));
+        assertRoundTrip(s, p);
+
+        assertPropertiesSame(vanilla, p);
+
+        return p;
+    }
+
+    private void assertRoundTrip(String s, Properties p) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        p.store(out, null);
+        assertEquals(s, out.toString());
+
+        // also check serializable
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        new ObjectOutputStream(bout).writeObject(p);
+
+        try {
+            FormatPreservingProperties deserialized =
+                (FormatPreservingProperties) new ObjectInputStream
+                    (new ByteArrayInputStream(bout.toByteArray())).
+                    readObject();
+            assertEquals(p, deserialized);
+
+            out = new ByteArrayOutputStream();
+            deserialized.store(out, null);
+            assertEquals(s, out.toString());
+        } catch (ClassNotFoundException cnfe) {
+            fail(cnfe + "");
+        }
+    }
+
+    public static void assertEquals(String expected, String actual) {
+        if (expected == actual)
+            return;
+
+        if (expected == null || !expected.equals(actual))
+            fail("Expected <" + expected + "> but was <" + actual + ">");
+    }
+
+    private void assertPropertiesSame(Properties vanilla, Properties p) {
+        assertEquals(vanilla, p);
+    }
+
+    protected void assertProperties(String[][] strings, Properties p) {
+        for (int i = 0; i < strings.length; i++)
+            assertEquals(strings[i][1], p.get(strings[i][0]));
+
+        assertEquals(strings.length, p.size());
+    }
+}