001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.util;
020:
021: import org.apache.batik.test.AbstractTest;
022: import org.apache.batik.test.DefaultTestReport;
023: import org.apache.batik.test.TestReport;
024:
025: import java.io.PipedOutputStream;
026: import java.io.PipedInputStream;
027: import java.io.InputStream;
028: import java.io.OutputStream;
029: import java.io.IOException;
030: import java.io.StringWriter;
031: import java.io.PrintWriter;
032:
033: import java.net.URL;
034:
035: /**
036: * This test validates that the Base65 encoder/decoders work properly.
037: *
038: * @author <a href="mailto:deweese@apache.org">Thomas DeWeese</a>
039: * @version $Id: Base64Test.java 504106 2007-02-06 12:31:06Z dvholten $
040: */
041: public class Base64Test extends AbstractTest {
042: /**
043: * Error when bad action string given.
044: * {0} = Bad action string
045: */
046: public static final String ERROR_BAD_ACTION_STRING = "Base64Test.error.bad.action.string";
047:
048: /**
049: * Error when unable to read/open in URL
050: * {0} = URL
051: * {1} = exception stack trace.
052: */
053: public static final String ERROR_CANNOT_READ_IN_URL = "Base64Test.error.cannot.read.in.url";
054:
055: /**
056: * Error when unable to read/open ref URL
057: * {0} = URL
058: * {1} = exception stack trace.
059: */
060: public static final String ERROR_CANNOT_READ_REF_URL = "Base64Test.error.cannot.read.ref.url";
061:
062: /**
063: * Result didn't match reference result.
064: * {0} = first byte of mismatch
065: */
066: public static final String ERROR_WRONG_RESULT = "Base64Test.error.wrong.result";
067:
068: public static final String ENTRY_KEY_ERROR_DESCRIPTION = "Base64Test.entry.key.error.description";
069:
070: protected String action = null;
071: protected URL in = null;
072: protected URL ref = null;
073:
074: /**
075: * Constructor. ref is ignored if action == ROUND.
076: * @param action The action to perform, one of:
077: * ROUND : base64 encode then base64 decode.
078: * ENCODE : encode in to base 64 and compare result to ref.
079: * DECODE : decode in (must be base 64) and compare to ref.
080: * @param in The source file to apply 'action' to.
081: * @param ref The reference file.
082: */
083: public Base64Test(String action, URL in, URL ref) {
084: this .action = action;
085: this .in = in;
086: this .ref = ref;
087: }
088:
089: /**
090: * Constructor, for round trip testing (only one file required).
091: * @param in The source file to round trip.
092: */
093: public Base64Test(URL in) {
094: this .action = "ROUND";
095: this .in = in;
096: }
097:
098: /**
099: * Returns this Test's name
100: */
101: public String getName() {
102: return action + " -- " + in + " -- " + super .getName();
103: }
104:
105: /**
106: * This method will only throw exceptions if some aspect
107: * of the test's internal operation fails.
108: */
109: public TestReport runImpl() throws Exception {
110: DefaultTestReport report = new DefaultTestReport(this );
111:
112: InputStream inIS;
113:
114: try {
115: inIS = in.openStream();
116: } catch (Exception e) {
117: StringWriter trace = new StringWriter();
118: e.printStackTrace(new PrintWriter(trace));
119: report.setErrorCode(ERROR_CANNOT_READ_IN_URL);
120: report
121: .setDescription(new TestReport.Entry[] { new TestReport.Entry(
122: TestMessages.formatMessage(
123: ENTRY_KEY_ERROR_DESCRIPTION, null),
124: TestMessages.formatMessage(
125: ERROR_CANNOT_READ_IN_URL,
126: new String[] { in.toString(),
127: trace.toString() })) });
128: report.setPassed(false);
129: return report;
130: }
131:
132: if (action.equals("ROUND"))
133: this .ref = in;
134: else if (!action.equals("ENCODE") && !action.equals("DECODE")) {
135: report.setErrorCode(ERROR_BAD_ACTION_STRING);
136: report
137: .setDescription(new TestReport.Entry[] { new TestReport.Entry(
138: TestMessages.formatMessage(
139: ENTRY_KEY_ERROR_DESCRIPTION, null),
140: TestMessages.formatMessage(
141: ERROR_BAD_ACTION_STRING,
142: new String[] { action })) });
143: report.setPassed(false);
144: return report;
145: }
146:
147: InputStream refIS;
148: try {
149: refIS = ref.openStream();
150: } catch (Exception e) {
151: StringWriter trace = new StringWriter();
152: e.printStackTrace(new PrintWriter(trace));
153: report.setErrorCode(ERROR_CANNOT_READ_REF_URL);
154: report
155: .setDescription(new TestReport.Entry[] { new TestReport.Entry(
156: TestMessages.formatMessage(
157: ENTRY_KEY_ERROR_DESCRIPTION, null),
158: TestMessages.formatMessage(
159: ERROR_CANNOT_READ_REF_URL,
160: new String[] { ref.toString(),
161: trace.toString() })) });
162: report.setPassed(false);
163: return report;
164: }
165:
166: if (action.equals("ENCODE") || action.equals("ROUND")) {
167: // We need to encode the incomming data
168: PipedOutputStream pos = new PipedOutputStream();
169: OutputStream os = new Base64EncoderStream(pos);
170:
171: // Copy the input to the Base64 Encoder (in a seperate thread).
172: Thread t = new StreamCopier(inIS, os);
173:
174: // Read that from the piped output stream.
175: inIS = new PipedInputStream(pos);
176: t.start();
177: }
178:
179: if (action.equals("DECODE") || action.equals("ROUND")) {
180: inIS = new Base64DecodeStream(inIS);
181: }
182:
183: int mismatch = compareStreams(inIS, refIS, action
184: .equals("ENCODE"));
185:
186: if (mismatch == -1) {
187: report.setPassed(true);
188: return report;
189: }
190:
191: report.setErrorCode(ERROR_WRONG_RESULT);
192: report
193: .setDescription(new TestReport.Entry[] { new TestReport.Entry(
194: TestMessages.formatMessage(
195: ENTRY_KEY_ERROR_DESCRIPTION, null),
196: TestMessages
197: .formatMessage(ERROR_WRONG_RESULT,
198: new String[] { String
199: .valueOf(mismatch) })) });
200: report.setPassed(false);
201: return report;
202: }
203:
204: /**
205: * Returns true if the contents of <tt>is1</tt> match the
206: * contents of <tt>is2</tt>
207: */
208: public static int compareStreams(InputStream is1, InputStream is2,
209: boolean skipws) {
210: byte[] data1 = new byte[100];
211: byte[] data2 = new byte[100];
212: int off1 = 0;
213: int off2 = 0;
214: int idx = 0;
215:
216: try {
217: while (true) {
218: int len1 = is1.read(data1, off1, data1.length - off1);
219: int len2 = is2.read(data2, off2, data2.length - off2);
220:
221: if (off1 != 0) {
222: if (len1 == -1)
223: len1 = off1;
224: else
225: len1 += off1;
226: }
227:
228: if (off2 != 0) {
229: if (len2 == -1)
230: len2 = off2;
231: else
232: len2 += off2;
233: }
234:
235: if (len1 == -1) {
236: if (len2 == -1)
237: break; // Both done...
238:
239: // Only is1 is done...
240: if (!skipws)
241: return idx;
242:
243: // check if the rest of is2 is whitespace...
244: for (int i2 = 0; i2 < len2; i2++)
245: if ((data2[i2] != '\n') && (data2[i2] != '\r')
246: && (data2[i2] != ' '))
247: return idx + i2;
248: off1 = off2 = 0;
249: continue;
250: }
251:
252: if (len2 == -1) {
253: // Only is2 is done...
254: if (!skipws)
255: return idx;
256:
257: // Check if rest of is1 is whitespace...
258: for (int i1 = 0; i1 < len1; i1++)
259: if ((data1[i1] != '\n') && (data1[i1] != '\r')
260: && (data1[i1] != ' '))
261: return idx + i1;
262: off1 = off2 = 0;
263: continue;
264: }
265:
266: int i1 = 0;
267: int i2 = 0;
268: while ((i1 < len1) && (i2 < len2)) {
269: if (skipws) {
270: if ((data1[i1] == '\n') || (data1[i1] == '\r')
271: || (data1[i1] == ' ')) {
272: i1++;
273: continue;
274: }
275: if ((data2[i2] == '\n') || (data2[i2] == '\r')
276: || (data2[i2] == ' ')) {
277: i2++;
278: continue;
279: }
280: }
281: if (data1[i1] != data2[i2])
282: return idx + i2;
283:
284: i1++;
285: i2++;
286: }
287:
288: if (i1 != len1)
289: System.arraycopy(data1, i1, data1, 0, len1 - i1);
290: if (i2 != len2)
291: System.arraycopy(data2, i2, data2, 0, len2 - i2);
292: off1 = len1 - i1;
293: off2 = len2 - i2;
294: idx += i2;
295: }
296: } catch (IOException ioe) {
297: ioe.printStackTrace();
298: return idx;
299: }
300:
301: return -1;
302: }
303:
304: static class StreamCopier extends Thread {
305: InputStream src;
306: OutputStream dst;
307:
308: public StreamCopier(InputStream src, OutputStream dst) {
309: this .src = src;
310: this .dst = dst;
311: }
312:
313: public void run() {
314: try {
315: byte[] data = new byte[1000];
316: while (true) {
317: int len = src.read(data, 0, data.length);
318: if (len == -1)
319: break;
320:
321: dst.write(data, 0, len);
322: }
323: } catch (IOException ioe) {
324: // Nothing
325: }
326: try {
327: dst.close();
328: } catch (IOException ioe) {
329: // Nothing
330: }
331: }
332: }
333: }
|