blob: 5c05f3845e09c8277f49d73d42e5331ca2afa194 [file] [log] [blame]
/*
* 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.openide.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.netbeans.junit.NbTestCase;
public class EditablePropertiesTest extends NbTestCase {
public EditablePropertiesTest(String name) {
super(name);
}
public void testLoad() throws Exception {
Map<String,String> content = new HashMap<String,String>();
for (int i=1; i<=26; i++) {
content.put("key"+i, "value"+i);
}
content.put("@!#$%^keyA", "valueA!@#$%^&*(){}");
content.put(" =:keyB", "valueB =:");
content.put(""+(char)0x1234+"keyC", "valueC"+(char)0x9876);
content.put("keyD", "");
content.put("keyE", "");
content.put("keyF", "");
content.put("keyG", "");
content.put("keyH", "value#this is not comment");
content.put("keyI", "incorrect end: \\u123");
// #46234: does not handle bad Unicode escapes well
content.put("keyJ", "malformed Unicode escape: \\uabyz");
EditableProperties ep = loadTestProperties();
for (Map.Entry<String,String> entry : content.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
String epValue = ep.getProperty(key);
assertEquals("Expected value for key "+key+" is different", value, epValue);
}
int count = 0;
for (Map.Entry<String,String> entry : ep.entrySet()) {
if (entry.getKey() != null) {
count++;
}
}
assertEquals("Number of items in property file", content.keySet().size(), count);
}
/* Doesn't work; java.util.Properties throws IAE for malformed Unicode escapes:
public void testJavaUtilPropertiesEquivalence() throws Exception {
Properties p = loadTestJavaUtilProperties();
EditableProperties ep = loadTestProperties();
Iterator it = p.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
String val = (String) entry.getValue();
assertEquals("right value for " + key, val, ep.getProperty(key));
}
assertEquals("right number of items", p.size(), ep.size());
}
*/
public void testSave() throws Exception {
clearWorkDir();
EditableProperties ep = loadTestProperties();
String dest = getWorkDirPath()+File.separatorChar+"new.properties";
saveProperties(ep, dest);
assertFile("Saved properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
}
public void testClonability() throws Exception {
clearWorkDir();
EditableProperties ep = loadTestProperties();
EditableProperties ep2 = ep.cloneProperties();
String dest = getWorkDirPath()+File.separatorChar+"new2.properties";
saveProperties(ep2, dest);
assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
EditableProperties ep3 = (EditableProperties)ep.clone();
dest = getWorkDirPath()+File.separatorChar+"new3.properties";
saveProperties(ep3, dest);
assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
}
public void testCopyOnWriteClonability() throws Exception {
EditableProperties ep1 = new EditableProperties(true);
ep1.setProperty("k1", "v1");
EditableProperties ep2 = ep1.cloneProperties();
ep2.setProperty("k2", "v2");
EditableProperties ep3 = ep2.cloneProperties();
ep1.setProperty("k4", "v4");
ep2.setProperty("k2", "v2a");
Iterator<Map.Entry<String,String>> it = ep3.entrySet().iterator();
it.next().setValue("v1b");
it.next();
it.remove();
ep3.setProperty("k3", "v3");
assertEquals("{k1=v1, k4=v4}", ep1.toString());
assertEquals("{k1=v1, k2=v2a}", ep2.toString());
assertEquals("{k1=v1b, k3=v3}", ep3.toString());
ep1 = new EditableProperties(true);
ep1.setProperty("k", "v1");
ep2 = ep1.cloneProperties();
ep2.entrySet().iterator().next().setValue("v2");
assertEquals("{k=v1}", ep1.toString());
assertEquals("{k=v2}", ep2.toString());
}
// test that array values are stored correctly
public void testArrayValues() throws Exception {
EditableProperties ep = new EditableProperties(false);
ep.setProperty("key1", new String[]{"1. line;", "2. line;", "3. line"});
ep.setProperty("key2", "1. line;2. line;3. line");
String output = getAsString(ep);
String expected =
"key1=\\"+System.getProperty("line.separator")+
" 1. line;\\"+System.getProperty("line.separator")+
" 2. line;\\"+System.getProperty("line.separator")+
" 3. line"+System.getProperty("line.separator")+
"key2=1. line;2. line;3. line"+System.getProperty("line.separator");
assertEquals(expected, output);
assertEquals(ep.getProperty("key1"), "1. line;2. line;3. line");
assertEquals(ep.getProperty("key2"), "1. line;2. line;3. line");
ep.setProperty("key1", "one; two; three");
output = getAsString(ep);
expected =
"key1=one; two; three"+System.getProperty("line.separator")+
"key2=1. line;2. line;3. line"+System.getProperty("line.separator");
assertEquals(expected, output);
assertEquals(ep.getProperty("key1"), "one; two; three");
assertEquals(ep.getProperty("key2"), "1. line;2. line;3. line");
ep.setProperty("key2", new String[]{"1. line;", "2. line;", "3. line", "one;", "more;", "line;"});
ep.setProperty("key", new String[0]);
output = getAsString(ep);
expected =
"key1=one; two; three"+System.getProperty("line.separator")+
"key2=\\"+System.getProperty("line.separator")+
" 1. line;\\"+System.getProperty("line.separator")+
" 2. line;\\"+System.getProperty("line.separator")+
" 3. line\\"+System.getProperty("line.separator")+
" one;\\"+System.getProperty("line.separator")+
" more;\\"+System.getProperty("line.separator")+
" line;"+System.getProperty("line.separator")+
"key="+System.getProperty("line.separator"); // #45061
assertEquals(expected, output);
assertEquals(ep.getProperty("key1"), "one; two; three");
assertEquals(ep.getProperty("key2"), "1. line;2. line;3. lineone;more;line;");
assertEquals(ep.getProperty("key"), "");
}
public void testSorting() throws Exception {
EditableProperties ep = new EditableProperties(false);
ep.setProperty("a", "val-a");
ep.setProperty("c", "val-c");
ep.put("b", "val-b");
String output = getAsString(ep);
String expected = "a=val-a"+System.getProperty("line.separator")+"c=val-c"+
System.getProperty("line.separator")+"b=val-b"+
System.getProperty("line.separator");
assertEquals(expected, output);
ep = new EditableProperties(false);
ep.setProperty("a", "val-a");
ep.setProperty("c", "val-c");
ep.put("b", "val-b");
output = getAsString(ep);
expected = "a=val-a"+System.getProperty("line.separator")+"c=val-c"+
System.getProperty("line.separator")+"b=val-b"+
System.getProperty("line.separator");
assertEquals(expected, output);
ep = new EditableProperties(true);
ep.setProperty("a", "val-a");
ep.setProperty("c", "val-c");
ep.put("b", "val-b");
output = getAsString(ep);
expected = "a=val-a"+System.getProperty("line.separator")+"b=val-b"+
System.getProperty("line.separator")+"c=val-c"+
System.getProperty("line.separator");
assertEquals(expected, output);
}
// test that changing comments work and modify only comments
// test that misc chars are correctly escaped, unicode encoded, etc.
public void testEscaping() throws Exception {
String umlaut = "" + (char)252;
EditableProperties ep = new EditableProperties(false);
ep.setProperty("a a", "a space a");
ep.setProperty("b"+(char)0x4567, "val"+(char)0x1234);
ep.setProperty("@!#$%^\\", "!@#$%^&*(){}\\");
ep.setProperty("d\nd", "d\nnewline\nd");
ep.setProperty("umlaut", umlaut);
ep.setProperty("_a a", new String[]{"a space a"});
ep.setProperty("_b"+(char)0x4567, new String[]{"val"+(char)0x1234});
ep.setProperty("_@!#$%^\\", new String[]{"!@#$%^&*\\", "(){}\\"});
ep.setProperty("_d\nd", new String[]{"d\nnew","line\nd", "\n", "end"});
ep.setProperty("_umlaut", new String[]{umlaut, umlaut});
String output = getAsString(ep);
String expected = "a\\ a=a space a"+System.getProperty("line.separator")+
"b\\u4567=val\\u1234"+System.getProperty("line.separator")+
"@!#$%^\\\\=!@#$%^&*(){}\\\\"+System.getProperty("line.separator")+
"d\\nd=d\\nnewline\\nd"+System.getProperty("line.separator")+
"umlaut=\\u00fc"+System.getProperty("line.separator")+
"_a\\ a=\\"+System.getProperty("line.separator")+" a space a"+System.getProperty("line.separator")+
"_b\\u4567=\\"+System.getProperty("line.separator")+" val\\u1234"+System.getProperty("line.separator")+
"_@!#$%^\\\\=\\"+System.getProperty("line.separator")+" !@#$%^&*\\\\\\"+System.getProperty("line.separator")+
" (){}\\\\"+System.getProperty("line.separator")+
"_d\\nd=\\"+System.getProperty("line.separator")+" d\\nnew\\"+System.getProperty("line.separator")+
" line\\nd\\"+System.getProperty("line.separator")+
" \\n\\"+System.getProperty("line.separator")+
" end"+System.getProperty("line.separator")+
"_umlaut=\\" +System.getProperty("line.separator")+" \\u00fc\\"+System.getProperty("line.separator")+
" \\u00fc"+System.getProperty("line.separator");
assertEquals(expected, output);
assertEquals("a space a", ep.getProperty("a a"));
assertEquals("val"+(char)0x1234, ep.getProperty("b"+(char)0x4567));
assertEquals("!@#$%^&*(){}\\", ep.getProperty("@!#$%^\\"));
assertEquals("d\nnewline\nd", ep.getProperty("d\nd"));
assertEquals(umlaut, ep.getProperty("umlaut"));
assertEquals("a space a", ep.getProperty("_a a"));
assertEquals("val"+(char)0x1234, ep.getProperty("_b"+(char)0x4567));
assertEquals("!@#$%^&*\\(){}\\", ep.getProperty("_@!#$%^\\"));
assertEquals("d\nnewline\nd\nend", ep.getProperty("_d\nd"));
assertEquals(umlaut+umlaut, ep.getProperty("_umlaut"));
}
public void testMetaCharacters() throws Exception {
testRoundTrip("foo=bar", "v");
testRoundTrip("foo:bar", "v");
testRoundTrip("#foobar", "v");
testRoundTrip("foo#bar", "v");
testRoundTrip("foobar#", "v");
testRoundTrip("foobar", "#v");
testRoundTrip("foobar", "v#");
testRoundTrip(" #foo", " #bar");
testRoundTrip(" foo bar ", "v");
testRoundTrip("foobar", " v ");
testRoundTrip("= : # \\\n", "= : # \\\n");
}
private void testRoundTrip(String key, String value) throws Exception {
EditableProperties ep = new EditableProperties(false);
ep.setProperty(key, value);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ep.store(baos);
ep = new EditableProperties(false);
ep.load(new ByteArrayInputStream(baos.toByteArray()));
assertEquals(baos.toString(), Collections.singletonMap(key, value), ep);
Properties p = new Properties();
p.load(new ByteArrayInputStream(baos.toByteArray()));
assertEquals(baos.toString(), Collections.singletonMap(key, value), p);
}
public void testPreservationOfFormatting() throws Exception {
String text = "k1=\\\n"
+ "A long line. \\\n"
+ " Broken into pieces.\n"
+ "k3=whatever\n"
+ "k5=Label\\:\n";
EditableProperties p = new EditableProperties(true);
p.load(new ByteArrayInputStream(text.getBytes(StandardCharsets.ISO_8859_1)));
assertEquals("A long line. Broken into pieces.", p.get("k1"));
assertEquals("whatever", p.get("k3"));
assertEquals("Label:", p.get("k5"));
p.put("k2", "more");
p.put("k4", "more");
assertEquals(text.replace("k3=whatever", "k2=more\nk3=whatever\nk4=more").replace("\n", System.getProperty("line.separator")), getAsString(p));
}
// test that iterator implementation is OK
public void testIterator() throws Exception {
EditableProperties ep = loadTestProperties();
Iterator<Map.Entry<String,String>> it1 = ep.entrySet().iterator();
while (it1.hasNext()) {
it1.next();
}
Iterator<String> it2 = ep.keySet().iterator();
while (it2.hasNext()) {
it2.next();
}
it2 = ep.keySet().iterator();
while (it2.hasNext()) {
it2.next();
it2.remove();
}
ep.put("a", "aval");
ep.remove("a");
ep = loadTestProperties();
it1 = ep.entrySet().iterator();
while (it1.hasNext()) {
Map.Entry<String,String> entry = it1.next();
assertNotNull("Property key cannot be null", entry.getKey());
assertNotNull("Property value cannot be null", entry.getValue());
entry.setValue(entry.getValue()+"-something-new");
}
it1 = ep.entrySet().iterator();
while (it1.hasNext()) {
it1.next();
it1.remove();
}
}
// test that syntax errors are survived
public void testInvalidPropertiesFile() throws Exception {
String invalidProperty = "key=value without correct end\\";
ByteArrayInputStream is = new ByteArrayInputStream(invalidProperty.getBytes());
EditableProperties ep = new EditableProperties(false);
ep.load(is);
assertEquals("Syntax error should be resolved", 1, ep.keySet().size());
assertEquals("value without correct end", ep.getProperty("key"));
}
public void testNonLatinComments() throws Exception {
// #60249.
String lsep = System.getProperty("line.separator");
EditableProperties p = new EditableProperties(false);
p.setProperty("k", "v");
p.setComment("k", new String[] {"# \u0158ekni koment teda!"}, false);
String expected = "# \\u0158ekni koment teda!" + lsep + "k=v" + lsep;
assertEquals("Storing non-Latin chars in comments works", expected, getAsString(p));
p = new EditableProperties(false);
p.load(new ByteArrayInputStream(expected.getBytes(StandardCharsets.ISO_8859_1)));
assertEquals("Reading non-Latin chars in comments works", Collections.singletonList("# \u0158ekni koment teda!"), Arrays.asList(p.getComment("k")));
p.setProperty("k", "v2");
expected = "# \\u0158ekni koment teda!" + lsep + "k=v2" + lsep;
assertEquals("Reading and re-writing non-Latin chars in comments works", expected, getAsString(p));
}
// Test consistency of size of the property list for different view of it
public void testConsistentSize() throws Exception {
EditableProperties testProperties = loadTestProperties();
// Enumerate entries manually
int count = 0;
for(Entry<String,String> entry: testProperties.entrySet()) {
count++;
}
assertEquals(testProperties.size(), count);
assertEquals(testProperties.size(), testProperties.entrySet().size());
assertEquals(testProperties.size(), testProperties.keySet().size());
}
// This test ensures, that the copy constructor or EditableProperties.State
// does not introduce NULL keys. These NULL keys are observable from the
// outside by an increased number of elements.
public void testCopyWithIndependentComments() throws Exception {
EditableProperties testProperties = loadTestProperties();
EditableProperties test2Properties = testProperties.cloneProperties();
int originalSize = testProperties.size();
assertTrue(testProperties.containsKey("key1"));
testProperties.setProperty("key1", "XXX");
assertEquals(originalSize, testProperties.size());
}
// helper methods:
private String filenameOfTestProperties() {
// #50987: never use URL.path for this purpose...
return BaseUtilities.toFile(URI.create(EditablePropertiesTest.class.getResource("data/test.properties").toExternalForm())).getAbsolutePath();
}
private EditableProperties loadTestProperties() throws IOException {
URL u = EditablePropertiesTest.class.getResource("data/test.properties");
EditableProperties ep = new EditableProperties(false);
InputStream is = u.openStream();
try {
ep.load(is);
} finally {
is.close();
}
return ep;
}
/*
private Properties loadTestJavaUtilProperties() throws IOException {
URL u = EditablePropertiesTest.class.getResource("data/test.properties");
Properties p = new Properties();
InputStream is = u.openStream();
try {
p.load(is);
} finally {
is.close();
}
return p;
}
*/
private void saveProperties(EditableProperties ep, String path) throws Exception {
OutputStream os = new FileOutputStream(path);
try {
ep.store(os);
} finally {
os.close();
}
}
private String getAsString(EditableProperties ep) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ep.store(os);
os.close();
return os.toString("ISO-8859-1");
}
}