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.io.File;
033 import java.util.Arrays;
034 import java.util.Collections;
035 import java.util.HashSet;
036 import java.util.List;
037 import java.util.Properties;
038 import java.util.Set;
039
040 import junit.framework.TestCase;
041
042 import org.hd.d.pg2k.svrCore.AllExhibitProperties;
043 import org.hd.d.pg2k.svrCore.CS8Bit;
044 import org.hd.d.pg2k.svrCore.ExhibitName;
045 import org.hd.d.pg2k.svrCore.FileTools;
046 import org.hd.d.pg2k.svrCore.GenUtils;
047 import org.hd.d.pg2k.svrCore.Name;
048 import org.hd.d.pg2k.svrCore.Name.ExhibitFull;
049 import org.hd.d.pg2k.svrCore.TextUtils;
050 import org.hd.d.pg2k.svrCore.props.GenProps;
051 import org.hd.d.pg2k.svrCore.props.GenPropsGenNames;
052
053 /**
054 * Created by IntelliJ IDEA.
055 * User: Damon Hart-Davis
056 * Date: 27-Jul-2003
057 * Time: 17:37:14
058 */
059
060 /**Tests various features of exhibit name syntax, parsing, etc.
061 * This should be extended to test all the various conversions and parses
062 * and comparators from ExhibitName and Name.
063 */
064 public final class ExhibitNameTest extends TestCase
065 {
066 /**Test the "valid syntax" methods.
067 * Created after enabling assert()s revealed internal inconsistencies.
068 */
069 public void testValidSyntaxMethods()
070 {
071 final String minFinalNameComponent = "b-C.d";
072 final String minName ="a/" + minFinalNameComponent;
073
074 // Check minimal valid name is regarded as valid.
075 assertTrue("Minimal valid final name component must be seen as valid",
076 ExhibitName.validNameFinalComponentSyntax(minFinalNameComponent));
077 assertTrue("Minimal valid name must be seen as valid by ExhibitName.validNameSyntaxBasic()",
078 ExhibitName.validNameSyntaxBasic(minName));
079 assertTrue("Minimal valid name must be seen as valid by ExhibitName.validNameSyntax()",
080 ExhibitName.validNameSyntax(minName));
081 }
082
083 /**Test some extraction/comparison methods.
084 * Initially created after enabling assert()s revealed internal inconsistencies.
085 */
086 public void testExtractionMethods()
087 {
088 final String text = "z/e-W.q";
089 final Name.ExhibitFull full = Name.ExhibitFull.create(text);
090 assertEquals(text, full.toString());
091 final byte[] asBytes = full.toByteArray();
092 final int offset = 3;
093 final byte[] offsetBytes = new byte[39];
094 full.writeToByteArray(offsetBytes, offset);
095 for(int i = text.length(); --i >= 0; )
096 {
097 assertEquals((char) asBytes[i], text.charAt(i));
098 assertEquals((char) offsetBytes[i + offset], text.charAt(i));
099 }
100 assertEquals(0, full.compareTo(full));
101 assertTrue(full.compareTo(Name.ExhibitFull.create("a/e-W.q")) > 0);
102 assertTrue(Name.ExhibitFull.create("a/e-W.q").compareTo(full) < 0);
103 assertTrue(full.compareTo(Name.ExhibitFull.create("z/e-W.qq")) < 0);
104 assertTrue(Name.ExhibitFull.create("z/e-W.qq").compareTo(full) > 0);
105 // Some short name extraction.
106 assertEquals(0, full.getShortName().compareTo(full.getShortName()));
107 assertEquals(0, full.getShortName().compareTo(Name.ExhibitFull.create("somethingelse/e-W.q").getShortName()));
108 // Some virtual name extraction.
109 assertEquals(full, full.getVirtualExhibitName());
110 assertEquals(full, Name.ExhibitFull.create("z/_more2012/_more01/e-W.q").getVirtualExhibitName());
111 }
112
113 /**Test generation of "virtual" names.
114 * Simple tests that names are "virtualised" correctly.
115 */
116 public void testVirtualNameGeneration()
117 {
118 assertEquals("Already-virtual name must be returned as-is",
119 Name.ExhibitFull.create("a/b-C.d"),
120 Name.ExhibitFull.create("a/b-C.d").getVirtualExhibitName());
121 assertEquals("Name must be correctly virtualised",
122 Name.ExhibitFull.create("a/B-C.def"),
123 Name.ExhibitFull.create("a/_more1999/_more01/B-C.def").getVirtualExhibitName());
124 // And while we're at it confirm that going via toByteArray() and toString() doesn't break anything.
125 assertEquals("Name must be correctly virtualised",
126 Name.ExhibitFull.create("a/B-C.def").toString(),
127 (new CS8Bit(Name.ExhibitFull.create("a/_more1999/_more01/B-C.def").getVirtualExhibitName().toByteArray())).toString());
128 }
129
130 /**Test extraction of main-words component of a name.
131 * Does some very basic testing of the ability to extract the main words.
132 * <p>
133 * These tests are entirely syntactic,
134 * ie do not require particular exhibit names or meanings.
135 */
136 public void testMainWordExtraction()
137 {
138 // Test set of attribute words, were used.
139 final Set<String> attrWords = new HashSet<String>(Arrays.asList(
140 new String[]{ "mono", "false", "colour", "rotated" }));
141
142 // Set of values to test:
143 // * Column 1 is the exhibit names.
144 // * Column 2 is the main words fragment expected with no attribute words.
145 // * Column 3 is the main words fragment expected with attrWords.
146 final String testData[][] =
147 {
148 { "a/b-C.d", "b", "b" },
149 { "a/b-3-C.d", "b", "b" },
150 { "a/b-mono-3-C.d", "b-mono", "b" },
151 { "a/b-false-colour-05-C.d", "b-false-colour", "b" },
152 { "a/b-dog-Cat-zoo-apple-false-colour-05-C.d", "b-dog-Cat-zoo-apple-false-colour", "b-dog-Cat-zoo-apple" },
153 };
154
155 // Work through all the test cases.
156 for(int i = testData.length; --i >= 0; )
157 {
158 final String exhibitName = testData[i][0];
159 final String noAWParse = testData[i][1];
160 final String aWParse = testData[i][2];
161 final Set<String> noAttrWords = Collections.emptySet();
162 final String actualNoAWParse = ExhibitName.getMainWordsComponent(exhibitName, noAttrWords).toString();
163 assertTrue("unexpected results without attr words: got `"+actualNoAWParse+"', expected `"+noAWParse+"'",
164 noAWParse.equals(actualNoAWParse));
165 final String acutalAWParse = ExhibitName.getMainWordsComponent(exhibitName, attrWords).toString();
166 assertTrue("unexpected results with attr words: got `"+acutalAWParse+"', expected `"+aWParse+"'",
167 aWParse.equals(acutalAWParse));
168 }
169 }
170
171 /**Test isSensitive() exhibit-flagging mechanism. */
172 public void testIsSensitive()
173 {
174 final String SENSITIVE_PHRASE = "-Monty-Python-";
175
176 // Synthetic sensitive name.
177 final String sensName = "memes/some-prefix" + SENSITIVE_PHRASE + "some-suffix-ANON.jpg";
178
179 // Set up minimal GP with a test "sensitive" value.
180 final Properties p = new Properties();
181 p.setProperty(GenPropsGenNames.GEN_PREFIX + GenPropsGenNames.GPGEN_SENSITIVE_NAME_SUBSTRS_KEY, SENSITIVE_PHRASE);
182 final GenProps gp = new GenProps(p, System.currentTimeMillis());
183
184 // Check sensitive values against default GP.
185 assertTrue("Sensitive exhibit name must be flagged as such: " + sensName, GenUtils.isSensitive(sensName, gp));
186 assertFalse("Non-sensitive exhibit name must not be flagged as sensitive", GenUtils.isSensitive("a/a-A.a", gp));
187 }
188
189 /**Test sane (internal) behaviour of Name on real AEP data.
190 * This is white-box testing.
191 */
192 public static void testNameSharing()
193 {
194 // To verify some internal parameters, invent a couple of unique names here.
195 final Name.ExhibitFull nufn1 = Name.ExhibitFull.createNoIntern("abcdefghq/a-B.c", null);
196 Main.getOut().println(nufn1.showInternalStructure());
197 assertEquals("must not share any state with any other name", 0, nufn1.getPrevChainLength());
198 assertEquals("must not share any state with any other name", 0, nufn1.getPrefixLen());
199 final Name.ExhibitFull nufn2 = Name.ExhibitFull.createNoIntern("abcdefghq/d-E.f", nufn1);
200 Main.getOut().println(nufn2.showInternalStructure());
201 assertEquals("must link to the one previous name above", 1, nufn2.getPrevChainLength());
202 assertEquals("must share a maximal prefix with the previous name", 10, nufn2.getPrefixLen());
203 final Name.ExhibitFull nufn3 = Name.ExhibitFull.createNoIntern("abcdefghq/de-E.f", nufn2);
204 Main.getOut().println(nufn3.showInternalStructure());
205 assertEquals("must link to the previous names above", 2, nufn3.getPrevChainLength());
206 assertEquals("must share a maximal prefix with the previous name", 11, nufn3.getPrefixLen());
207 assertEquals("should share a reasonable suffix with the previous name", 4, nufn3.getSuffixLen());
208 }
209
210 /**Test correct behaviour of Name on real AEP data. */
211 public static void testNameOnAEP()
212 throws Exception
213 {
214 // Test performance on a realistic exhibit-name data set.
215 final File lastAEPFile = new File(BackCompatTest.frozenAEPs.get(BackCompatTest.frozenAEPs.size()-1));
216
217 Main.getOut().println("Using captured AEP " + lastAEPFile);
218 final AllExhibitProperties lastAEP =
219 (AllExhibitProperties) FileTools.deserialiseFromFile(lastAEPFile, true);
220
221 // Check that short names are extracted correctly with getShortName();
222 assertEquals("cd-E.f", Name.ExhibitFull.create("b/cd-E.f").getShortName().toString());
223 assertEquals("cd-E.f", Name.ExhibitFull.create("ab/cd-E.f").getShortName().toString());
224 assertEquals("cd-E.fg", Name.ExhibitFull.create("ab/cd-E.fg").getShortName().toString());
225 final List<ExhibitFull> allExhibitNamesSorted = lastAEP.aeid.getAllExhibitNamesSorted();
226 for(final Name.ExhibitFull fn : allExhibitNamesSorted)
227 { assertTrue(TextUtils.contentEquals(ExhibitName.getFileComponent(fn), fn.getShortName())); }
228
229 // Check that virtual names are extracted correctly with getVirtualExhibitName();
230 assertEquals("b/cd-E.f", Name.ExhibitFull.create("b/cd-E.f").getVirtualExhibitName().toString());
231 assertEquals("ab/cd-E.f", Name.ExhibitFull.create("ab/cd-E.f").getVirtualExhibitName().toString());
232 assertEquals("ab/cd-E.fg", Name.ExhibitFull.create("ab/cd-E.fg").getVirtualExhibitName().toString());
233 assertEquals("ab/cd-E.fg", Name.ExhibitFull.create("ab/_more2001/_more01/cd-E.fg").getVirtualExhibitName().toString());
234 for(final Name.ExhibitFull fn : allExhibitNamesSorted)
235 { assertTrue(TextUtils.contentEquals(ExhibitName.getCategoryComponent(fn) + "/" + fn.getShortName(), fn.getVirtualExhibitName())); }
236
237 // Check that all persistableKey() values are actually unique (as String values),
238 // for this recent AEP instance.
239 final Set<String> pKeys = new HashSet<String>(lastAEP.aeid.length * 2);
240 for(final Name.ExhibitFull fn : allExhibitNamesSorted)
241 {
242 final CharSequence pKey = fn.getShortName().persistableKey();
243 // Check that key is pure-printable-ASCII and of the right length, etc.
244 assertTrue(pKey.length() == Name.ExhibitShort.PERSISTABLE_NAME_LENGTH);
245 assertTrue(pKey.charAt(0) == Name.ExhibitShort.PERSISTABLE_NAME_LEADING_CHAR);
246 for(int i = pKey.length(); --i >= 0; )
247 {
248 final char c = pKey.charAt(i);
249 assertTrue(c > ' ');
250 assertTrue(c < 127);
251 }
252 assertTrue("each persistableKey value should be unique", pKeys.add(pKey.toString()));
253 assertEquals("should be able to look up full name from persistable key", fn, lastAEP.aeid.getFullNameFromPersistableKey(pKey));
254 assertEquals("should be able to look up full name from persistable key as-is", fn, lastAEP.aeid.getFullNameFromPersistableKey(pKey));
255 assertEquals("should be able to look up full name from persistable key as String", fn, lastAEP.aeid.getFullNameFromPersistableKey(pKey.toString()));
256 assertEquals("should be able to look up full name from persistable key as Name", fn, lastAEP.aeid.getFullNameFromPersistableKey(Name.create(pKey)));
257 }
258
259 final int nameCount = allExhibitNamesSorted.size();
260 final String[] allStringExhibitNamesSorted = new String[nameCount];
261 for(int i = nameCount; --i >= 0; )
262 { allStringExhibitNamesSorted[i] = allExhibitNamesSorted.get(i).toString(); }
263 byte[] rawStringsSer = SerializationTest.serialiseToByteArray(allStringExhibitNamesSorted);
264 final int rawStringsSerLen = rawStringsSer.length;
265 final int rawStringsSerCompressedLen = GenUtils.compressData(rawStringsSer, GenUtils.MAX_SUPPORTED_COMPRESSION_LEVEL, false).second.length;
266 Main.getOut().println("Serialised in-order String array bytes raw | compressed: " + rawStringsSerLen + " | " + rawStringsSerCompressedLen);
267 // Find out how small a compressed representation we can make...
268 rawStringsSer = null; // Release resources.
269
270 final long startTime = System.currentTimeMillis();
271
272 // Compute best 'from-fresh' set of ExhibitFull values.
273 final Name[] compactExhibitNamesSorted = new Name[nameCount];
274 int maxPrevChainLength = 0;
275 long totalChars = 0;
276 long savedChars = 0;
277 Name prev = null;
278 for(int i = 0; i < nameCount; ++i)
279 {
280 final String s = allStringExhibitNamesSorted[i];
281 final Name out = /* allExhibitNamesSorted.get(i); */ Name.ExhibitFull.createNoIntern(s, prev);
282 totalChars += out.length();
283 compactExhibitNamesSorted[i] = out;
284 final int savedThisTime = out.getPrefixLen() + out.getSuffixLen();
285 //if(savedThisTime == 0) { System.err.println("Unable to save any chars from "+out+" with prev "+prev); }
286 savedChars += savedThisTime;
287 if(out.getPrevChainLength() > maxPrevChainLength) { maxPrevChainLength = out.getPrevChainLength(); }
288 //if(s.length() > 256) { Main.getOut().println("LONG NAME: "+ s); }
289 prev = out;
290 }
291 Main.getOut().println("'Saved' prefix chars (from "+nameCount+" names) vs total = "+savedChars+" vs "+totalChars+" => "+(savedChars/nameCount)+"chars/entry vs average "+(totalChars/nameCount)+"chars/entry");
292 Main.getOut().println("max prevChainLength = "+ maxPrevChainLength);
293
294 byte[] compactStringsSer = SerializationTest.serialiseToByteArray(compactExhibitNamesSorted);
295 final int compactStringsSerLen = compactStringsSer.length;
296 final int compactStringsSerCompressedLen = GenUtils.compressData(compactStringsSer, GenUtils.MAX_SUPPORTED_COMPRESSION_LEVEL, false).second.length;
297 Main.getOut().println("Serialised in-order compact representation array bytes raw | compressed: " + compactStringsSerLen + " | " + compactStringsSerCompressedLen);
298 compactStringsSer = null; // Release resources.
299
300 assertTrue("Raw compact form should be no larger on-the-wire than raw simple String form", compactStringsSerLen <= rawStringsSerLen);
301 assertTrue("Compressed compact form should not be larger on-the-wire than raw simple String form", compactStringsSerCompressedLen <= rawStringsSerCompressedLen);
302
303 final long endTime = System.currentTimeMillis();
304 Main.getOut().println("Run time to rebuild: " + (endTime-startTime) + "ms.");
305 }
306
307 /**Simple test of comparator to exercise all branches. */
308 public static void testNameCompareTo()
309 {
310 assertTrue(Name.create("bat").compareTo(Name.create("bat")) == 0);
311 assertTrue(Name.create("bat").compareTo(Name.create("cat")) < 0);
312 assertTrue(Name.create("cat").compareTo(Name.create("bat")) > 0);
313 assertTrue(Name.create("battle").compareTo(Name.create("cat")) < 0);
314 assertTrue(Name.create("battle").compareTo(Name.create("cattle")) < 0);
315 assertTrue(Name.create("cat").compareTo(Name.create("cattle")) < 0);
316
317 assertTrue(Name.ExhibitFull.create("a/bat-A.a").compareTo(Name.ExhibitFull.create("a/bat-A.a")) == 0);
318 assertTrue(Name.ExhibitFull.create("a/bat-A.a").compareTo(Name.ExhibitFull.create("a/cat-A.a")) < 0);
319 assertTrue(Name.ExhibitFull.create("a/cat-A.a").compareTo(Name.ExhibitFull.create("a/bat-A.a")) > 0);
320 final ExhibitFull n1 = Name.ExhibitFull.create("a/bat-A.aa");
321 assertTrue(n1.compareTo(Name.ExhibitFull.create("a/cat-A.a")) < 0);
322 final ExhibitFull n2 = Name.ExhibitFull.create("a/cat-A.aa", n1);
323 assertTrue(n1.compareTo(n2) < 0);
324 assertTrue(Name.ExhibitFull.create("a/cat-A.a").compareTo(n2) < 0);
325 }
326 }