java.fedora.server.test.JournalBase64Test Maven / Gradle / Ivy
Show all versions of fcrepo-client Show documentation
/*
* -----------------------------------------------------------------------------
*
* License and Copyright: The contents of this file are subject to 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.fedora-commons.org/licenses.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The entire file consists of original code.
* Copyright © 2008 Fedora Commons, Inc.
*
Copyright © 2002-2007 The Rector and Visitors of the University of
* Virginia and Cornell University
* All rights reserved.
*
* -----------------------------------------------------------------------------
*/
package fedora.server.test;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import junit.framework.TestCase;
import fedora.server.journal.helpers.DecodingBase64OutputStream;
import fedora.server.journal.helpers.EncodingBase64InputStream;
/**
*
*
* Title: JournalBase64Test.java
*
*
* Description: Confirm that we can properly encode and decode any data
* stream that comes our way.
*
*
* @author [email protected]
* @version $Id: JournalBase64Test.java 5600 2007-01-20 03:04:32Z cwilper $
*/
public class JournalBase64Test extends TestCase {
private static final String WHITE_SPACE_CHARACTERS = " \t\n\r";
private static final String BASE64_ENCODING_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
public JournalBase64Test(String name) {
super(name);
}
/**
* Generate a temporary file with all possible bytes in it. Encode it,
* decode it, test for correctness.
*/
public void testOnArbitraryBytes() throws IOException {
// create the source file and fill it.
File source = createTempFile();
OutputStream os = new FileOutputStream(source);
for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
os.write(i);
}
os.close();
// process the file and test.
encodeAndDecodeWithAssertions(source);
}
/**
* Generate a temporary file with all sorts of Unicode characters in it,
* encoded to UTF-8. Encode it, decode it, test for correctness.
*/
public void testOnUTF8Characters() throws IOException {
// create the source file and fill it.
File source = createTempFile();
Writer writer = new OutputStreamWriter(new FileOutputStream(source),
"UTF-8");
for (int i = 0, charValue = 0; i < 256; i++) {
writer.write(charValue);
charValue += 131;
}
writer.close();
// process the file and test.
encodeAndDecodeWithAssertions(source);
}
/**
* Generate a temporary file with a troublesome section in it - this is text
* that is known to have caused problems with the previous encode/decode
* scheme. Encode it, decode it, test for correctness.
*/
public void testOnKnownProblemFile() throws IOException {
byte[] problemData = new byte[] { 0x73, 0x00, 0x6F, 0x00, 0x75, 0x00,
0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00,
0x61, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x20, 0x00, 0x6C, 0x00,
0x65, 0x00, 0x61, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x69, 0x00,
0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63, 0x00,
0x74, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x74, 0x00,
0x69, 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x74, 0x00,
0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x20, 0x00, 0x72, 0x00,
0x65, 0x00, 0x66, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x63, 0x00,
0x74, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00,
0x20, 0x00, 0x6D, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00,
0x75, 0x00, 0x6D, 0x00, (byte) 0xE2, 0x00, (byte) 0xAC, 0x20,
0x22, 0x21, 0x73, 0x00, 0x20, 0x00, 0x66, 0x00, 0x6F, 0x00,
0x75, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00,
0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x6F, 0x00,
0x66, 0x00, 0x20, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x61, 0x00,
0x79, 0x00, 0x66, 0x00, 0x75, 0x00, 0x6C, 0x00, 0x20, 0x00,
0x65, 0x00, 0x78, 0x00, 0x68, 0x00, 0x69, 0x00, 0x62, 0x00,
0x69, 0x00, 0x74, 0x00, 0x2D, 0x00, 0x62, 0x00, 0x61, 0x00,
0x73, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x69, 0x00,
0x6E, 0x00, 0x71, 0x00, 0x75, 0x00, 0x69, 0x00, 0x72, 0x00,
0x79, 0x00, 0x20, 0x00 };
// create the source file and fill it.
File source = createTempFile();
OutputStream os = new FileOutputStream(source);
os.write(problemData);
os.close();
// process the file and test.
encodeAndDecodeWithAssertions(source);
}
/**
* Create a temp file that will be deleted when the test is complete.
*/
private File createTempFile() throws IOException {
File file = File.createTempFile("test", null);
System.out.println(file.getPath());
file.deleteOnExit();
return file;
}
/**
* Encode the source file to a String, then decode back to a file. Check
* that the String is valid Base64, and that the target file has the same
* contents as the source file.
*/
private void encodeAndDecodeWithAssertions(File source)
throws FileNotFoundException, IOException {
// encode the file to a String and test that it is valid Base64
String encodedString = readClearFileCreateEncodedString(source);
assertStringContainsValidBase64(encodedString);
// read the encoded String and encode it to the target file.
File target = createTempFile();
writeClearFileFromEncodedString(target, encodedString);
// test the files.
assertFileContentsAreEqual(source, target);
}
/**
* Read a file of arbitrary bytes and produce a base-64 encoded String.
*/
private String readClearFileCreateEncodedString(File source)
throws IOException {
// use a small buffer size, not a multiple, so we will do multiple
// reads.
EncodingBase64InputStream encoder = new EncodingBase64InputStream(
new FileInputStream(source), 61);
StringBuffer encoded = new StringBuffer();
String chunk;
while (null != (chunk = encoder.read(35))) {
encoded.append(chunk);
}
encoder.close();
String encodedString = encoded.toString();
return encodedString;
}
/**
* Produce a file of arbitrary bytes from a base-64 encoded String. We need
* to process the String in chunks to simulate the Text events we will get
* from the XmlReader.
*/
private void writeClearFileFromEncodedString(File target,
String encodedString) throws FileNotFoundException, IOException {
int maxChunkSize = 61;
int length = encodedString.length();
DecodingBase64OutputStream decoder = new DecodingBase64OutputStream(
new FileOutputStream(target));
int start = 0;
int remainder;
while (0 < (remainder = length - start)) {
int chunkSize = Math.min(remainder, maxChunkSize);
String chunk = encodedString.substring(start, start + chunkSize);
decoder.write(chunk);
start += chunkSize;
}
decoder.close();
}
/**
* Base64 data should consist only of the 64 pemissible characters and the
* equals sign, in groups of 4 characters. Equals should appear only at the
* end, and no more than 2 are allowed.
*
* The spec allows any other characters to appear, and they should be
* ignored, but we'll be a little more strict and only allow white space
* characters.
*/
private void assertStringContainsValidBase64(String string) {
int howManyEqualsSigns = 0;
int howManyEncodingCharacters = 0;
for (int i = 0; i < string.length(); i++) {
char thisChar = string.charAt(i);
if (thisChar == '=') {
howManyEqualsSigns++;
} else if (BASE64_ENCODING_CHARACTERS.indexOf(thisChar) != -1) {
howManyEncodingCharacters++;
if (howManyEqualsSigns > 0) {
fail("Encoding character at " + i
+ " follows an equals sign.");
}
} else if (WHITE_SPACE_CHARACTERS.indexOf(thisChar) == -1) {
fail("Character '" + thisChar + "' at " + i
+ " is not an encoding character, "
+ "an equals sign, or white space.");
}
}
if (howManyEqualsSigns > 2) {
fail("Too many equals signs: " + howManyEqualsSigns);
}
int totalCharacters = howManyEqualsSigns + howManyEncodingCharacters;
if (0 != ((totalCharacters) % 4)) {
fail("Number of encoding characters plus equals signs "
+ "must be a multiple of 4, not " + totalCharacters);
}
}
/**
* Read the files byte by byte and compare.
*/
private void assertFileContentsAreEqual(File source, File target)
throws IOException {
InputStream sourceStream = new BufferedInputStream(new FileInputStream(
source));
InputStream targetStream = new BufferedInputStream(new FileInputStream(
target));
for (int i = 0; true; i++) {
int sourceByte = sourceStream.read();
int targetByte = targetStream.read();
if (sourceByte == -1) {
if (targetByte == -1) {
break;
} else {
fail("source file is shorter than target file.");
}
} else if (targetByte == -1) {
fail("target file is shorter than source file.");
} else if (sourceByte != targetByte) {
fail("files don't match at byte " + i + ": source="
+ sourceByte + ", target=" + targetByte);
}
}
}
}