001 /*
002 Copyright (c) 1996-2012, Damon Hart-Davis
003 All rights reserved.
004
005 Redistribution and use in source and binary forms, with or without
006 modification, are permitted provided that the following conditions are
007 met:
008
009 * Redistributions of source code must retain the above copyright
010 notice, this list of conditions and the following disclaimer.
011
012 * Redistributions in binary form must reproduce the above copyright
013 notice, this list of conditions and the following disclaimer in the
014 documentation and/or other materials provided with the
015 distribution.
016
017 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028 */
029
030 package org.hd.d.pg2k.test.dev;
031
032 import java.util.ArrayList;
033 import java.util.Collection;
034 import java.util.Collections;
035 import java.util.Iterator;
036 import java.util.List;
037 import java.util.Map;
038 import java.util.SortedSet;
039
040 import junit.framework.TestCase;
041
042 import org.hd.d.pg2k.svrCore.Name;
043 import org.hd.d.pg2k.svrCore.TextUtils;
044 import org.hd.d.pg2k.webSvr.exhibit.TreeFilterBean;
045
046 /**Tests aspects of the the TreeFilterBean and related functionality.
047 */
048 public final class TreeFilterBeanTest extends TestCase
049 {
050 public TreeFilterBeanTest(final String name)
051 {
052 super(name);
053 }
054
055 /**Test conversion of partial URIs to prefix word sequences. */
056 public void testConvertTrailingURIToWordSequence()
057 {
058 assertEquals("Simple conversion 1a: one-word partial URI with leading /", "word1-", TreeFilterBean.convertTrailingURIToWordSequence("/word1/index.html").toString());
059 assertEquals("Simple conversion 1b: one-word partial URI without leading /", "word1-", TreeFilterBean.convertTrailingURIToWordSequence("word1/index.html").toString());
060 assertEquals("Simple conversion 1c: one-word partial URI with repeated /", "word1-", TreeFilterBean.convertTrailingURIToWordSequence("////word1/////index.html").toString());
061
062 assertEquals("Simple conversion 2: multiple words", "the-cat-sat-", TreeFilterBean.convertTrailingURIToWordSequence("/the/cat/sat/index.jsp?page=9").toString());
063
064 assertEquals("Simple conversion 3: conversion to lowercase", "the-cat-sat-", TreeFilterBean.convertTrailingURIToWordSequence("/The/caT/sat/index.jsp?").toString());
065
066 assertEquals("Simple conversion 4a: final component with dot is discarded", "word-second-", TreeFilterBean.convertTrailingURIToWordSequence("/word/second/x.y").toString());
067 assertEquals("Simple conversion 4b: final component before trailing slash is kept", "word-second-", TreeFilterBean.convertTrailingURIToWordSequence("/word/second/").toString());
068 assertEquals("Simple conversion 4c: final component without dot is treated as dir/word, not discarded", "word-second-", TreeFilterBean.convertTrailingURIToWordSequence("/word/second").toString());
069
070 try {
071 TreeFilterBean.convertTrailingURIToWordSequence("/the-cat/index.html");
072 fail("Embedded word separator in URI should be rejected.");
073 }
074 catch(final IllegalArgumentException e) { } // Good: we want an exception to have been thrown...
075
076 try {
077 TreeFilterBean.convertTrailingURIToWordSequence("/the*cat/index.html");
078 fail("Invalid word character in URI should be rejected.");
079 }
080 catch(final IllegalArgumentException e) { } // Good: we want an exception to have been thrown...
081 }
082
083 /**Test the generation of a browsable tree of exhibits from an input set...
084 * This tests if the tree construction (and generated data set)
085 * behaves as expected.
086 */
087 public void testBrowsableTree()
088 {
089 // Check that looking for uninteresting entries with a root node
090 // unconditionally returns an empty list.
091 final Map<Name,Collection<CharSequence>> emptyMap = Collections.emptyMap();
092 assertTrue("Inspection at root node unconditionally should return an empty list",
093 TreeFilterBean.getUninterestingNodesFromMap(emptyMap, Name.EMPTY).length == 0);
094
095 // Short names of the exhibits.
096 final String SHNAME1 = "cat-sat-on-mat-ANON.htxt";
097 final String SHNAME2 = "CAT-fat-DHD.jpg"; // Note different case of main word.
098 final String SHNAME3 = "dog-in-manger-1-ANON.gif";
099 final String SHNAME4 = "dog-in-manger-2-ANON.gif";
100 final String SHNAME5 = "dog-in-manger-11-ANON.gif";
101
102 // Full names of exhibits.
103 final String EXNAME1 = "memes/" + SHNAME1;
104 final String EXNAME2 = "memes/" + SHNAME2;
105 final String EXNAME3 = "other/" + SHNAME3;
106 final String EXNAME4 = "another/" + SHNAME4;
107 final String EXNAME5 = "another/" + SHNAME5;
108
109 // For now, ignore the effects of attribute words...
110 // final long now = System.currentTimeMillis();
111
112 final List<Name.ExhibitFull> exhibits1 = new ArrayList<Name.ExhibitFull>();
113 exhibits1.add(Name.ExhibitFull.create(EXNAME1));
114 exhibits1.add(Name.ExhibitFull.create(EXNAME2));
115 exhibits1.add(Name.ExhibitFull.create(EXNAME3));
116 exhibits1.add(Name.ExhibitFull.create(EXNAME4));
117 exhibits1.add(Name.ExhibitFull.create(EXNAME5));
118
119 // Build this Map non-optimised and then optimised,
120 // checking that it gives the right results in both cases.
121 for(final boolean optimised : new boolean[]{ false, true} )
122 {
123 // Build this map first with SortedSet nodes then with List nodes.
124 for(final boolean asSet : new boolean[]{ false, true} )
125 {
126 final Map<Name,Collection<CharSequence>> m1 = TreeFilterBean.computePrefixMap(Collections.unmodifiableList(exhibits1), asSet, optimised);
127
128 // Check that all node entries are in correct sorted order.
129 for(final Iterator<Collection<CharSequence>> it = m1.values().iterator(); it.hasNext(); )
130 {
131 final Collection<CharSequence> nodeEntries = (it.next());
132 if(nodeEntries.size() > 1)
133 {
134 final Iterator<CharSequence> eit = nodeEntries.iterator();
135 final CharSequence prev = eit.next();
136 while(eit.hasNext())
137 {
138 final CharSequence cur = eit.next();
139 assertTrue("all node elements must be correctly ordered",
140 TextUtils.CASE_INSENSITIVE_ORDER.compare(prev, cur) <= 0);
141 }
142 }
143 }
144
145 // Root node should contain just the entire flattened tree
146 // if it is small enough when we are optimising.
147 // Otherwise we expect to find the "cat-" and "dog-...-" subtrees.
148 // (And should be the right type.)
149 final Collection<CharSequence> rn1 = m1.get(Name.EMPTY);
150 assertTrue("expect nodes to be of right type (List/SortedSet)",
151 (asSet ? (rn1 instanceof SortedSet) : (rn1 instanceof List)));
152 final SortedSet<CharSequence> rootNode1 = TextUtils.createCharSequenceSortedSet(rn1);
153 if(!optimised)
154 {
155 assertEquals("expected exactly two subtrees (for cat and dog) at root", 2, rootNode1.size());
156 assertTrue("expected to find the cat and dog subtrees at root (optimised="+optimised+"): " + rootNode1,
157 (rootNode1.contains("cat-")) && rootNode1.contains("dog-"));
158 }
159 else
160 {
161 assertEquals("expect full exhibit set (flattened tree) at root", exhibits1.size(), rootNode1.size());
162 }
163
164 // Look at "cat-" sub-tree.
165 // We expect to find the entries merged together ignoring case,
166 // and exactly the two subtrees.
167 final SortedSet<CharSequence> catNode1 = TextUtils.createCharSequenceSortedSet(m1.get(Name.create("cat-")));
168 assertTrue("expect exactly two subtrees [or exhibits in optimised case] (for sat and fat) below cat", catNode1.size() == 2);
169 if(!optimised)
170 {
171 assertTrue("expected to find the sat and fat subtrees below cat",
172 (catNode1.contains("cat-fat-")) && (catNode1.contains("cat-sat-")));
173 }
174 else
175 {
176 assertTrue("expected to find the sat and fat exhibits below cat",
177 (catNode1.contains(Name.ExhibitFull.create(EXNAME1).getShortName())) && (catNode1.contains(Name.ExhibitFull.create(EXNAME2).getShortName())));
178 }
179
180 // Look at "cat-fat-" sub-tree.
181 // We expect to find one actual exhibit entry..
182 final Collection<CharSequence> catFatNode1 = m1.get(Name.create("cat-fat-"));
183 assertTrue("expect one exhibit entry in beneath cat-fat", catFatNode1.size() == 1);
184 assertEquals("expected to find the one exhibit-name entry", catFatNode1.iterator().next(), Name.ExhibitFull.create(EXNAME2).getShortName());
185 assertTrue("expected to find the one entry for " + SHNAME2, catFatNode1.contains(Name.ExhibitFull.create(EXNAME2).getShortName()));
186
187 // Look at "dog-in-manger-" sub-tree.
188 // We expect to find the two exhibit entries.
189 final Collection<CharSequence> dogInMangerNode1 = m1.get(Name.create("dog-in-manger-"));
190 assertTrue("expect three exhibit entries below dog-in-manger", dogInMangerNode1.size() == 3);
191 assertTrue("expected to find the three dog-in-manger entries",
192 (dogInMangerNode1.contains(Name.ExhibitFull.create(EXNAME3).getShortName())) &&
193 (dogInMangerNode1.contains(Name.ExhibitFull.create(EXNAME4).getShortName())) &&
194 (dogInMangerNode1.contains(Name.ExhibitFull.create(EXNAME5).getShortName())));
195 // Check uninteresting parents.
196 final boolean dogInMangerBoring[] = TreeFilterBean.getUninterestingNodesFromMap(m1, "dog-in-manger-");
197 assertTrue("dog-in-manger- should have two parents noted",
198 dogInMangerBoring.length == 2);
199 assertTrue("dog-in-manger- parent nodes should both be marked uninteresting if not optimised",
200 dogInMangerBoring[0] && dogInMangerBoring[1]);
201
202 // Now in the non-optimised case the "dog-" node should point to the "dog-in-" node,
203 // but in the optimised case it should point straight at the less dull "dog-in-manger-" node.
204 final Collection<CharSequence> dogNode1 = m1.get(Name.create("dog-"));
205 if(!optimised)
206 {
207 assertTrue("expect one sub-tree below dog", dogNode1.size() == 1);
208 assertTrue("expected one subnode: " + dogNode1.iterator().next(), dogNode1.contains(Name.create("dog-in-")));
209 }
210
211 // Check that looking for uninteresting entries with a single-word node
212 // returns an empty list.
213 assertTrue("Inspection at single-word node unconditionally should an empty list",
214 TreeFilterBean.getUninterestingNodesFromMap(m1, "cat-").length == 0);
215 assertTrue("Inspection at single-word node unconditionally should an empty list",
216 TreeFilterBean.getUninterestingNodesFromMap(m1, "dog-").length == 0);
217
218 final boolean catSatOnMatBoring[] = TreeFilterBean.getUninterestingNodesFromMap(m1, "cat-sat-on-mat-");
219 assertTrue("cat-sat-on-mat- should have three (un)interesting parents noted: " + catSatOnMatBoring.length,
220 catSatOnMatBoring.length == 3);
221 assertTrue("cat-sat-on-mat- parent nodes should both be marked uninteresting except first: " +
222 catSatOnMatBoring[0]+','+catSatOnMatBoring[1]+','+catSatOnMatBoring[2],
223 (!catSatOnMatBoring[0]) && catSatOnMatBoring[1] && catSatOnMatBoring[2]);
224
225 }
226 }
227 }
228 }