org.apache.wicket.MarkupContainerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.MarkupContainerTest.java

Source

/*
 * 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.wicket;

import static org.hamcrest.CoreMatchers.equalToObject;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;

import java.lang.reflect.Field;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Random;

import org.apache.commons.collections4.map.LinkedMap;
import org.apache.wicket.core.util.lang.WicketObjects;
import org.apache.wicket.markup.IMarkupResourceStreamProvider;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.EmptyPanel;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.StringResourceStream;
import org.apache.wicket.util.tester.WicketTestCase;
import org.junit.Assert;
import org.junit.Test;

@SuppressWarnings({ "javadoc", "serial" })
public class MarkupContainerTest extends WicketTestCase {
    private static final int NUMBER_OF_CHILDREN_FOR_A_MAP = MarkupContainer.MAPIFY_THRESHOLD + 1;

    /**
     * Make sure components are iterated in the order they were added. Required e.g. for Repeaters
     */
    @Test
    public void iteratorOrder() {
        MarkupContainer container = new WebMarkupContainer("component");
        for (int i = 0; i < 10; i++) {
            container.add(new WebComponent(Integer.toString(i)));
        }
        int i = 0;
        for (Component component : container) {
            assertEquals(Integer.toString(i++), component.getId());
        }
    }

    @Test
    public void markupId() throws Exception {
        executeTest(MarkupIdTestPage.class, "MarkupIdTestPageExpectedResult.html");
    }

    @Test
    public void get() {
        WebMarkupContainer a = new WebMarkupContainer("a");
        WebMarkupContainer b = new WebMarkupContainer("b");
        WebMarkupContainer c = new WebMarkupContainer("c");
        WebMarkupContainer d = new WebMarkupContainer("d");
        WebMarkupContainer e = new WebMarkupContainer("e");
        WebMarkupContainer f = new WebMarkupContainer("f");

        // ....A
        // ...B....C
        // .......D..E
        // ...........F

        a.add(b);
        a.add(c);
        c.add(d);
        c.add(e);
        e.add(f);

        // basic gets

        assertTrue(a.get(null) == a);
        assertTrue(a.get("") == a);
        assertTrue(a.get("b") == b);
        assertTrue(a.get("c") == c);
        assertTrue(a.get("c:d") == d);
        assertTrue(a.get("c:e:f") == f);

        // parent path gets

        assertTrue(b.get("..") == a);
        assertTrue(e.get("..:..") == a);
        assertTrue(d.get("..:..:c:e:f") == f);
        assertTrue(e.get("..:d:..:e:f") == f);
        assertTrue(e.get("..:d:..:..") == a);

        // invalid gets

        assertNull(a.get(".."));
        assertNull(a.get("..:a"));
        assertNull(b.get("..|.."));
        assertNull(a.get("q"));
    }

    /**
     * https://issues.apache.org/jira/browse/WICKET-4006
     */
    @Test(expected = IllegalArgumentException.class)
    public void addMyself() {
        WebMarkupContainer me = new WebMarkupContainer("a");
        me.add(me);
    }

    /**
     * https://issues.apache.org/jira/browse/WICKET-5911
     */
    @Test
    public void rerenderAfterRenderFailure() {
        FirstRenderFailsPage page = new FirstRenderFailsPage();
        try {
            tester.startPage(page);
        } catch (WicketRuntimeException expected) {
        }

        tester.startPage(page);

        // rendering flags where properly reset, so second rendering works properly
        assertEquals(2, page.beforeRenderCalls);
    }

    /**
     * https://issues.apache.org/jira/browse/WICKET-4012
     */
    @Test
    public void afterRenderJustOnce() {
        AfterRenderJustOncePage page = new AfterRenderJustOncePage();
        tester.startPage(page);

        assertEquals(1, page.afterRenderCalls);
    }

    /**
     * https://issues.apache.org/jira/browse/WICKET-4016
     */
    @Test
    public void callToStringFromConstructor() {
        ToStringComponent page = new ToStringComponent();
    }

    private static class ToStringComponent extends WebMarkupContainer {
        private ToStringComponent() {
            super("id");
            toString(true);
        }
    }

    private static class AfterRenderJustOncePage extends WebPage implements IMarkupResourceStreamProvider {
        private int afterRenderCalls = 0;

        private AfterRenderJustOncePage() {

            WebMarkupContainer a1 = new WebMarkupContainer("a1");
            add(a1);

            WebMarkupContainer a2 = new WebMarkupContainer("a2");
            a1.add(a2);

            WebMarkupContainer a3 = new WebMarkupContainer("a3") {

                @Override
                protected void onAfterRender() {
                    super.onAfterRender();
                    afterRenderCalls++;
                }

            };
            a2.add(a3);
        }

        @Override
        public IResourceStream getMarkupResourceStream(MarkupContainer container, Class<?> containerClass) {
            return new StringResourceStream(
                    "<html><body><div wicket:id='a1'><div wicket:id='a2'><div wicket:id='a3'></div></div></div></body></html>");
        }
    }

    private static class FirstRenderFailsPage extends WebPage implements IMarkupResourceStreamProvider {
        private boolean firstRender = true;

        private int beforeRenderCalls = 0;

        private FirstRenderFailsPage() {
            WebMarkupContainer a1 = new WebMarkupContainer("a1") {
                @Override
                protected void onBeforeRender() {
                    super.onBeforeRender();

                    beforeRenderCalls++;

                    if (firstRender) {
                        firstRender = false;
                        throw new WicketRuntimeException();
                    }
                }
            };
            add(a1);
        }

        @Override
        public IResourceStream getMarkupResourceStream(MarkupContainer container, Class<?> containerClass) {
            return new StringResourceStream("<html><body><div wicket:id='a1'></div></body></html>");
        }
    }

    /*
     * Iterator tests
     * 
     * The tests below are specific for testing addition and removal of children while maintaining
     * the correct order of iterators without throwing ConcurrentModificationException.
     */

    @Test
    public void noChildShouldNotIterate() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        Iterator<Component> iterator = wmc.iterator();
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildAddingChildAfterIteratorAcquiredShouldIterateAndReturnNewChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        Iterator<Component> iterator = wmc.iterator();

        Label label1 = new Label("label1", "Label1");
        wmc.add(label1);

        assertThat(wmc.size(), is(1));

        Assert.assertThat(iterator.hasNext(), is(true));
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildAddingNChildrenAfterIteratorAcquiredShouldIterateAndReturnNewChildren() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        Iterator<Component> iterator = wmc.iterator();

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);

        assertThat(wmc.size(), is(NUMBER_OF_CHILDREN_FOR_A_MAP));

        Label label1 = new Label("label1", "Label1");
        wmc.add(label1);

        Assert.assertThat(iterator.hasNext(), is(true));

        takeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP);

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildAddingNChildrenAfterIteratorAcquiredShouldIterateAndReturnNewChildren2() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);

        assertThat(wmc.size(), is(NUMBER_OF_CHILDREN_FOR_A_MAP));

        Iterator<Component> iterator = wmc.iterator();

        takeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP);

        Label label1 = new Label("label1", "Label1");
        wmc.add(label1);

        Assert.assertThat(iterator.hasNext(), is(true));
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildAddingAndRemoveChildAfterIteratorAcquiredShouldNotIterate() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");

        Iterator<Component> iterator = wmc.iterator();

        wmc.add(label1);
        wmc.remove(label1);

        assertThat(wmc.size(), is(0));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void addingNewChildAfterIterationHasStartedShouldIterateNewChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        // add one child
        addNChildren(wmc, 1);

        Iterator<Component> iterator = wmc.iterator();

        // iterate
        takeNChildren(iterator, 1);

        // there are no more children to iterate
        Assert.assertThat(iterator.hasNext(), is(false));

        // add the new child
        Label newChild = new Label("label1", "Label1");
        wmc.add(newChild);

        assertThat(wmc.size(), is(2));

        // ensure that the newChild is up next (as it was added)
        Assert.assertThat(iterator.next(), is(equalToObject(newChild)));
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void replacingTheFirstChildAfterIteratingDoesntIterateTheNewChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");
        Component label2 = new Label("label2", "Label2");

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);
        wmc.add(label1);
        wmc.add(label2);

        Iterator<Component> iterator = wmc.iterator();

        takeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP);

        iterator.next();

        // replace the first child **after** we already passed the child with the iterator
        Label newChild = new Label("label1", "newChild");
        wmc.replace(newChild);

        // the next child is still label 2
        assertThat(iterator.next(), is(sameInstance(label2)));

        // and the new child is not iterated (was replaced before the current position of the
        // iterator).
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void removingComponentsDuringIterationDoesntFail() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        Component label1 = new Label("label1", "Label1");
        Component label2 = new Label("label2", "Label2");
        Component label3 = new Label("label3", "Label3");
        Component label4 = new Label("label4", "Label4");
        Component label5 = new Label("label5", "Label5");

        wmc.add(label1);
        wmc.add(label2);
        wmc.add(label3);
        wmc.add(label4);
        wmc.add(label5);

        // start iterating the 5 children
        Iterator<Component> iterator = wmc.iterator();

        assertThat(iterator.next(), is(sameInstance(label1)));
        assertThat(iterator.next(), is(sameInstance(label2)));
        assertThat(iterator.next(), is(sameInstance(label3)));

        // remove the current, previous and next children
        wmc.remove(label3);
        wmc.remove(label2);
        wmc.remove(label4);

        // ensure that the next iterated child is the 5th label
        assertThat(iterator.next(), is(sameInstance(label5)));

        // and that there are no more children to iterate
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void childrenBecomesListWhenMoreThanOneChild() throws Exception {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        addNChildren(wmc, 5);

        Field childrenField = MarkupContainer.class.getDeclaredField("children");
        childrenField.setAccessible(true);
        Object field = childrenField.get(wmc);
        assertThat(field, is(instanceOf(List.class)));
    }

    @Test
    public void childrenListBecomesMapWhenThresholdPassed() throws Exception {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP - 1);
        assertChildrenType(wmc, List.class);

        addNChildren(wmc, 1);
        assertChildrenType(wmc, LinkedMap.class);
    }

    @Test
    public void childrenBecomesLinkedMapWhenThresholdPassed() throws Exception {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP + 1);

        assertChildrenType(wmc, LinkedMap.class);
    }

    @Test
    public void linkedMapChildrenBecomesChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);
        wmc.add(new EmptyPanel("panel"));

        assertChildrenType(wmc, LinkedMap.class);

        Iterator<Component> iterator = wmc.iterator();
        removeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP);

        assertChildrenType(wmc, EmptyPanel.class);
    }

    @Test
    public void listChildrenBecomesChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP - 2);
        wmc.add(new EmptyPanel("panel"));

        assertChildrenType(wmc, List.class);

        Iterator<Component> iterator = wmc.iterator();
        removeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP - 2);

        assertChildrenType(wmc, EmptyPanel.class);
    }

    @Test
    public void geenIdee3() throws Exception {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP + 1);

        Iterator<Component> iterator = wmc.iterator();

        removeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP);

        assertThat(iterator.hasNext(), is(true));
        assertThat(wmc.size(), is(1));

        iterator.next();

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildAddIterateAndRemoveChildShouldIterateChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");

        Iterator<Component> iterator = wmc.iterator();

        wmc.add(label1);
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));

        wmc.remove(label1);
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildAddIterateAndRemoveAndAddSameChildShouldIterateChildTwice() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");

        Iterator<Component> iterator = wmc.iterator();

        wmc.add(label1);
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));

        Assert.assertThat(iterator.hasNext(), is(false));

        wmc.remove(label1);

        Assert.assertThat(iterator.hasNext(), is(false));

        wmc.add(label1);
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
    }

    @Test
    public void noChildAddIterateAndRemoveAndAddDifferentChildShouldIterateNewChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");
        Label label2 = new Label("label1", "Label2");

        Iterator<Component> iterator = wmc.iterator();

        wmc.add(label1);
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));

        Assert.assertThat(iterator.hasNext(), is(false));

        wmc.remove(label1);

        Assert.assertThat(iterator.hasNext(), is(false));

        wmc.add(label2);
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));
    }

    @Test
    public void noChildAddingAndReplaceChildAfterIteratorAcquiredShouldIterateAndReturnNewReplacementChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");
        Label label2 = new Label("label1", "Label2");

        Iterator<Component> iterator = wmc.iterator();

        wmc.add(label1);
        wmc.replace(label2);

        Assert.assertThat(iterator.hasNext(), is(true));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void singleChildIterateOneChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        wmc.add(label1 = new Label("label1", "Label1"));

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.hasNext(), is(true));
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void singleChildShouldAllowReplacingChildAfterIterationHasStarted() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Component label1 = new Label("label1", "Label1");
        Component label2 = new Label("label1", "Label2");

        wmc.add(label1);

        Iterator<Component> iterator = wmc.iterator();

        wmc.replace(label2);

        Assert.assertThat(iterator.hasNext(), is(true));
        Assert.assertThat(iterator.next(), is(sameInstance(label2)));
        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void singleChildShouldAllowReplacingVisitedChildButNotRevisitReplacementChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");
        Label label2 = new Label("label1", "Label2");
        wmc.add(label1);

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.hasNext(), is(true));
        Assert.assertThat(iterator.next(), is(equalToObject(label1)));

        wmc.replace(label2);

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void multipleChildIteratorRetainsOrderOfAddition() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));
        wmc.add(label3 = new Label("label3", "Label3"));

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));
        Assert.assertThat(iterator.next(), is(equalToObject(label3)));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowAddingComponentAfterIterationStarted() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));

        wmc.add(label3 = new Label("label3", "Label3"));
        Assert.assertThat(iterator.next(), is(equalToObject(label3)));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowRemovingComponentAfterIterationStarted0() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));
        wmc.add(label3 = new Label("label3", "Label3"));

        Iterator<Component> iterator = wmc.iterator();

        wmc.remove(label1);

        Assert.assertThat(iterator.next(), is(equalToObject(label2)));
        Assert.assertThat(iterator.next(), is(equalToObject(label3)));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowRemovingComponentAfterIterationStarted1() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1 = new Label("label1", "Label1");
        Label label2 = new Label("label2", "Label2");
        Label label3 = new Label("label3", "Label3");
        wmc.add(label1);
        wmc.add(label2);
        wmc.add(label3);

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        wmc.remove(label1);
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));
        Assert.assertThat(iterator.next(), is(equalToObject(label3)));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowRemovingComponentAfterIterationStarted2() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));
        wmc.add(label3 = new Label("label3", "Label3"));

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));
        wmc.remove(label1);
        Assert.assertThat(iterator.next(), is(equalToObject(label3)));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowRemovingComponentAfterIterationStarted3() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));
        wmc.add(label3 = new Label("label3", "Label3"));

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));
        Assert.assertThat(iterator.next(), is(equalToObject(label3)));
        wmc.remove(label1);

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowReplacingComponentAfterIterationStarted0() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));

        Iterator<Component> iterator = wmc.iterator();

        wmc.replace(label3 = new Label("label1", "Label3"));

        Assert.assertThat(iterator.next(), is(equalToObject(label3)));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowReplacingComponentAfterIterationStarted1() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));

        Iterator<Component> iterator = wmc.iterator();

        wmc.replace(label3 = new Label("label1", "Label3"));

        Assert.assertThat(iterator.next(), is(equalToObject(label3)));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowReplacingComponentAfterIterationStarted() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));
        Assert.assertThat(iterator.next(), is(equalToObject(label2)));

        wmc.replace(label3 = new Label("label1", "Label3"));

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void iteratorShouldAllowReplacingComponentAfterIterationStarted24() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        Label label1;
        Label label2;
        Label label3;
        wmc.add(label1 = new Label("label1", "Label1"));
        wmc.add(label2 = new Label("label2", "Label2"));

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);

        Iterator<Component> iterator = wmc.iterator();

        Assert.assertThat(iterator.next(), is(equalToObject(label1)));

        wmc.replace(label3 = new Label("label2", "Label3"));

        Assert.assertThat(iterator.next(), is(equalToObject(label3)));

        takeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP);

        Assert.assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildLeftBehindRemoveEach() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);

        Iterator<Component> iterator = wmc.iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove();
        }
        assertThat(wmc.size(), is(0));
    }

    @Test
    public void noChildLeftBehindRemoveAll() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);

        Iterator<Component> iterator = wmc.iterator();

        wmc.removeAll();

        assertThat(wmc.size(), is(0));
        assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void noChildLeftBehindRemoveAll2() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);

        Iterator<Component> iterator = wmc.iterator();

        iterator.next();

        wmc.removeAll();

        assertThat(wmc.size(), is(0));
        assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void ensureSerializationDeserializationWorks() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        Iterator<Component> iterator = wmc.iterator();

        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);
        assertThat(wmc.size(), is(NUMBER_OF_CHILDREN_FOR_A_MAP));

        assertThat(WicketObjects.cloneObject(wmc), is(not(nullValue())));

        removeNChildren(iterator, 1);
        assertThat(wmc.size(), is(NUMBER_OF_CHILDREN_FOR_A_MAP - 1));
        assertThat(WicketObjects.cloneObject(wmc), is(not(nullValue())));

        removeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP - 2);
        assertThat(WicketObjects.cloneObject(wmc), is(not(nullValue())));

        assertThat(wmc.size(), is(1));
        removeNChildren(iterator, 1);
        assertThat(wmc.size(), is(0));
        assertThat(WicketObjects.cloneObject(wmc), is(not(nullValue())));
    }

    @Test
    public void detachDuringIterationWorks() {
        int halfOfChildren = NUMBER_OF_CHILDREN_FOR_A_MAP / 2;
        int numberOfRemainingChildren = halfOfChildren + NUMBER_OF_CHILDREN_FOR_A_MAP % 2;

        WebMarkupContainer wmc = new WebMarkupContainer("id");

        Iterator<Component> iterator = wmc.iterator();
        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP);

        takeNChildren(iterator, halfOfChildren);

        wmc.detach();

        takeNChildren(iterator, numberOfRemainingChildren);

        assertThat(iterator.hasNext(), is(false));
    }

    @Test
    public void detachDuringIterationWithRemovalsSucceeds() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");

        Iterator<Component> iterator = wmc.iterator();

        addNChildren(wmc, 2);
        removeNChildren(iterator, 1);
        wmc.detach();
        takeNChildren(iterator, 1);

        assertThat(iterator.hasNext(), is(false));
        assertThat(wmc.size(), is(1));
    }

    /**
     * Tests whether two iterators being used simultaneously keep correct score of where they are.
     */
    @Test
    public void twoIteratorsWorkInTandem() {
        int n = NUMBER_OF_CHILDREN_FOR_A_MAP * 2;

        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, n);

        Iterator<Component> iterator1 = wmc.iterator();
        Iterator<Component> iterator2 = wmc.iterator();

        Random r = new Random();

        for (int i = 0; i < n; i++) {
            if (r.nextBoolean()) {
                iterator1.next();
                iterator1.remove();
            } else {
                iterator2.next();
                iterator2.remove();
            }
        }

        // after 2*N removals there should not be any child left
        assertThat(iterator1.hasNext(), is(false));
        assertThat(iterator2.hasNext(), is(false));
    }

    /**
     * Tests removing a child when an iterator is active, followed by a detach still has the correct
     * state for the iterator.
     */
    @Test
    public void detachWithOneIteratorOneChild() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, 1);

        Iterator<Component> iterator1 = wmc.iterator();

        iterator1.next();
        iterator1.remove();

        wmc.detach();

        assertThat(iterator1.hasNext(), is(false));
    }

    /**
     * Tests removing and adding a component when an iterator is active, followed by a detach still
     * has the correct state for the iterator.
     */
    @Test
    public void detachWithOneIteratorOneChildRemovedAndAdded() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, 1);

        Iterator<Component> iterator1 = wmc.iterator();

        iterator1.next();
        iterator1.remove();

        addNChildren(wmc, 1);

        assertThat(iterator1.hasNext(), is(true));

        wmc.detach();

        assertThat(iterator1.hasNext(), is(true));
        assertThat(iterator1.next(), is(not(nullValue())));
    }

    /**
     * Tests the case when one child is removed from a list the iterator still works after a detach.
     */
    @Test
    public void detachWithOneIteratorTwoChildren() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, 2);

        Iterator<Component> iterator1 = wmc.iterator();

        iterator1.next();
        iterator1.remove();

        assertThat(iterator1.hasNext(), is(true));

        wmc.detach();

        assertThat(iterator1.hasNext(), is(true));
        assertThat(iterator1.next(), is(not(nullValue())));
    }

    /**
     * Tests whether when the children is a list, removal and iteration still work after a detach.
     */
    @Test
    public void detachWithOneIteratorWithListForChildren() {
        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, NUMBER_OF_CHILDREN_FOR_A_MAP - 2);

        assertChildrenType(wmc, List.class);

        Iterator<Component> iterator = wmc.iterator();

        takeNChildren(iterator, 1);

        removeNChildren(iterator, 1);

        wmc.detach();

        takeNChildren(iterator, NUMBER_OF_CHILDREN_FOR_A_MAP - 4);
        assertThat(iterator.hasNext(), is(false));
    }

    /**
     * Tests whether when the children is a map, removal and iteration still work after a detach.
     */
    @Test
    public void detachWithOneIteratorsWithMapForChildren() {
        int n = NUMBER_OF_CHILDREN_FOR_A_MAP * 2;

        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, n);

        Iterator<Component> iterator1 = wmc.iterator();

        Random r = new Random();

        for (int i = 0; i < NUMBER_OF_CHILDREN_FOR_A_MAP; i++) {
            iterator1.next();
            iterator1.remove();
        }
        wmc.detach();
        for (int i = 0; i < NUMBER_OF_CHILDREN_FOR_A_MAP; i++) {
            iterator1.next();
            iterator1.remove();
        }
        assertThat(iterator1.hasNext(), is(false));
    }

    /**
     * This tests a functional bug in the iterator implementation where you have multiple iterators
     * traversing the children, a detach happens and one of the iterators removes a child component
     * before the other iterator has a chance to update its internal state to the new world. This is
     * a known bug and we expect that this doesn't pose a problem in real world usage.
     */
    @Test(expected = ConcurrentModificationException.class)
    public void knownBugForDetachWithTwoIteratorsAndRemovals() {
        int n = NUMBER_OF_CHILDREN_FOR_A_MAP * 2;

        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, n);

        Iterator<Component> iterator1 = wmc.iterator();
        Iterator<Component> iterator2 = wmc.iterator();

        Random r = new Random();

        for (int i = 0; i < NUMBER_OF_CHILDREN_FOR_A_MAP; i++) {
            if (r.nextBoolean()) {
                iterator1.next();
                iterator1.remove();
            } else {
                iterator2.next();
                iterator2.remove();
            }
        }
        wmc.detach();
        iterator1.next();
        iterator1.remove();

        // implementation detail that gets in the way of properly solving this exotic use case: at
        // this moment iterator 2 doesn't know that the modification count was reset before the
        // iterator 1 removed the component.
        iterator2.next();

        // code never reaches this point due to the ConcurrentModificationException
    }

    /**
     * This test is the working case for the above scenario where two iterators traverse the
     * children, the component gets detached and in this case both iterators have a chance to update
     * their internal state to the new world, before they continue to traverse the children.
     */
    @Test
    public void detachWithTwoIteratorsAndRemovalsWork() {
        int n = NUMBER_OF_CHILDREN_FOR_A_MAP * 2;

        WebMarkupContainer wmc = new WebMarkupContainer("id");
        addNChildren(wmc, n);

        Iterator<Component> iterator1 = wmc.iterator();
        Iterator<Component> iterator2 = wmc.iterator();

        Random r = new Random();

        for (int i = 0; i < NUMBER_OF_CHILDREN_FOR_A_MAP; i++) {
            Iterator<Component> iterator = r.nextBoolean() ? iterator1 : iterator2;
            if (iterator.hasNext()) {
                iterator.next();
                iterator.remove();
            }
        }
        wmc.detach();
        iterator1.next();
        iterator2.next();
        iterator1.remove();
        while (iterator1.hasNext() || iterator2.hasNext()) {
            Iterator<Component> iterator = r.nextBoolean() ? iterator1 : iterator2;
            if (iterator.hasNext()) {
                iterator.next();
                iterator.remove();
            }
        }
        assertThat(iterator1.hasNext(), is(false));
        assertThat(iterator2.hasNext(), is(false));
    }

    /**
     * Asserts that the children property of the {@code wmc} is of a particular {@code type}.
     * 
     * @param wmc
     *            the web markup container whose children property is to be checked
     * @param type
     *            the expected type
     */
    private void assertChildrenType(WebMarkupContainer wmc, Class<?> type) {
        try {
            Field childrenField = MarkupContainer.class.getDeclaredField("children");
            childrenField.setAccessible(true);
            Object field = childrenField.get(wmc);
            assertThat(field, is(instanceOf(type)));
        } catch (Exception e) {
            throw new AssertionError("Unable to read children", e);
        }
    }

    /**
     * Adds {@code numberOfChildrenToAdd} anonymous children to the {@code parent}.
     * 
     * @param parent
     *            the parent to add the children to
     * @param numberOfChildrenToAdd
     *            the number of children
     */
    private void addNChildren(WebMarkupContainer parent, int numberOfChildrenToAdd) {
        assertThat(numberOfChildrenToAdd, is(greaterThanOrEqualTo(0)));
        int start = parent.size();
        for (int i = 0; i < numberOfChildrenToAdd; i++) {
            int index = start + i;
            parent.add(new Label("padding" + index, "padding" + index));
        }
    }

    /**
     * Removes {@code numberOfChildrenToRemove} anonymous children from the parent using the
     * {@code iterator}.
     * 
     * @param iterator
     *            the iterator to remove the children with
     * @param numberOfChildrenToAdd
     *            the number of children
     */
    private void removeNChildren(Iterator<Component> iterator, int numberOfChildrenToRemove) {
        for (int i = 0; i < numberOfChildrenToRemove; i++) {
            iterator.next();
            iterator.remove();
        }
    }

    /**
     * Progresses the {@code iterator} with {@code numberOfChildrenToTake} anonymous children.
     * 
     * @param iterator
     *            the iterator to progress
     * @param numberOfChildrenToTake
     *            the number of children
     */
    private void takeNChildren(Iterator<Component> iterator, int numberOfChildrenToTake) {
        for (int i = 0; i < numberOfChildrenToTake; i++)
            iterator.next();
    }

    @Test
    public void stream() {
        LoginPage loginPage = new LoginPage();
        Optional<Component> first = loginPage.stream().filter(c -> c.getId().equals("form")).findFirst();
        assertThat(first.isPresent(), is(false));

        loginPage.add(new Form<>("form"));
        Optional<Component> second = loginPage.stream().filter(c -> c.getId().equals("form")).findFirst();
        assertThat(second.isPresent(), is(true));

        loginPage.add(new WebMarkupContainer("wmc"));

        Optional<Form> form = loginPage.stream().filter(Form.class::isInstance).map(Form.class::cast).findFirst();
        assertThat(form.isPresent(), is(true));

        Optional<WebMarkupContainer> wmc = loginPage.stream().filter(WebMarkupContainer.class::isInstance)
                .map(WebMarkupContainer.class::cast).findFirst();
        assertThat(wmc.isPresent(), is(true));
    }

    @Test
    public void streamChildren() {
        LoginPage loginPage = new LoginPage();
        Optional<Component> first = loginPage.stream().filter(c -> c.getId().equals("form")).findFirst();
        assertThat(first.isPresent(), is(false));

        Form<Object> form = new Form<>("form");
        loginPage.add(form);

        form.add(new TextField<>("field"));

        assertThat(loginPage.streamChildren().filter(c -> c.getId().equals("form")).findFirst().isPresent(),
                is(true));

        assertThat(loginPage.streamChildren().filter(c -> c.getId().equals("field")).findFirst().isPresent(),
                is(true));

        assertThat(loginPage.streamChildren().filter(TextField.class::isInstance)
                .filter(c -> c.getId().equals("field")).findFirst().isPresent(), is(true));
    }
}