// Copyright 2011 Google Inc.
//
// 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 com.google.enterprise.connector.common;
import com.google.enterprise.connector.test.ConnectorTestUtils;
import junit.framework.TestCase;
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.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
public class PropertiesUtilsTest extends TestCase {
private static final Logger logger =
Logger.getLogger(PropertiesUtilsTest.class.getName());
private static final String TEST_DIR_NAME =
"testdata/tmp/PropertiesUtilsTests";
private static final String NAME = "test.properties";
private final File baseDirectory = new File(TEST_DIR_NAME);
private static final String PROP1 = "prop1";
private static final String PROP1_VALUE = "value1";
private static final String PROP2 = "prop2";
private static final String PROP2_VALUE = "value2";
private static final String COMMENT = "Comment";
private static final String MULTILINE_COMMENT = COMMENT + "\n More Comment";
private static final String PROPERTIES =
"# " + COMMENT + "\n"
+ PROP1 + " = " + PROP1_VALUE + "\n"
+ PROP2 + " = " + PROP2_VALUE + "\n";
private Properties expected;
@Override
protected void setUp() throws Exception {
super.setUp();
ConnectorTestUtils.deleteAllFiles(baseDirectory);
assertTrue(ConnectorTestUtils.mkdirs(baseDirectory));
expected = new Properties();
expected.setProperty(PROP1, PROP1_VALUE);
expected.setProperty(PROP2, PROP2_VALUE);
}
@Override
protected void tearDown() throws Exception {
try {
ConnectorTestUtils.deleteAllFiles(baseDirectory);
} finally {
super.tearDown();
}
}
/** Test load from null string. */
public void testLoadFromNullString() throws Exception {
assertNull(PropertiesUtils.loadFromString(null));
}
/** Test load from empty string. */
public void testLoadFromEmptyString() throws Exception {
Properties props = PropertiesUtils.loadFromString("");
assertNotNull(props);
assertTrue(props.isEmpty());
}
/** Test load from string. */
public void testLoadFromString() throws Exception {
Properties props = PropertiesUtils.loadFromString(PROPERTIES);
compareProperties(expected, props);
}
/** Test store to string. */
public void testStoreToString() throws Exception {
String output = PropertiesUtils.storeToString(expected, COMMENT);
assertTrue(output.startsWith("#" + COMMENT));
assertTrue(output.contains(PROP1 + "=" + PROP1_VALUE));
assertTrue(output.contains(PROP2 + "=" + PROP2_VALUE));
Properties props = PropertiesUtils.loadFromString(output);
compareProperties(expected, props);
}
/** Test load from null InputStream. */
public void testLoadFromNullStream() throws Exception {
assertNull(PropertiesUtils.loadProperties(null));
}
/** Test load from InputStream. */
public void testLoadFromStream() throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(
PROPERTIES.getBytes(PropertiesUtils.PROPERTIES_ENCODING));
Properties props = PropertiesUtils.loadProperties(bais);
compareProperties(expected, props);
}
/** Test load from Bad InputStream. */
public void testLoadFromBadStream() throws Exception {
Properties props;
try {
BadInputStream is = new BadInputStream();
props = PropertiesUtils.loadProperties(is);
fail("Expected PropertiesException");
} catch (PropertiesException expected) {
// Expected.
}
}
/** Test store null properties to OutputStream. */
public void testNullStoreToStream() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
assertEquals(0, baos.size());
PropertiesUtils.storeProperties(null, baos, COMMENT);
assertEquals(0, baos.size());
}
/** Test store empty properties to OutputStream. */
public void testEmptyStoreToStream() throws Exception {
Properties emptyProps = new Properties();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
assertEquals(0, baos.size());
PropertiesUtils.storeProperties(emptyProps, baos, MULTILINE_COMMENT);
assertTrue(baos.size() > 0);
String output = baos.toString(PropertiesUtils.PROPERTIES_ENCODING);
// Make sure multi-line comments get "commented out".
assertTrue(output.startsWith("#" + COMMENT));
assertTrue(output.contains("# More Comment"));
compareProperties(emptyProps, PropertiesUtils.loadFromString(output));
}
/** Test store properties to OutputStream. */
public void testStoreToStream() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
assertEquals(0, baos.size());
PropertiesUtils.storeProperties(expected, baos, COMMENT);
assertTrue(baos.size() > 0);
String output = baos.toString(PropertiesUtils.PROPERTIES_ENCODING);
assertTrue(output.startsWith("#" + COMMENT));
assertTrue(output.contains(PROP1 + "=" + PROP1_VALUE));
assertTrue(output.contains(PROP2 + "=" + PROP2_VALUE));
compareProperties(expected, PropertiesUtils.loadFromString(output));
}
/** Test store to Bad OutputStream. */
public void testStoreToBadStream() throws Exception {
try {
BadOutputStream os = new BadOutputStream();
PropertiesUtils.storeProperties(new Properties(), os, COMMENT);
fail("Expected PropertiesException");
} catch (PropertiesException expected) {
// Expected.
}
}
/** Test load from non-existent file. */
public void testLoadFromFileNoExist() throws Exception {
try {
Properties props = PropertiesUtils.loadFromFile(
new File(baseDirectory, "nonExistent.properties"));
fail("Expected PropertiesException");
} catch (PropertiesException expected) {
// Expected.
}
}
/** Test load from file. */
public void testLoadFromFile() throws Exception {
File propFile = new File(baseDirectory, NAME);
assertFalse(propFile.exists());
FileOutputStream fos = new FileOutputStream(propFile);
fos.write(PROPERTIES.getBytes(PropertiesUtils.PROPERTIES_ENCODING));
fos.close();
assertTrue(propFile.exists());
Properties props = PropertiesUtils.loadFromFile(propFile);
compareProperties(expected, props);
}
/** Test store to non-existent directory. */
public void testStoreToDirNoExist() throws Exception {
File propFile = new File(new File(baseDirectory, "nonExistentDir"), NAME);
assertFalse(propFile.exists());
try {
PropertiesUtils.storeToFile(expected, propFile, COMMENT);
fail("Expected PropertiesException");
} catch (PropertiesException expected) {
// Expected.
}
}
/** Test store to file. */
public void testStoreToFile() throws Exception {
File propFile = new File(baseDirectory, NAME);
assertFalse(propFile.exists());
PropertiesUtils.storeToFile(expected, propFile, null);
assertTrue(propFile.exists());
Properties props = PropertiesUtils.loadFromFile(propFile);
compareProperties(expected, props);
}
/** Test copy Properties. */
public void testCopy() throws Exception {
Properties emptyProps = new Properties();
compareProperties(emptyProps, PropertiesUtils.copy(null));
compareProperties(emptyProps, PropertiesUtils.copy(emptyProps));
Properties copyProps = PropertiesUtils.copy(expected);
compareProperties(expected, copyProps);
// Make sure it is a deep copy.
copyProps.setProperty(PROP1, "foo");
assertEquals("foo", copyProps.getProperty(PROP1));
assertEquals(PROP1_VALUE, expected.getProperty(PROP1));
}
/** Test toMap and fromMap. */
public void testToFromMap() throws Exception {
assertNull(PropertiesUtils.toMap(null));
assertNull(PropertiesUtils.fromMap(null));
Map<String, String> propMap = PropertiesUtils.toMap(expected);
assertEquals(PROP1_VALUE, propMap.get(PROP1));
assertEquals(PROP2_VALUE, propMap.get(PROP2));
compareProperties(expected, PropertiesUtils.fromMap(propMap));
}
/** Test Properties Version. */
public void testVersion() throws Exception {
Properties props = PropertiesUtils.copy(expected);
// No explicit version.
assertEquals(0, PropertiesUtils.getPropertiesVersion(props));
// Test real version.
PropertiesUtils.stampPropertiesVersion(props);
assertEquals(PropertiesUtils.GOOGLE_PROPERTIES_VERSION_NUMBER,
PropertiesUtils.getPropertiesVersion(props));
// Test newer version.
props.put(PropertiesUtils.GOOGLE_PROPERTIES_VERSION,
Integer.toString(PropertiesUtils.GOOGLE_PROPERTIES_VERSION_NUMBER
+ 100000));
assertEquals(PropertiesUtils.GOOGLE_PROPERTIES_VERSION_NUMBER + 100000,
PropertiesUtils.getPropertiesVersion(props));
// Test invalid version.
props.put(PropertiesUtils.GOOGLE_PROPERTIES_VERSION, "bad_version");
assertEquals(0, PropertiesUtils.getPropertiesVersion(props));
}
/** Test encrypt/decrypt sensitive properties. */
public void testSensitiveProperties() throws Exception {
Properties props = PropertiesUtils.copy(expected);
props.setProperty("password", "foo");
props.setProperty("otherPassword", "bar");
PropertiesUtils.encryptSensitiveProperties(props);
// Password properties should be encrypted.
assertFalse("foo".equals(props.getProperty("password")));
assertFalse("bar".equals(props.getProperty("otherPassword")));
// Non-Password properties should be unmodified.
assertEquals(PROP1_VALUE, props.getProperty(PROP1));
assertEquals(PROP2_VALUE, props.getProperty(PROP2));
PropertiesUtils.decryptSensitiveProperties(props);
// Password properties should be restored.
assertEquals("foo", props.getProperty("password"));
assertEquals("bar", props.getProperty("otherPassword"));
// Non-Password properties should be unmodified.
assertEquals(PROP1_VALUE, props.getProperty(PROP1));
assertEquals(PROP2_VALUE, props.getProperty(PROP2));
}
/** Test encrypt/decrypt sensitive properties during load/store. */
public void testLoadStoreSensitiveProperties() throws Exception {
Properties props = PropertiesUtils.copy(expected);
props.setProperty("password", "foo");
props.setProperty("otherPassword", "bar");
String output = PropertiesUtils.storeToString(props, COMMENT);
// Password properties should be encrypted.
assertFalse(output.contains("foo"));
assertFalse(output.contains("bar"));
// Passwords should be restored when read back in.
compareProperties(props, PropertiesUtils.loadFromString(output));
}
/** Compare the supplied propertied against expected properties. */
private void compareProperties(Properties expected, Properties props)
throws Exception {
assertNotNull(props);
ConnectorTestUtils.compareMaps(expected, props);
}
/** An InputStream that throws IOException. */
private class BadInputStream extends InputStream {
public int read() throws IOException {
throw new IOException("test");
}
}
/** An OutputStream that throws IOException. */
private class BadOutputStream extends OutputStream {
public void write(int b) throws IOException {
throw new IOException("test");
}
}
}