001/*
002 * Shredzone Commons
003 *
004 * Copyright (C) 2014 Richard "Shred" Körber
005 *   http://commons.shredzone.org
006 *
007 * This program is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU Library General Public License as
009 * published by the Free Software Foundation, either version 3 of the
010 * License, or (at your option) any later version.
011 *
012 * This program is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU Library General Public License
018 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
019 */
020package org.shredzone.commons.xml;
021
022import static java.util.Arrays.copyOfRange;
023import static java.util.stream.Collectors.toList;
024import static org.hamcrest.Matchers.*;
025import static org.junit.Assert.assertThat;
026import static org.mockito.Mockito.mock;
027
028import java.util.ArrayList;
029import java.util.List;
030import java.util.Spliterator;
031import java.util.stream.IntStream;
032
033import org.junit.Before;
034import org.junit.Test;
035import org.w3c.dom.Node;
036import org.w3c.dom.NodeList;
037
038/**
039 * Unit tests for the {@link NodeListSpliterator}.
040 *
041 * @author Richard "Shred" Körber
042 */
043public class NodeListSpliteratorTest {
044
045    private static final int NODES = 11; // prime number, so splitting isn't too easy
046    private static final int HALF = NODES / 2;
047
048    private Node[] nodes;
049    private NodeList nodeList;
050
051    /**
052     * Sets up an array of nodes and a {@link NodeList} returning that array.
053     */
054    @Before
055    public void init() {
056        nodes = new Node[NODES];
057        IntStream.range(0, nodes.length).forEach(ix -> nodes[ix] = mock(Node.class));
058
059        nodeList = new NodeList() {
060            @Override
061            public Node item(int index) {
062                return nodes[index];
063            }
064            @Override
065            public int getLength() {
066                return nodes.length;
067            }
068        };
069    }
070
071    /**
072     * Is {@link NodeListSpliterator#characteristics()} returning the correct
073     * characteristics?
074     */
075    @Test
076    public void characteristicsTest() {
077        NodeListSpliterator spliterator = new NodeListSpliterator(nodeList);
078        assertThat(spliterator.characteristics(), is(Spliterator.ORDERED
079                        | Spliterator.DISTINCT | Spliterator.SIZED | Spliterator.NONNULL
080                        | Spliterator.SUBSIZED));
081    }
082
083    /**
084     * Does the {@link NodeListSpliterator#tryAdvance(java.util.function.Consumer)}
085     * return all {@link NodeList} elements, in correct order?
086     */
087    @Test
088    public void simpleTest() {
089        NodeListSpliterator spliterator = new NodeListSpliterator(nodeList);
090        assertThat(spliterator.estimateSize(), is((long) nodes.length));
091
092        List<Node> result = consume(spliterator);
093
094        assertThat(result, contains(nodes));
095        assertThat(spliterator.estimateSize(), is(0L));
096    }
097
098    /**
099     * Does {@link NodeListSpliterator#trySplit()} split up correctly, leaving two
100     * spliterators containing half of the {@link NodeList} each?
101     */
102    @Test
103    public void splitTest() {
104        NodeListSpliterator s1 = new NodeListSpliterator(nodeList);
105        assertThat(s1.estimateSize(), is((long) nodes.length));
106
107        Spliterator<Node> s2 = s1.trySplit();
108        assertThat(s1.estimateSize(), is((long) HALF));
109        assertThat(s2.estimateSize(), is((long) (NODES - HALF)));
110
111        List<Node> r1 = consume(s1);
112        assertThat(s1.estimateSize(), is(0L));
113        assertThat(r1, contains(copyOfRange(nodes, 0, HALF)));
114
115        List<Node> r2 = consume(s2);
116        assertThat(s2.estimateSize(), is(0L));
117        assertThat(r2, contains(copyOfRange(nodes, HALF, NODES)));
118    }
119
120    /**
121     * Does {@link NodeListSpliterator#trySplit()} split up correctly to the limits?
122     */
123    @Test
124    public void fullSplitTest() {
125        List<Spliterator<Node>> spliterators = new ArrayList<>();
126        spliterators.add(new NodeListSpliterator(nodeList));
127
128        // Split all spliterators until they contain a single element
129        while (spliterators.size() != NODES) {
130            List<Spliterator<Node>> newSpliterators = new ArrayList<>();
131            for (Spliterator<Node> sp1 : spliterators) {
132                newSpliterators.add(sp1);
133                Spliterator<Node> sp2 = sp1.trySplit();
134                if (sp2 != null) {
135                    newSpliterators.add(sp2);
136                }
137            }
138            spliterators = newSpliterators;
139        }
140
141        // Make sure all spliterators cannot be split any further
142        for (Spliterator<Node> sp : spliterators) {
143            assertThat(sp.estimateSize(), is(1L));
144            assertThat(sp.trySplit(), is(nullValue()));
145        }
146
147        // Consume the content of each spliterator, make sure they are in correct order
148        for (int ix = 0; ix < NODES; ix++) {
149            Node refNode = nodes[ix];
150            Spliterator<Node> sp = spliterators.get(ix);
151            sp.tryAdvance(it -> assertThat(it, is(refNode)));
152            assertThat(sp.estimateSize(), is(0L));
153        }
154    }
155
156    /**
157     * Does the {@link NodeListSpliterator#stream()} method return a stream containing
158     * all remaining elements?
159     */
160    @Test
161    public void streamTest() {
162        NodeListSpliterator spliterator = new NodeListSpliterator(nodeList);
163        List<Node> result = spliterator.stream().collect(toList());
164        assertThat(result, contains(nodes));
165    }
166
167    /**
168     * Consumes the {@link Spliterator} and returns a list of all {@link Node} retrieved.
169     *
170     * @param spliterator
171     *            {@link Spliterator} to consume
172     * @return List of all {@link Node} elements of that {@link Spliterator}
173     */
174    private List<Node> consume(Spliterator<Node> spliterator) {
175        List<Node> result = new ArrayList<>();
176        spliterator.forEachRemaining(result::add);
177        return result;
178    }
179
180}