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 * Created by IntelliJ IDEA.
031 * User: Administrator
032 * Date: 28-Dec-02
033 * Time: 22:24:51
034 * To change template for new class use
035 * Code Style | Class Templates options (Tools | IDE Options).
036 */
037 package org.hd.d.pg2k.test.dev;
038
039 import java.util.Collections;
040 import java.util.GregorianCalendar;
041 import java.util.HashSet;
042 import java.util.Iterator;
043 import java.util.Properties;
044 import java.util.Set;
045
046 import junit.framework.TestCase;
047
048 import org.hd.d.pg2k.ai.scorer.ScorerCacheIF;
049 import org.hd.d.pg2k.svrCore.AllExhibitImmutableData;
050 import org.hd.d.pg2k.svrCore.AllExhibitProperties;
051 import org.hd.d.pg2k.svrCore.ExhibitName;
052 import org.hd.d.pg2k.svrCore.ExhibitPropsComputable;
053 import org.hd.d.pg2k.svrCore.ExhibitPropsComputableMutable;
054 import org.hd.d.pg2k.svrCore.ExhibitPropsGlobalImmutable;
055 import org.hd.d.pg2k.svrCore.ExhibitPropsLoadable;
056 import org.hd.d.pg2k.svrCore.ExhibitStaticAttr;
057 import org.hd.d.pg2k.svrCore.Name;
058 import org.hd.d.pg2k.svrCore.datasource.ExhibitDataFileSource;
059 import org.hd.d.pg2k.svrCore.datasource.SimpleExhibitPipelineIF;
060 import org.hd.d.pg2k.svrCore.props.GenProps;
061 import org.hd.d.pg2k.svrCore.uploader.ExhibitHandlerBeanBase;
062 import org.hd.d.pg2k.svrCore.vars.SimpleVariableValue;
063 import org.hd.d.pg2k.svrCore.vars.SystemVariables;
064 import org.hd.d.pg2k.webSvr.catalogue.SearchPageJavaBean;
065 import org.hd.d.pg2k.webSvr.exhibit.BuiltInFilters;
066
067 /**Test of filtering of exhibits (eg for search page).
068 * Tests that the filters behave as expected.
069 */
070 public final class ExhibitFilterTest extends TestCase
071 {
072 public ExhibitFilterTest(final String name)
073 {
074 super(name);
075 }
076
077 // Our test data set; null when not running the tests to save memory...
078 private AllExhibitProperties aep;
079
080 // Our private empty but non-zero-timestamp GenProps set.
081 private GenProps gp;
082
083 // Names of some notable members of our test set that we look for specifically...
084 /**A small single-word-name JPEG by DHD with timestamp of now. */
085 private static final String EX_SMALL_DHD_JPEG_NOW = "example/example-DHD.jpg";
086 /**A small single-word GIF by DHD a fortnight old. */
087 private static final String EX_SMALL_DHD_GIF_2WOLD = "example/2wold-DHD.gif";
088 /**A small single-word HTML fragment by ANON from the dawn of timeS (1998). */
089 private static final String EX_SMALL_ANON_HTXT_1998 = "example/1998file-blah-blah-ANON.htxt";
090
091 /**Time this class was instantiated. */
092 private final static long now = System.currentTimeMillis();
093
094 /**Time representing 19980101. */
095 private static final long year1998 = (new GregorianCalendar(1998, 1, 1)).getTime().getTime();
096
097 /**Do any setup needed for the tests. */
098 @Override
099 protected void setUp()
100 {
101 aep = _makeAEP(null);
102 gp = new GenProps(new Properties(), System.currentTimeMillis());
103 }
104
105 /**Make our standard test AEP value. */
106 private static AllExhibitProperties _makeAEP(final AllExhibitProperties oldAEP)
107 {
108 final Set<ExhibitStaticAttr> esas = new HashSet<ExhibitStaticAttr>();
109 esas.add(new ExhibitStaticAttr(EX_SMALL_DHD_JPEG_NOW, 100, now));
110 esas.add(new ExhibitStaticAttr(EX_SMALL_DHD_GIF_2WOLD, 100, now - 14 * SearchPageJavaBean.DAY_MS));
111 esas.add(new ExhibitStaticAttr(EX_SMALL_ANON_HTXT_1998, 123, year1998));
112
113 return (new AllExhibitProperties(oldAEP,
114 new ExhibitPropsGlobalImmutable(),
115 new AllExhibitImmutableData(esas, now),
116 Collections.<Name.ExhibitFull, ExhibitPropsLoadable>emptyMap(),
117 Collections.<Name.ExhibitFull, ExhibitPropsComputable>emptyMap(),
118 0));
119 }
120
121 /**Do any clearup needed after the tests. */
122 @Override
123 protected void tearDown()
124 {
125 // cleanup code
126 aep = null;
127 gp = null;
128 }
129
130 /**Do some tests on evaluations of newness, goodness, etc, of exhibits using ExhibitPropsComputableMutable. */
131 public void testSimpleExhibitPropsComputableMutableExhibitInspection()
132 {
133 // Get details of new (right-up-to-date) exhibit.
134 final ExhibitStaticAttr esaNew = aep.aeid.getStaticAttr(EX_SMALL_DHD_JPEG_NOW);
135 final ExhibitPropsComputableMutable epcmNew =
136 ExhibitPropsComputableMutable.generateFastApproximation(esaNew, new GenProps());
137 // assertTrue("new exhibit must be seen as new", epcmNew.isExhibitNew());
138 assertTrue("new-exhibit goodness must be in [-1,+1] range",
139 (epcmNew.getGoodnessAsFloat() >= -1) &&
140 (epcmNew.getGoodnessAsFloat() <= +1));
141 // System.out.println("[Example 'new' simple goodness value: "+epcmNew.getGoodnessAsFloat()+" ("+epcmNew.getGoodness()+")]");
142 // Get details of old exhibit.
143 final ExhibitStaticAttr esaOld = aep.aeid.getStaticAttr(EX_SMALL_ANON_HTXT_1998);
144 final ExhibitPropsComputableMutable epcmOld =
145 ExhibitPropsComputableMutable.generateFastApproximation(esaOld, new GenProps());
146 // assertFalse("old exhibit must be seen as old", epcmOld.isExhibitNew());
147 assertTrue("old-exhibit goodness must be in [-1,+1] range",
148 (epcmOld.getGoodnessAsFloat() >= -1) &&
149 (epcmOld.getGoodnessAsFloat() <= +1));
150 // System.out.println("[Example 'old' simple goodness value: "+epcmOld.getGoodnessAsFloat()+" ("+epcmOld.getGoodness()+")]");
151 }
152
153 /**Simple tests of some built-in exhibit filters. */
154 public void testBuiltInFilters()
155 {
156 final BuiltInFilters.filtByCategory filtEXAMPLE = new BuiltInFilters.filtByCategory(new String[]{"example"});
157 assertTrue("should accept exhibit with correct category", filtEXAMPLE.accept(aep, Name.ExhibitFull.create(EX_SMALL_DHD_JPEG_NOW)));
158 assertFalse("should reject name too short to possibly match category", filtEXAMPLE.accept(aep, Name.ExhibitFull.create("e/e-E.e")));
159 final BuiltInFilters.filtByCategory filtEXAXXLE = new BuiltInFilters.filtByCategory(new String[]{"exaxxle"});
160 assertFalse("should reject exhibit with category of correct length but at least one differing char", filtEXAXXLE.accept(aep, Name.ExhibitFull.create(EX_SMALL_DHD_JPEG_NOW)));
161 final BuiltInFilters.filtByCategory filtEXAMPLES = new BuiltInFilters.filtByCategory(new String[]{"examples"});
162 assertFalse("should reject match with longer category whose prefix matches", filtEXAMPLES.accept(aep, Name.ExhibitFull.create(EX_SMALL_DHD_JPEG_NOW)));
163 final BuiltInFilters.filtByCategory filtOTHER = new BuiltInFilters.filtByCategory(new String[]{"other"});
164 assertFalse("should reject different category", filtOTHER.accept(aep, Name.ExhibitFull.create(EX_SMALL_DHD_JPEG_NOW)));
165
166 // TODO
167
168
169 }
170
171 /**Test differentials in "goodness" induced via GenProps.
172 * Tests that the smallest possible changes in static "goodness" attributes
173 * always show up in the goodness score and overwhelm any "random" factors.
174 * <p>
175 * Tests firstly for author weightings.
176 */
177 public void testSimpleExhibitPropsComputableMutableGoodnessDifferentials()
178 {
179 for(final Iterator<ExhibitStaticAttr> it = aep.aeid.getAllStaticAttrs().iterator(); it.hasNext(); )
180 {
181 final ExhibitStaticAttr esa = it.next();
182 // Calculate "base" goodness with empty GenProps.
183 final ExhibitPropsComputableMutable epcmBase =
184 ExhibitPropsComputableMutable.generateFastApproximation(esa, new GenProps());
185 final int gBase = epcmBase.getGoodness();
186
187 final Name.ExhibitFull fullName = esa.getExhibitFullName();
188 final String auth = ExhibitName.getAuthorComponent(fullName).toString();
189
190 // Compute "down" value with smallest increment down by author.
191 final Properties pDownA = new Properties();
192 pDownA.setProperty(GenProps.PPREFIX_POPWT_DETAILS + GenProps.PCOMP_POPWR_BYAUTH + auth,
193 "-1");
194 final ExhibitPropsComputableMutable epcmDownA =
195 ExhibitPropsComputableMutable.generateFastApproximation(esa, new GenProps(pDownA, 1));
196 final int gDownA = epcmDownA.getGoodness();
197 assertTrue("negative author weighting must reduce goodness from base value",
198 gDownA < gBase);
199
200 // Compute "up" value with smallest increment up by author.
201 final Properties pUpA = new Properties();
202 pUpA.setProperty(GenProps.PPREFIX_POPWT_DETAILS + GenProps.PCOMP_POPWR_BYAUTH + auth,
203 "1");
204 final ExhibitPropsComputableMutable epcmUpA =
205 ExhibitPropsComputableMutable.generateFastApproximation(esa, new GenProps(pUpA, 1));
206 final int gUpA = epcmUpA.getGoodness();
207 assertTrue("positive author weighting must increase goodness from base value",
208 gUpA > gBase);
209 }
210
211 // TODO: test by attribute...
212
213 // TODO: test by file type/extension...
214 }
215
216 /**Test that computable-mutable properties are preserved through serialisation.
217 * Note that (de)serialising the whole AEP may choose
218 * not to preserve this information as a matter of policy
219 * (to save transmission/storage space at the expense of CPU time).
220 */
221 public void testEPCMSer()
222 throws Exception
223 {
224 // We'll make sure that creating new instances of the AEP
225 // produces a new (slightly randomised) goodness each time,
226 // for all exhibits.
227
228 // Variable "source".
229 // final BasicVarMgrInterface vars = new BasicVarMgr(true);
230 final ExhibitDataFileSource edfs = new ExhibitDataFileSource(null);
231 final SimpleExhibitPipelineIF vars = edfs;
232
233 // Put in one vote pro and one con (albeit void) entry.
234 // We need to do this to keep vote/score computations happy!
235 vars.setVariable(new SimpleVariableValue(SystemVariables.VOTE_CON, null));
236 vars.setVariable(new SimpleVariableValue(SystemVariables.VOTE_PRO, null));
237
238 // Repeat test for all exhibits.
239 for(final Iterator<ExhibitStaticAttr> it = aep.aeid.getAllStaticAttrs().iterator(); it.hasNext(); )
240 {
241 final ExhibitStaticAttr esa = it.next();
242 final Name.ExhibitFull fullName = esa.getExhibitFullName();
243
244 // Check that when we ask for a full EPCM we don't get a trivially-stale one.
245 assertFalse("Must not get trivially-stale EPCM when told to do full calc",
246 aep.getExhibitPropsComputableMutable(fullName, false, gp, vars, ScorerCacheIF.TRIVIAL).isTriviallyStale());
247
248 // Show that retrieving EPCM from same AEP twice gets the same value.
249 // (Providing we don't force recomputation.)
250 assertEquals("EPCM goodness value must be same when fetched twice",
251 aep.getExhibitPropsComputableMutable(fullName, false, gp, vars, ScorerCacheIF.TRIVIAL).getGoodness(),
252 aep.getExhibitPropsComputableMutable(fullName, false, gp, vars, ScorerCacheIF.TRIVIAL).getGoodness());
253
254 // Show that we will get a different EPCM from a new identical AEP.
255 // (Because of random component.)
256 final AllExhibitProperties aep2 = _makeAEP(null);
257 assertNotSame("EPCM goodness values expected to be different from different AEP instances",
258 aep.getExhibitPropsComputableMutable(fullName, false, gp, vars, ScorerCacheIF.TRIVIAL).getGoodness(),
259 aep2.getExhibitPropsComputableMutable(fullName, false, gp, vars, ScorerCacheIF.TRIVIAL).getGoodness());
260
261 // Show that creating new AEP using old one for expensive computable state
262 // preserves that state providing that this was not a quick approximation value...
263 final AllExhibitProperties aepMakeWithOld = _makeAEP(aep);
264 assertEquals("EPCM goodness value must be same when fetched from new instance made using old",
265 aep.getExhibitPropsComputableMutable(fullName, false, gp, vars, ScorerCacheIF.TRIVIAL).getGoodness(),
266 aepMakeWithOld.getExhibitPropsComputableMutable(fullName, false, gp, vars, ScorerCacheIF.TRIVIAL).getGoodness());
267
268 // // Show that serialising and deserialising AEP preserves expensive computable state.
269 // // Only valid if the epcmMap field is not transient.
270 // final AllExhibitProperties aepDeser =
271 // (AllExhibitProperties) SerializationTest.checkSerialisationPreservesEquality(aep);
272 // assertEquals("EPCM goodness value must be same when fetched from (de)serialised copy",
273 // aep.getExhibitPropsComputableMutable(fullName, true, gp, vars).getGoodness(),
274 // aepDeser.getExhibitPropsComputableMutable(fullName, true, gp, vars).getGoodness());
275 }
276 }
277
278
279 /**Test aspects of filtering by the SearchPageJavaBean.
280 * Depends on default data set.
281 */
282 public void testSearchPageJavaBean()
283 {
284 final SearchPageJavaBean spjb1 = new SearchPageJavaBean();
285 spjb1.setAep(aep);
286
287 // Check that a new search bean returns no filter (null).
288 assertTrue("new search search bean does not not return null filter", spjb1.getSearchFilter() == null);
289
290 // Check by-author filtering.
291 spjb1.setAuthor("DHD");
292 assertTrue("expected author DHD, got: " + spjb1.getAuthor(), spjb1.getAuthor().equals("DHD"));
293 // Note that the accept() filter takes exhibit short names (without the dir path)
294 // or short persistent keys as of 200908XX.
295 // This will probably not work for exhibits not in our official exhibit set.
296 assertTrue(spjb1.getSearchFilter().accept("example-DHD.jpg"));
297 assertTrue(!spjb1.getSearchFilter().accept("a-ANON.jpg"));
298 assertTrue(spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_DHD_JPEG_NOW).toString()));
299 spjb1.setAuthor(ExhibitHandlerBeanBase.SETTER_ALL);
300 assertTrue("could not clear author, got: " + spjb1.getAuthor(), spjb1.getAuthor().equals(""));
301
302 // Check recent-additions filtering.
303 assertTrue("expected no recent-days filtering, got: " + spjb1.getRecentDaysFilter(), spjb1.getRecentDaysFilter() == 0);
304 spjb1.setRecentDaysFilter(ExhibitHandlerBeanBase.SETTER_ALL);
305 assertTrue("expected no recent-days filtering [2], got: " + spjb1.getRecentDaysFilter(), spjb1.getRecentDaysFilter() == 0);
306 // Check that each symbolic time value sets the correct number of days.
307 for(final Iterator<String> it = SearchPageJavaBean.symFilterDays.keySet().iterator(); it.hasNext(); )
308 {
309 final String symName = it.next();
310 spjb1.setRecentDaysFilter(symName);
311 assertTrue("failed to get right value for symbolic name: " + symName + " got: " + spjb1.getRecentDaysFilter(),
312 spjb1.getRecentDaysFilter() == (SearchPageJavaBean.symFilterDays.get(symName)).intValue());
313 }
314 // Now test that filtering threshold works correctly,
315 // especially trying to look for overflow of milliseconds in an int.
316 spjb1.setRecentDaysFilter("d"); // Filter on a week first.
317 assertTrue("failed to return new exhibit with d filter", spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_DHD_JPEG_NOW).toString()));
318 assertTrue("failed to reject 2w-old exhibit with d filter", !spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_DHD_GIF_2WOLD).toString()));
319 assertTrue("failed to reject 1998 exhibit with d filter", !spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_ANON_HTXT_1998).toString()));
320 // especially trying to look for overflow of milliseconds in an int.
321 spjb1.setRecentDaysFilter("7"); // Filter on a week first.
322 assertTrue("failed to return new exhibit with 7 filter", spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_DHD_JPEG_NOW).toString()));
323 assertTrue("failed to reject 2w-old exhibit with 7 filter", !spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_DHD_GIF_2WOLD).toString()));
324 assertTrue("failed to reject 1998 exhibit with 7 filter", !spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_ANON_HTXT_1998).toString()));
325 spjb1.setRecentDaysFilter("y"); // Filter on a week first.
326 assertTrue("failed to return new exhibit with y filter", spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_DHD_JPEG_NOW).toString()));
327 assertTrue("failed to return 2w-old exhibit with y filter", spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_DHD_GIF_2WOLD).toString()));
328 assertTrue("failed to reject 1998 exhibit with y filter", !spjb1.getSearchFilter().accept(ExhibitName.getFileComponent(EX_SMALL_ANON_HTXT_1998).toString()));
329 // Clear recent-days filtering.
330 spjb1.setRecentDaysFilter("0");
331 assertTrue("expected no recent-days filtering [3], got: " + spjb1.getRecentDaysFilter(), spjb1.getRecentDaysFilter() == 0);
332
333
334
335
336
337 // Check that a new search bean returns no filter (null).
338 assertTrue("used but cleaned-up search bean does not not return null filter", spjb1.getSearchFilter() == null);
339
340 }
341 }