/*
* 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.apache.tomcat.util.descriptor.web;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
/**
* Test case for {@link WebXml} fragment ordering.
*/
public class TestWebXmlOrdering {
private WebXml app;
private WebXml a;
private WebXml b;
private WebXml c;
private WebXml d;
private WebXml e;
private WebXml f;
private Map<String,WebXml> fragments;
private int posA;
private int posB;
private int posC;
private int posD;
private int posE;
private int posF;
@Before
public void setUp() {
app = new WebXml();
a = new WebXml();
a.setName("a");
b = new WebXml();
b.setName("b");
c = new WebXml();
c.setName("c");
d = new WebXml();
d.setName("d");
e = new WebXml();
e.setName("e");
f = new WebXml();
f.setName("f");
// Control the input order
fragments = new LinkedHashMap<>();
fragments.put("a",a);
fragments.put("b",b);
fragments.put("c",c);
fragments.put("d",d);
fragments.put("e",e);
fragments.put("f",f);
}
@Test
public void testOrderWebFragmentsAbsolute() {
app.addAbsoluteOrdering("c");
app.addAbsoluteOrdering("a");
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering("e");
app.addAbsoluteOrdering("d");
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
assertEquals(c,iter.next());
assertEquals(a,iter.next());
assertEquals(b,iter.next());
assertEquals(e,iter.next());
assertEquals(d,iter.next());
assertFalse(iter.hasNext());
}
@Test
public void testOrderWebFragmentsAbsolutePartial() {
app.addAbsoluteOrdering("c");
app.addAbsoluteOrdering("a");
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
assertEquals(c,iter.next());
assertEquals(a,iter.next());
assertFalse(iter.hasNext());
}
@Test
public void testOrderWebFragmentsAbsoluteOthersStart() {
app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering("d");
Set<WebXml> others = new HashSet<>();
others.add(a);
others.add(c);
others.add(e);
others.add(f);
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
while (others.size() > 0) {
WebXml o = iter.next();
assertTrue(others.contains(o));
others.remove(o);
}
assertEquals(b,iter.next());
assertEquals(d,iter.next());
assertFalse(iter.hasNext());
}
@Test
public void testOrderWebFragmentsAbsoluteOthersMiddle() {
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
app.addAbsoluteOrdering("d");
Set<WebXml> others = new HashSet<>();
others.add(a);
others.add(c);
others.add(e);
others.add(f);
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
assertEquals(b,iter.next());
while (others.size() > 0) {
WebXml o = iter.next();
assertTrue(others.contains(o));
others.remove(o);
}
assertEquals(d,iter.next());
assertFalse(iter.hasNext());
}
@Test
public void testWebFragmentsAbsoluteWrongFragmentName() {
app.addAbsoluteOrdering("a");
app.addAbsoluteOrdering("z");
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
assertEquals(1,ordered.size());
assertEquals(fragments.get("a"),ordered.toArray()[0]);
}
@Test
public void testOrderWebFragmentsAbsoluteOthersEnd() {
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering("d");
app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
Set<WebXml> others = new HashSet<>();
others.add(a);
others.add(c);
others.add(e);
others.add(f);
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
assertEquals(b,iter.next());
assertEquals(d,iter.next());
while (others.size() > 0) {
WebXml o = iter.next();
assertTrue(others.contains(o));
others.remove(o);
}
assertFalse(iter.hasNext());
}
private void doRelativeOrderingTest(RelativeOrderingTestRunner runner) {
// Confirm we have all 720 possible input orders
// Set<String> orders = new HashSet<>();
// Test all possible input orders since some bugs were discovered that
// depended on input order
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 4; k++) {
for (int l = 0; l < 3; l++) {
for (int m = 0; m < 2; m++) {
setUp();
runner.init();
ArrayList<WebXml> source = new ArrayList<>();
source.addAll(fragments.values());
Map<String,WebXml> input =
new LinkedHashMap<>();
WebXml one = source.remove(i);
input.put(one.getName(), one);
WebXml two = source.remove(j);
input.put(two.getName(), two);
WebXml three = source.remove(k);
input.put(three.getName(), three);
WebXml four = source.remove(l);
input.put(four.getName(), four);
WebXml five = source.remove(m);
input.put(five.getName(), five);
WebXml six = source.remove(0);
input.put(six.getName(), six);
/*
String order = one.getName() + two.getName() +
three.getName() + four.getName() +
five.getName() + six.getName();
orders.add(order);
*/
Set<WebXml> ordered =
WebXml.orderWebFragments(app, input, null);
populatePositions(ordered);
runner.validate(getOrder(ordered));
}
}
}
}
}
// System.out.println(orders.size());
}
private String getOrder(Set<WebXml> ordered) {
StringBuilder sb = new StringBuilder(ordered.size());
for (WebXml webXml : ordered) {
sb.append(webXml.getName());
}
return sb.toString();
}
private void populatePositions(Set<WebXml> ordered) {
List<WebXml> indexed = new ArrayList<>();
indexed.addAll(ordered);
posA = indexed.indexOf(a);
posB = indexed.indexOf(b);
posC = indexed.indexOf(c);
posD = indexed.indexOf(d);
posE = indexed.indexOf(e);
posF = indexed.indexOf(f);
}
@Test
public void testOrderWebFragmentsRelative1() {
// First example from servlet spec
doRelativeOrderingTest(new RelativeTestRunner1());
}
@Test
public void testOrderWebFragmentsRelative2() {
// Second example - use fragment a for no-id fragment
doRelativeOrderingTest(new RelativeTestRunner2());
}
@Test
public void testOrderWebFragmentsRelative3() {
// Third example from spec with e & f added
doRelativeOrderingTest(new RelativeTestRunner3());
}
@Test
public void testOrderWebFragmentsRelative4Bug54068() {
// Simple sequence that failed for some inputs
doRelativeOrderingTest(new RelativeTestRunner4());
}
@Test
public void testOrderWebFragmentsRelative5Bug54068() {
// Simple sequence that failed for some inputs
doRelativeOrderingTest(new RelativeTestRunner5());
}
@Test
public void testOrderWebFragmentsRelative6Bug54068() {
// Simple sequence that failed for some inputs
doRelativeOrderingTest(new RelativeTestRunner6());
}
@Test
public void testOrderWebFragmentsRelative7() {
// Reference loop (but not circular dependencies)
doRelativeOrderingTest(new RelativeTestRunner7());
}
@Test
public void testOrderWebFragmentsRelative8() {
// More complex, trying to break the algorithm
doRelativeOrderingTest(new RelativeTestRunner8());
}
@Test
public void testOrderWebFragmentsRelative9() {
// Variation on bug 54068
doRelativeOrderingTest(new RelativeTestRunner9());
}
@Test
public void testOrderWebFragmentsRelative10() {
// Variation on bug 54068
doRelativeOrderingTest(new RelativeTestRunner10());
}
@Test
public void testOrderWebFragmentsRelative11() {
// Test references to non-existant fragments
doRelativeOrderingTest(new RelativeTestRunner11());
}
@Test(expected=IllegalArgumentException.class)
public void testOrderWebFragmentsrelativeCircular1() {
a.addBeforeOrdering("b");
b.addBeforeOrdering("a");
WebXml.orderWebFragments(app, fragments, null);
}
@Test(expected=IllegalArgumentException.class)
public void testOrderWebFragmentsrelativeCircular2() {
a.addBeforeOrderingOthers();
b.addAfterOrderingOthers();
c.addBeforeOrdering("a");
c.addAfterOrdering("b");
WebXml.orderWebFragments(app, fragments, null);
}
private interface RelativeOrderingTestRunner {
void init();
void validate(String order);
}
private class RelativeTestRunner1 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrderingOthers();
a.addAfterOrdering("c");
b.addBeforeOrderingOthers();
c.addAfterOrderingOthers();
f.addBeforeOrderingOthers();
f.addBeforeOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
//a.addAfterOrderingOthers();
assertTrue(order, posA > posB);
assertTrue(order, posA > posC);
assertTrue(order, posA > posD);
assertTrue(order, posA > posE);
assertTrue(order, posA > posF);
// a.addAfterOrdering("c");
assertTrue(order, posA > posC);
// b.addBeforeOrderingOthers();
assertTrue(order, posB < posC);
// c.addAfterOrderingOthers();
assertTrue(order, posC > posB);
assertTrue(order, posC > posD);
assertTrue(order, posC > posE);
assertTrue(order, posC > posF);
// f.addBeforeOrderingOthers();
assertTrue(order, posF < posA);
assertTrue(order, posF < posB);
assertTrue(order, posF < posC);
assertTrue(order, posF < posD);
assertTrue(order, posF < posE);
// f.addBeforeOrdering("b");
assertTrue(order, posF < posB);
}
}
private class RelativeTestRunner2 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrderingOthers();
a.addBeforeOrdering("c");
b.addBeforeOrderingOthers();
d.addAfterOrderingOthers();
e.addBeforeOrderingOthers();
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrderingOthers();
assertTrue(order, posA > posB);
assertTrue(order, posA > posE);
assertTrue(order, posA > posF);
// a.addBeforeOrdering("c");
assertTrue(order, posC > posA);
assertTrue(order, posC > posB);
assertTrue(order, posC > posE);
assertTrue(order, posC > posF);
// b.addBeforeOrderingOthers();
assertTrue(order, posB < posA);
assertTrue(order, posB < posC);
assertTrue(order, posB < posD);
assertTrue(order, posB < posF);
// d.addAfterOrderingOthers();
assertTrue(order, posD > posB);
assertTrue(order, posD > posE);
assertTrue(order, posD > posF);
// e.addBeforeOrderingOthers();
assertTrue(order, posE < posA);
assertTrue(order, posE < posC);
assertTrue(order, posE < posD);
assertTrue(order, posE < posF);
}
}
private class RelativeTestRunner3 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrdering("b");
c.addBeforeOrderingOthers();
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrdering("b");
assertTrue(order, posA > posB);
// c.addBeforeOrderingOthers();
assertTrue(order, posC < posA);
assertTrue(order, posC < posB);
assertTrue(order, posC < posD);
assertTrue(order, posC < posE);
assertTrue(order, posC < posF);
}
}
private class RelativeTestRunner4 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addAfterOrdering("a");
c.addAfterOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addAfterOrdering("a");
assertTrue(order, posB > posA);
// c.addAfterOrdering("b");
assertTrue(order, posC > posB);
}
}
private class RelativeTestRunner5 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addBeforeOrdering("a");
c.addBeforeOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addBeforeOrdering("a");
assertTrue(order, posB < posA);
// c.addBeforeOrdering("b");
assertTrue(order, posC < posB);
}
}
private class RelativeTestRunner6 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addBeforeOrdering("a");
b.addAfterOrdering("c");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addBeforeOrdering("a");
assertTrue(order, posB < posA);
//b.addAfterOrdering("c");
assertTrue(order, posB > posC);
}
}
private class RelativeTestRunner7 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addBeforeOrdering("a");
c.addBeforeOrdering("b");
a.addAfterOrdering("c");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addBeforeOrdering("a");
assertTrue(order, posB < posA);
// c.addBeforeOrdering("b");
assertTrue(order, posC < posB);
// a.addAfterOrdering("c");
assertTrue(order, posA > posC);
}
}
private class RelativeTestRunner8 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addBeforeOrderingOthers();
a.addBeforeOrdering("b");
b.addBeforeOrderingOthers();
c.addAfterOrdering("b");
d.addAfterOrdering("c");
e.addAfterOrderingOthers();
f.addAfterOrderingOthers();
f.addAfterOrdering("e");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addBeforeOrderingOthers();
assertTrue(order, posA < posB);
assertTrue(order, posA < posC);
assertTrue(order, posA < posD);
assertTrue(order, posA < posE);
assertTrue(order, posA < posF);
// a.addBeforeOrdering("b");
assertTrue(order, posA < posB);
// b.addBeforeOrderingOthers();
assertTrue(order, posB < posC);
assertTrue(order, posB < posD);
assertTrue(order, posB < posE);
assertTrue(order, posB < posF);
// c.addAfterOrdering("b");
assertTrue(order, posC > posB);
// d.addAfterOrdering("c");
assertTrue(order, posD > posC);
// e.addAfterOrderingOthers();
assertTrue(order, posE > posA);
assertTrue(order, posE > posB);
assertTrue(order, posE > posC);
assertTrue(order, posE > posD);
// f.addAfterOrderingOthers();
assertTrue(order, posF > posA);
assertTrue(order, posF > posB);
assertTrue(order, posF > posC);
assertTrue(order, posF > posD);
assertTrue(order, posF > posE);
// f.addAfterOrdering("e");
assertTrue(order, posF > posE);
}
}
private class RelativeTestRunner9 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addBeforeOrderingOthers();
b.addBeforeOrdering("a");
c.addBeforeOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addBeforeOrderingOthers();
assertTrue(order, posA < posD);
assertTrue(order, posA < posE);
assertTrue(order, posA < posF);
// b.addBeforeOrdering("a");
assertTrue(order, posB < posA);
// c.addBeforeOrdering("b");
assertTrue(order, posC < posB);
}
}
private class RelativeTestRunner10 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrderingOthers();
b.addAfterOrdering("a");
c.addAfterOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrderingOthers();
assertTrue(order, posA > posD);
assertTrue(order, posA > posE);
assertTrue(order, posA > posF);
// b.addAfterOrdering("a");
assertTrue(order, posB > posA);
// c.addAfterOrdering("b");
assertTrue(order, posC > posB);
}
}
private class RelativeTestRunner11 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrdering("b");
b.addAfterOrdering("z");
b.addBeforeOrdering("y");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrdering("b");
assertTrue(order, posA > posB);
}
}
}