InternetAddressTest.java
/*
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.mail.internet;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Test Internet address parsing.
*
* @author Bill Shannon
*/
@RunWith(Parameterized.class)
public class InternetAddressTest {
private String headerName;
private String headerValue;
private String[] expected;
private boolean doStrict;
private boolean doParseHeader;
static boolean strict = false; // enforce strict RFC822 syntax
static boolean gen_test_input = false; // output good for input to -p
static boolean parse_mail = false; // parse input in mail format
static boolean parse_header = false; // use parseHeader method?
static boolean verbose; // print progress?
static int errors = 0; // number of errors detected
static boolean junit;
static List<Object[]> testData;
public InternetAddressTest(String headerName, String headerValue,
String[] expected, boolean doStrict, boolean doParseHeader) {
this.headerName = headerName;
this.headerValue = headerValue;
this.expected = expected;
this.doStrict = doStrict;
this.doParseHeader = doParseHeader;
}
@Parameters
public static Collection<Object[]> data() throws IOException {
junit = true;
testData = new ArrayList<>();
parse(new BufferedReader(new InputStreamReader(
InternetAddressTest.class.getResourceAsStream("addrlist"))));
return testData;
}
public static void main(String[] argv) throws Exception {
verbose = true; // default for standalone
int optind;
for (optind = 0; optind < argv.length; optind++) {
if (argv[optind].equals("-")) {
// ignore
} else if (argv[optind].equals("-g")) {
gen_test_input = true;
} else if (argv[optind].equals("-h")) {
parse_header = true;
} else if (argv[optind].equals("-p")) {
parse_mail = true;
} else if (argv[optind].equals("-s")) {
strict = true;
} else if (argv[optind].equals("-q")) {
verbose = false;
} else if (argv[optind].equals("--")) {
optind++;
break;
} else if (argv[optind].startsWith("-")) {
System.out.println(
"Usage: addrtest [-g] [-h] [-p] [-s] [-q] [-] [address ...]");
System.exit(1);
} else {
break;
}
}
/*
* If there's any args left on the command line,
* concatenate them into a string and test that.
*/
if (optind < argv.length) {
StringBuffer sb = new StringBuffer();
for (int i = optind; i < argv.length; i++) {
sb.append(argv[i]);
sb.append(" ");
}
test("To", sb.toString(), null, strict, parse_header);
} else {
// read from stdin
BufferedReader in =
new BufferedReader(new InputStreamReader(System.in));
String s;
if (parse_mail)
parse(in);
else {
while ((s = in.readLine()) != null)
test("To", s, null, strict, parse_header);
}
}
System.exit(errors);
}
/*
* Parse the input in "mail" format, extracting the From, To, and Cc
* headers and testing them. The parse is rather crude, but sufficient
* to test against most existing UNIX mailboxes.
*/
public static void parse(BufferedReader in) throws IOException {
String header = "";
boolean doStrict = strict;
boolean doParseHeader = parse_header;
for (; ; ) {
String s = in.readLine();
if (s != null && s.length() > 0) {
char c = s.charAt(0);
if (c == ' ' || c == '\t') {
// a continuation line, add it to the current header
header += '\n' + s;
continue;
}
}
// "s" is the next header, "header" is the last complete header
if (header.startsWith("Strict: ")) {
doStrict = Boolean.parseBoolean(value(header));
} else if (header.startsWith("Header: ")) {
doParseHeader = Boolean.parseBoolean(value(header));
} else if (header.startsWith("From: ") ||
header.startsWith("To: ") ||
header.startsWith("Cc: ")) {
int i;
String[] expect = null;
if (s != null && s.startsWith("Expect: ")) {
try {
int nexpect = Integer.parseInt(s.substring(8));
expect = new String[nexpect];
for (i = 0; i < nexpect; i++)
expect[i] = readLine(in).trim();
} catch (NumberFormatException e) {
try {
if (s.substring(8, 17).equals("Exception")) {
expect = new String[1];
expect[0] = "Exception";
}
} catch (StringIndexOutOfBoundsException se) {
// ignore it
}
}
}
i = header.indexOf(':');
try {
if (junit)
testData.add(new Object[]{
header.substring(0, i), header.substring(i + 2),
expect, doStrict, doParseHeader});
else
test(header.substring(0, i), header.substring(i + 2),
expect, doStrict, doParseHeader);
} catch (StringIndexOutOfBoundsException e) {
e.printStackTrace(System.out);
}
}
if (s == null)
return; // EOF
if (s.length() == 0) {
while ((s = in.readLine()) != null) {
if (s.startsWith("From "))
break;
}
if (s == null)
return;
}
header = s;
}
}
private static String value(String header) {
return header.substring(header.indexOf(':') + 1).trim();
}
/**
* Read an "expected" line, handling continuations
* (backslash at end of line). If line ends with
* two backslashes, it's not a continuation, just a
* line that ends with a single backslash.
*/
private static String readLine(BufferedReader in) throws IOException {
String line = in.readLine();
if (!line.endsWith("\\"))
return line;
if (line.endsWith("\\\\"))
return line.substring(0, line.length() - 1);
StringBuilder sb = new StringBuilder(line);
sb.setCharAt(sb.length() - 1, '\n');
for (; ; ) {
line = in.readLine();
sb.append(line);
if (!line.endsWith("\\"))
break;
if (line.endsWith("\\\\")) {
sb.setLength(sb.length() - 1);
break;
}
sb.setCharAt(sb.length() - 1, '\n');
}
return sb.toString();
}
@Test
public void testAddress() {
test(headerName, headerValue, expected, doStrict, doParseHeader);
}
/**
* Test the header's value to see if we can parse it as expected.
*/
public static void test(String header, String value, String[] expect,
boolean doStrict, boolean doParseHeader) {
PrintStream out = System.out;
if (gen_test_input)
pr(header + ": " + value);
else
pr("Test: " + value);
try {
InternetAddress[] al;
if (doParseHeader)
al = InternetAddress.parseHeader(value, doStrict);
else
al = InternetAddress.parse(value, doStrict);
if (gen_test_input)
pr("Expect: " + al.length);
else {
pr("Got " + al.length + " addresses:");
if (expect != null && al.length != expect.length) {
pr("Expected " + expect.length + " addresses");
if (junit)
Assert.assertEquals("For " + value +
" number of addresses",
al.length, expect.length);
errors++;
}
}
for (int i = 0; i < al.length; i++) {
if (gen_test_input)
pr("\t" + al[i].getAddress()); // XXX - escape newlines
else {
pr("\t[" + (i + 1) + "] " + al[i].getAddress() +
"\t\tPersonal: " + n(al[i].getPersonal()));
if (expect != null && i < expect.length &&
!expect[i].equals(al[i].getAddress())) {
pr("\tExpected:\t" + expect[i]);
if (junit)
Assert.assertEquals("For " + value +
" address[" + i + "]",
expect[i], al[i].getAddress());
errors++;
}
}
}
if (al.length == 0)
return;
/*
* Some of the really bad addresses fail the toString
* tests, but we don't want them to cause build failures.
*/
if (junit)
return;
/*
* As a sanity test, convert the address array to a string and
* then parse it again, to see if we get the same thing back.
*/
try {
InternetAddress[] al2;
String ta = InternetAddress.toString(al);
if (doParseHeader)
al2 = InternetAddress.parseHeader(ta, doStrict);
else
al2 = InternetAddress.parse(ta, doStrict);
if (al.length != al2.length) {
pr("toString FAILED!!!");
pr("Expected length " + al.length +
", got " + al2.length);
if (junit)
Assert.assertEquals("For " + value +
" toString number of addresses",
al.length, al2.length);
errors++;
} else {
for (int i = 0; i < al.length; i++) {
if (!al[i].getAddress().equals(al2[i].getAddress())) {
pr("toString FAILED!!!");
pr("Expected address " +
al[i].getAddress() +
", got " + al2[i].getAddress());
if (junit)
Assert.assertEquals("For " + value +
" toString " + ta + " address[" + i + "]",
al[i].getAddress(), al2[i].getAddress());
errors++;
}
String p1 = al[i].getPersonal();
String p2 = al2[i].getPersonal();
if (!(p1 == p2 || (p1 != null && p1.equals(p2)))) {
pr("toString FAILED!!!");
pr("Expected personal " + n(p1) +
", got " + n(p2));
if (junit)
Assert.assertEquals("For " + value +
" toString " + ta + " personal[" + i + "]",
p1, p2);
errors++;
}
}
}
} catch (AddressException e2) {
pr("toString FAILED!!!");
pr("Got Exception: " + e2);
if (junit)
Assert.fail("For " + value +
" toString got Exception: " + e2);
errors++;
}
} catch (AddressException e) {
if (gen_test_input)
pr("Expect: Exception " + e);
else {
pr("Got Exception: " + e);
if (expect != null &&
(expect.length != 1 || !expect[0].equals("Exception"))) {
pr("Expected " + expect.length + " addresses");
for (int i = 0; i < expect.length; i++)
pr("\tExpected:\t" + expect[i]);
if (junit)
Assert.fail("For " + value + " expected " +
expect.length + "addresses, got Exception: " + e);
errors++;
}
}
}
}
private static final void pr(String s) {
if (verbose)
System.out.println(s);
}
private static final String n(String s) {
return s == null ? "<null>" : s;
}
}