From 590844e775f322377fe9d9dbb7137ed465d625d9 Mon Sep 17 00:00:00 2001 From: Jan Materne Date: Sat, 10 Jul 2004 17:15:37 +0000 Subject: [PATCH] Small redesign of ModifiedSelector. Integrating BugIDs 29742+29743. Adding support of custom classes. Checkstyle. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276688 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/CoreTypes/selectors.html | 73 +- src/etc/testcases/types/selectors.xml | 15 + .../modifiedselector/DigestAlgorithm.java | 9 +- .../modifiedselector/ModifiedSelector.java | 400 ++++++-- .../modifiedselector/PropertiesfileCache.java | 22 +- .../ant/types/selectors/MockAlgorithm.java | 36 + .../tools/ant/types/selectors/MockCache.java | 67 ++ .../ant/types/selectors/MockComparator.java | 31 + .../types/selectors/ModifiedSelectorTest.java | 927 ++++++++++++------ 9 files changed, 1209 insertions(+), 371 deletions(-) create mode 100644 src/testcases/org/apache/tools/ant/types/selectors/MockAlgorithm.java create mode 100644 src/testcases/org/apache/tools/ant/types/selectors/MockCache.java create mode 100644 src/testcases/org/apache/tools/ant/types/selectors/MockComparator.java diff --git a/docs/manual/CoreTypes/selectors.html b/docs/manual/CoreTypes/selectors.html index c2fed1aef..10798c18d 100755 --- a/docs/manual/CoreTypes/selectors.html +++ b/docs/manual/CoreTypes/selectors.html @@ -2,7 +2,7 @@ Selectors in Ant - + @@ -652,6 +652,7 @@ No, defaults to digest @@ -672,11 +673,35 @@ Acceptable values are (further information see later): No, defaults to equal + + algorithmclass + Classname of custom algorithm implementation. Lower + priority than algorithm. + (see note for restrictions) + No + + + cacheclass + Classname of custom cache implementation. Lower + priority than cache. + (see note for restrictions) + No + + + comparatorclass + Classname of custom comparator implementation. Lower + priority than comparator. + (see note for restrictions) + No + update Should the cache be updated when values differ? (boolean) @@ -687,6 +712,15 @@ Should directories be selected? (boolean) No, defaults to true + + delayupdate + If set to true, the storage of the cache will be delayed until the + next finished BuildEvent; task finished, target finished or build finished, + whichever comes first. This is provided for increased performance. If set + to false, the storage of the cache will happen with each change. This + attribute depends upon the update attribute. (boolean) + No, defaults to true +

These attributes can be set with nested <param/> tags. With <param/> @@ -695,6 +729,9 @@

  • algorithm : same as attribute algorithm
  • cache : same as attribute cache
  • comparator : same as attribute comparator
  • +
  • algorithmclass : same as attribute algorithmclass
  • +
  • cacheclass : same as attribute cacheclass
  • +
  • comparatorclass : same as attribute comparatorclass
  • update : same as attribute update
  • seldirs : same as attribute seldirs
  • algorithm.* : Value is transfered to the algorithm via its @@ -729,6 +766,16 @@ + + checksum + Uses java.util.zip.Checksum. This Algorithm supports + the following attributes: + + + Cache's propertyfile @@ -750,6 +797,8 @@ rule Uses java.text.RuleBasedCollator for Object comparison. + + (see note for restrictions) @@ -820,6 +869,24 @@ CacheSelector saves therefore much upload time.

    + + (see note for restrictions) + +

    Note on RuleBasedCollator

    +

    The RuleBasedCollator needs a format for its work, but its needed while + instantiation. There is a problem in the initialization algorithm for this + case. Therefore you should not use this (or tell me the workaround :-).

    + +

    The selector can not find the specified algorithm-, cache- or comparator- + class if the selector is loaded from a different classloader. + To be able to use your own classes you have to ensure that the selector + can find the classes by adding the your package to the core:

    + +

    Selector Containers

    @@ -1216,4 +1283,4 @@ - + \ No newline at end of file diff --git a/src/etc/testcases/types/selectors.xml b/src/etc/testcases/types/selectors.xml index 0644ac57b..4165f62f5 100644 --- a/src/etc/testcases/types/selectors.xml +++ b/src/etc/testcases/types/selectors.xml @@ -242,4 +242,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java b/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java index 8cfe8d68c..5c52f5c6c 100644 --- a/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java +++ b/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java @@ -51,7 +51,7 @@ import org.apache.tools.ant.BuildException; * * * - * @version 2003-09-13 + * @version 2004-07-08 * @since Ant 1.6 */ public class DigestAlgorithm implements Algorithm { @@ -132,12 +132,11 @@ public class DigestAlgorithm implements Algorithm { /** - * This algorithm doesn't need any configuration. - * Therefore it's always valid. + * This algorithm supports only MD5 and SHA. * @return true if all is ok, otherwise false. */ public boolean isValid() { - return true; + return "SHA".equalsIgnoreCase(algorithm) || "MD5".equalsIgnoreCase(algorithm); } @@ -200,4 +199,4 @@ public class DigestAlgorithm implements Algorithm { buf.append(">"); return buf.toString(); } -} +} \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java b/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java index ae7e0f123..dae5947a0 100644 --- a/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java +++ b/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java @@ -27,6 +27,9 @@ import java.io.File; // Ant import org.apache.tools.ant.Project; import org.apache.tools.ant.IntrospectionHelper; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.BuildListener; +import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.types.Parameter; import org.apache.tools.ant.types.selectors.BaseExtendSelector; @@ -93,7 +96,7 @@ import org.apache.tools.ant.types.selectors.BaseExtendSelector; * comparison.

    * *

    A useful scenario for this selector is inside a build environment - * for homepage generation (e.g. with + * for homepage generation (e.g. with * Apache Forrest).

      * 
      *      generate the site using forrest 
    @@ -109,7 +112,7 @@ import org.apache.tools.ant.types.selectors.BaseExtendSelector;
      * 
    Here all changed files are uploaded to the server. The * ModifiedSelector saves therefore much upload time.

    * - *

    This selector supports the following nested param's: + *

    This selector supports the following attributes: * * * @@ -122,20 +125,21 @@ import org.apache.tools.ant.types.selectors.BaseExtendSelector; * * * - * + * * * * * * - * + * * * * @@ -153,6 +157,34 @@ import org.apache.tools.ant.types.selectors.BaseExtendSelector; * * * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * * * * @@ -182,23 +214,32 @@ import org.apache.tools.ant.types.selectors.BaseExtendSelector; * a nested . * * - * @version 2003-09-13 + * @version 2004-07-09 * @since Ant 1.6 -*/ -public class ModifiedSelector extends BaseExtendSelector { + */ +public class ModifiedSelector extends BaseExtendSelector implements BuildListener { - // ----- member variables - configuration + // ----- attributes ----- - /** The Cache containing the old values. */ - private Cache cache = null; + /** Cache name for later instantiation. */ + private CacheName cacheName = null; - /** Algorithm for computing new values and updating the cache. */ - private Algorithm algorithm = null; + /** User specified classname for Cache. */ + private String cacheClass; - /** How should the cached value and the new one compared? */ - private Comparator comparator = null; + /** Algorithm name for later instantiation. */ + private AlgorithmName algoName = null; + + /** User specified classname for Algorithm. */ + private String algorithmClass; + + /** Comparator name for later instantiation. */ + private ComparatorName compName = null; + + /** User specified classname for Comparator. */ + private String comparatorClass; /** Should the cache be updated? */ private boolean update = true; @@ -206,22 +247,27 @@ public class ModifiedSelector extends BaseExtendSelector { /** Are directories selected? */ private boolean selectDirectories = true; + /** Delay the writing of the cache file */ + private boolean delayUpdate = true; - // ----- member variables - internal use + // ----- internal member variables ----- - /** Flag whether this object is configured. Configuration is only done once. */ - private boolean isConfigured = false; - /** Algorithm name for later instantiation. */ - private AlgorithmName algoName = null; + /** How should the cached value and the new one compared? */ + private Comparator comparator = null; - /** Cache name for later instantiation. */ - private CacheName cacheName = null; + /** Algorithm for computing new values and updating the cache. */ + private Algorithm algorithm = null; - /** Comparator name for later instantiation. */ - private ComparatorName compName = null; + /** The Cache containing the old values. */ + private Cache cache = null; + /** Count of modified properties */ + private int modified = 0; + + /** Flag whether this object is configured. Configuration is only done once. */ + private boolean isConfigured = false; /** * Parameter vector with parameters for later initialization. @@ -293,23 +339,26 @@ public class ModifiedSelector extends BaseExtendSelector { // // ----- Set default values ----- // - org.apache.tools.ant.Project project = getProject(); + Project project = getProject(); String filename = "cache.properties"; File cachefile = null; if (project != null) { // normal use inside Ant cachefile = new File(project.getBaseDir(), filename); + + // set self as a BuildListener to delay cachefile saves + getProject().addBuildListener(this); } else { - // no reference to project - e.g. during JUnit tests + // no reference to project - e.g. during normal JUnit tests cachefile = new File(filename); + setDelayUpdate(false); } - cache = new PropertiesfileCache(cachefile); - algorithm = new DigestAlgorithm(); - comparator = new EqualComparator(); + Cache defaultCache = new PropertiesfileCache(cachefile); + Algorithm defaultAlgorithm = new DigestAlgorithm(); + Comparator defaultComparator = new EqualComparator(); update = true; selectDirectories = true; - // // ----- Set the main attributes, pattern '*' ----- // @@ -328,54 +377,70 @@ public class ModifiedSelector extends BaseExtendSelector { // ----- Instantiate the interfaces ----- // String className = null; - String pkg = "org.apache.tools.ant.types.selectors.cacheselector"; + String pkg = "org.apache.tools.ant.types.selectors.modifiedselector"; - // the algorithm - if (algorithm == null) { + // specify the algorithm classname + if (algoName != null) { + // use Algorithm defined via name if ("hashvalue".equals(algoName.getValue())) { - className = pkg + ".HashvalueAlgorithm"; + algorithm = new HashvalueAlgorithm(); } else if ("digest".equals(algoName.getValue())) { - className = pkg + ".DigestAlgorithm"; + algorithm = new DigestAlgorithm(); + } else if ("checksum".equals(algoName.getValue())) { + algorithm = new ChecksumAlgorithm(); } - if (className != null) { - try { - // load the specified Algorithm, save the reference and configure it - algorithm = (Algorithm) Class.forName(className).newInstance(); - } catch (Exception e) { - e.printStackTrace(); - } + } else { + if (algorithmClass != null) { + // use Algorithm specified by classname + algorithm = (Algorithm) loadClass( + algorithmClass, + "is not an Algorithm.", + Algorithm.class); + } else { + // nothing specified - use default + algorithm = defaultAlgorithm; } } - // the cache - if (cache == null) { + // specify the cache classname + if (cacheName != null) { + // use Cache defined via name if ("propertyfile".equals(cacheName.getValue())) { - className = pkg + ".PropertiesfileCache"; + cache = new PropertiesfileCache(); } - if (className != null) { - try { - // load the specified Cache, save the reference and configure it - cache = (Cache) Class.forName(className).newInstance(); - } catch (Exception e) { - e.printStackTrace(); - } + } else { + if (cacheClass != null) { + // use Cache specified by classname + cache = (Cache) loadClass(cacheClass, "is not a Cache.", Cache.class); + } else { + // nothing specified - use default + cache = defaultCache; } } - // the comparator - if (comparator == null) { + // specify the comparator classname + if (compName != null) { + // use Algorithm defined via name if ("equal".equals(compName.getValue())) { - className = pkg + ".EqualComparator"; - } else if ("role".equals(compName.getValue())) { - className = "java.text.RuleBasedCollator"; + comparator = new EqualComparator(); + } else if ("rule".equals(compName.getValue())) { + // TODO there is a problem with the constructor for the RBC. + // you have to provide the rules in the constructors - no setters + // available. + throw new BuildException("RuleBasedCollator not yet supported."); + // Have to think about lazy initialization here... JHM + // comparator = new java.text.RuleBasedCollator(); } - if (className != null) { - try { - // load the specified Cache, save the reference and configure it - comparator = (Comparator) Class.forName(className).newInstance(); - } catch (Exception e) { - e.printStackTrace(); - } + } else { + if (comparatorClass != null) { + // use Algorithm specified by classname + comparator = (Comparator) loadClass( + comparatorClass, + "is not a Comparator.", + Comparator.class); + } else { + // nothing specified - use default + comparator = defaultComparator; } } @@ -390,6 +455,47 @@ public class ModifiedSelector extends BaseExtendSelector { } + /** + * Loads the specified class and initializes an object of that class. + * Throws a BuildException using the given message if an error occurs during + * loading/instantiation or if the object is not from the given type. + * @param classname the classname + * @param msg the message-part for the BuildException + * @param type the type to check against + * @return a castable object + */ + protected Object loadClass(String classname, String msg, Class type) { + try { + // load the specified class + + /* TODO: the selector cant find the specified class if the + * selector is loaded from a different classloader. + * See ModifiedSelectorTest.testCustom(). + * To be able to run these tests you have to ensure that can find + * the classes by adding the test package to the core: + * - by placing the ant-testutils.jar in %ANT_HOME/lib + * - by adding '-lib build/testcases' while invocation + * + * IMO this is not only a problem for the Mock-Classes inside the + * tests. The *classname attributes are designed for the user to + * provide his own implementations. Therefore they should be + * found ... Workaround again: -lib, ~/.ant/lib, ant.home/lib + * JHM + */ + Object rv = Class.forName(classname).newInstance(); + if (!type.isInstance(rv)) { + throw new BuildException("Specified class (" + classname + ") " + msg); + } + return rv; + } catch (ClassNotFoundException e) { + throw new BuildException("Specified class (" + classname + ") not found."); + } catch (Exception e) { + throw new BuildException(e); + } + } + + + // ----- the selection work ----- @@ -412,21 +518,63 @@ public class ModifiedSelector extends BaseExtendSelector { // Get the values and do the comparison String cachedValue = String.valueOf(cache.get(f.getAbsolutePath())); String newValue = algorithm.getValue(f); + boolean rv = (comparator.compare(cachedValue, newValue) != 0); // Maybe update the cache - if (update && !cachedValue.equals(newValue)) { + if (update && rv) { cache.put(f.getAbsolutePath(), newValue); - cache.save(); + setModified(getModified() + 1); + if (!getDelayUpdate()) { + saveCache(); + } } return rv; } + /** + * save the cache file + */ + protected void saveCache() { + if (getModified() > 1) { + cache.save(); + setModified(0); + } + } + + // ----- attribute and nested element support ----- + /** + * Setter for algorithmClass. + * @param classname new value + */ + public void setAlgorithmClass(String classname) { + algorithmClass = classname; + } + + + /** + * Setter for comparatorClass. + * @param classname new value + */ + public void setComparatorClass(String classname) { + comparatorClass = classname; + } + + + /** + * Setter for cacheClass. + * @param classname new value + */ + public void setCacheClass(String classname) { + cacheClass = classname; + } + + /** * Support for update attribute. * @param update new value @@ -445,6 +593,42 @@ public class ModifiedSelector extends BaseExtendSelector { } + /** + * Getter for the modified count + * @return modified count + */ + public int getModified() { + return modified; + } + + + /** + * Setter for the modified count + * @param modified count + */ + public void setModified(int modified) { + this.modified = modified; + } + + + /** + * Getter for the delay update + * @return true if we should delay for performance + */ + public boolean getDelayUpdate() { + return delayUpdate; + } + + + /** + * Setter for the delay update + * @param delayUpdate true if we should delay for performance + */ + public void setDelayUpdate(boolean delayUpdate) { + this.delayUpdate = delayUpdate; + } + + /** * Support for nested <param> tags. * @param key the key of the parameter @@ -516,6 +700,12 @@ public class ModifiedSelector extends BaseExtendSelector { ? true : false; setUpdate(updateValue); + } else if ("delayupdate".equals(key)) { + boolean updateValue = + ("true".equalsIgnoreCase(value)) + ? true + : false; + setDelayUpdate(updateValue); } else if ("seldirs".equals(key)) { boolean sdValue = ("true".equalsIgnoreCase(value)) @@ -548,7 +738,6 @@ public class ModifiedSelector extends BaseExtendSelector { Project prj = (getProject() != null) ? getProject() : new Project(); IntrospectionHelper iHelper = IntrospectionHelper.getHelper(prj, obj.getClass()); - try { iHelper.setAttribute(prj, obj, name, value); } catch (org.apache.tools.ant.BuildException e) { @@ -576,6 +765,79 @@ public class ModifiedSelector extends BaseExtendSelector { } + // ----- BuildListener interface methods ----- + + + /** + * Signals that the last target has finished. + * @param event recieved BuildEvent + */ + public void buildFinished(BuildEvent event) { + if (getDelayUpdate()) { + saveCache(); + } + } + + + /** + * Signals that a target has finished. + * @param event recieved BuildEvent + */ + public void targetFinished(BuildEvent event) { + if (getDelayUpdate()) { + saveCache(); + } + } + + + /** + * Signals that a task has finished. + * @param event recieved BuildEvent + */ + public void taskFinished(BuildEvent event) { + if (getDelayUpdate()) { + saveCache(); + } + } + + + /** + * Signals that a build has started. + * @param event recieved BuildEvent + */ + public void buildStarted(BuildEvent event) { + // no-op + } + + + /** + * Signals that a target is starting. + * @param event recieved BuildEvent + */ + public void targetStarted(BuildEvent event) { + // no-op + } + + + + /** + * Signals that a task is starting. + * @param event recieved BuildEvent + */ + public void taskStarted(BuildEvent event) { + // no-op + } + + + /** + * Signals a message logging event. + * @param event recieved BuildEvent + */ + public void messageLogged(BuildEvent event) { + // no-op + } + + // The EnumeratedAttributes for the three interface implementations. // Name-Classname mapping is done in the configure() method. @@ -597,7 +859,7 @@ public class ModifiedSelector extends BaseExtendSelector { } public static class AlgorithmName extends EnumeratedAttribute { public String[] getValues() { - return new String[] {"hashvalue", "digest" }; + return new String[] {"hashvalue", "digest", "checksum" }; } } @@ -612,4 +874,4 @@ public class ModifiedSelector extends BaseExtendSelector { } } -} +} //class-ModifiedSelector \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java b/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java index c4bdc76e1..6c3e106eb 100644 --- a/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java +++ b/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java @@ -96,12 +96,27 @@ public class PropertiesfileCache implements Cache { // ----- Cache-Configuration ----- + /** + * Setter. + * @param file new value + */ public void setCachefile(File file) { cachefile = file; } - public File getCachefile() { return cachefile; } + /** + * Getter. + * @return the cachefile + */ + public File getCachefile() { + return cachefile; + } + + /** + * This cache is valid if the cachefile is set. + * @return true if all is ok false otherwise + */ public boolean isValid() { return (cachefile != null); } @@ -110,6 +125,9 @@ public class PropertiesfileCache implements Cache { // ----- Data Access + /** + * Load the cache from underlying properties file. + */ public void load() { if ((cachefile != null) && cachefile.isFile() && cachefile.canRead()) { try { @@ -214,4 +232,4 @@ public class PropertiesfileCache implements Cache { buf.append(">"); return buf.toString(); } -} +} \ No newline at end of file diff --git a/src/testcases/org/apache/tools/ant/types/selectors/MockAlgorithm.java b/src/testcases/org/apache/tools/ant/types/selectors/MockAlgorithm.java new file mode 100644 index 000000000..20640d5ad --- /dev/null +++ b/src/testcases/org/apache/tools/ant/types/selectors/MockAlgorithm.java @@ -0,0 +1,36 @@ +/* + * Copyright 2003-2004 The Apache Software Foundation + * + * Licensed 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.tools.ant.types.selectors; + + +import java.io.File; +import org.apache.tools.ant.types.selectors.modifiedselector.Algorithm; + +public class MockAlgorithm implements Algorithm { + public boolean isValid() { + return true; + } + + public String getValue(File file) { + return "TEST"; + } + + public String toString() { + return "MockAlgorithm@" + hashCode(); + } +} \ No newline at end of file diff --git a/src/testcases/org/apache/tools/ant/types/selectors/MockCache.java b/src/testcases/org/apache/tools/ant/types/selectors/MockCache.java new file mode 100644 index 000000000..820bceac2 --- /dev/null +++ b/src/testcases/org/apache/tools/ant/types/selectors/MockCache.java @@ -0,0 +1,67 @@ +/* + * Copyright 2003-2004 The Apache Software Foundation + * + * Licensed 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.tools.ant.types.selectors; + + +import java.util.Iterator; +import org.apache.tools.ant.types.selectors.modifiedselector.Cache; + +public class MockCache implements Cache { + + public boolean debug = false; + public boolean saved = false; + + + public MockCache() { + log("()"); + } + + public boolean isValid() { + log(".isValid()"); + return true; + } + public void delete() { + log(".delete()"); + } + public void load() { + log(".load()"); + } + public void save() { + log(".save()"); + saved = true; + } + public Object get(Object key) { + log(".get("+key+")"); + return key; + } + public void put(Object key, Object value) { + log(".put("+key+", "+value+")"); + saved = false; + } + public Iterator iterator() { + log("iterator()"); + return null; + } + public String toString() { + return "MockCache@" + hashCode(); + } + + private void log(String msg) { + if (debug) System.out.println(this+msg); + } +}//class-MockCache \ No newline at end of file diff --git a/src/testcases/org/apache/tools/ant/types/selectors/MockComparator.java b/src/testcases/org/apache/tools/ant/types/selectors/MockComparator.java new file mode 100644 index 000000000..93ffbdbf7 --- /dev/null +++ b/src/testcases/org/apache/tools/ant/types/selectors/MockComparator.java @@ -0,0 +1,31 @@ +/* + * Copyright 2003-2004 The Apache Software Foundation + * + * Licensed 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.tools.ant.types.selectors; + +import java.util.Comparator; + +public class MockComparator implements Comparator { + + public int compare(Object o1, Object o2) { + return 0; + } + + public String toString() { + return "MockComparator"; + } +}//class-MockCache \ No newline at end of file diff --git a/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java b/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java index 87a46c896..23c6adf10 100644 --- a/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java +++ b/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java @@ -29,6 +29,12 @@ import java.text.RuleBasedCollator; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.types.Parameter; +// inside MockProject +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildEvent; + // The classes to test import org.apache.tools.ant.types.selectors.modifiedselector.*; @@ -36,15 +42,22 @@ import org.apache.tools.ant.types.selectors.modifiedselector.*; /** * Unit tests for ModifiedSelector. * - * @version 2003-09-13 + * @version 2004-07-07 * @since Ant 1.6 */ public class ModifiedSelectorTest extends BaseSelectorTest { + + // ===================== attributes ===================== + + /** Package of the CacheSelector classes. */ private static String pkg = "org.apache.tools.ant.types.selectors.modifiedselector"; + // ===================== constructors, factories ===================== + + public ModifiedSelectorTest(String name) { super(name); } @@ -59,6 +72,68 @@ public class ModifiedSelectorTest extends BaseSelectorTest { } + // ===================== JUnit stuff ===================== + + + + /* There are two tests which cannot run until the test package is added + * to the core classloader. See comment in ModifiedSelelector.loadClass(). + * The tests should pass then - but because the usual build wont add + * these classes I exclude them from being executed: + * - classloaderProblem_testCustomAlgorithm2 + * - classloaderProblem_testCustomClasses + * + * For activating decomment the suite method. + * + * the addTest-part can be generated via grep and sed: + * grep "void test" ModifiedSelectorTest.java + * | sed -e 's/() {/"));/' + * -e 's/public void / suite.addTest(new ModifiedSelectorTest("/' + */ + + /* * / + // for test only - ignore tests where we arent work at the moment + public static junit.framework.Test suite() { + junit.framework.TestSuite suite= new junit.framework.TestSuite(); + suite.addTest(new ModifiedSelectorTest("testValidateWrongCache")); + suite.addTest(new ModifiedSelectorTest("testValidateWrongAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testValidateWrongComparator")); + suite.addTest(new ModifiedSelectorTest("testIllegalCustomAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testNonExistentCustomAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testCustomAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testPropcacheInvalid")); + suite.addTest(new ModifiedSelectorTest("testPropertyfileCache")); + suite.addTest(new ModifiedSelectorTest("testCreatePropertiesCacheDirect")); + suite.addTest(new ModifiedSelectorTest("testCreatePropertiesCacheViaModifiedSelector")); + suite.addTest(new ModifiedSelectorTest("testCreatePropertiesCacheViaCustomSelector")); + suite.addTest(new ModifiedSelectorTest("testHashvalueAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testDigestAlgorithmMD5")); + suite.addTest(new ModifiedSelectorTest("testDigestAlgorithmSHA")); + suite.addTest(new ModifiedSelectorTest("testChecksumAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testChecksumAlgorithmCRC")); + suite.addTest(new ModifiedSelectorTest("testChecksumAlgorithmAdler")); + suite.addTest(new ModifiedSelectorTest("testEqualComparator")); + suite.addTest(new ModifiedSelectorTest("testRuleComparator")); + suite.addTest(new ModifiedSelectorTest("testEqualComparatorViaSelector")); + suite.addTest(new ModifiedSelectorTest("testSeldirs")); + suite.addTest(new ModifiedSelectorTest("testScenario1")); + suite.addTest(new ModifiedSelectorTest("testScenarioCoreSelectorDefaults")); + suite.addTest(new ModifiedSelectorTest("testScenarioCoreSelectorSettings")); + suite.addTest(new ModifiedSelectorTest("testScenarioCustomSelectorSettings")); + + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testDelayUpdateTaskFinished")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testDelayUpdateTargetFinished")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testDelayUpdateBuildFinished")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testCustomAlgorithm2")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testCustomClasses")); + return suite; + } + /* */ + + + // ======= testcases for the attributes and nested elements of the selector ===== + + /** Test right use of cache names. */ public void testValidateWrongCache() { String name = "this-is-not-a-valid-cache-name"; @@ -103,209 +178,177 @@ public class ModifiedSelectorTest extends BaseSelectorTest { } - /** - * Propertycache must have a set 'cachefile' attribute. - * The default in ModifiedSelector "cache.properties" is set by the selector. - */ - public void testPropcacheInvalid() { - Cache cache = new PropertiesfileCache(); - if (cache.isValid()) - fail("PropertyfilesCache does not check its configuration."); + public void testIllegalCustomAlgorithm() { + try { + String algo = getAlgoName("javax.swing.JFrame"); + fail("Illegal classname used."); + } catch (Exception e) { + assertTrue("Wrong exception type: " + e.getClass().getName(), e instanceof BuildException); + assertEquals("Wrong exception message.", + "Specified class (javax.swing.JFrame) is not an Algorithm.", + e.getMessage()); + + } } - /** - * Tests whether the seldirs attribute is used. - */ - public void testSeldirs() { - ModifiedSelector s = (ModifiedSelector)getSelector(); + public void testNonExistentCustomAlgorithm() { + boolean noExcThrown = false; try { - makeBed(); - - StringBuffer sbTrue = new StringBuffer(); - StringBuffer sbFalse = new StringBuffer(); - for (int i=0; i - *
  • try fist time --> should select all
  • - *
  • try second time --> should select no files (only directories)
  • - *
  • modify timestamp of one file and content of a nother one
  • - *
  • try third time --> should select only the file with modified - * content
  • - */ - public void testScenario1() { - BFT bft = null; - ModifiedSelector s = null; + public void classloaderProblem_testCustomClasses() { + BFT bft = new BFT(); + bft.setUp(); try { - // - // ***** initialize test environment (called "bed") ***** - // - makeBed(); - String results = null; - - // Configure the selector - only defaults are used - s = (ModifiedSelector)getSelector(); + // do the actions + bft.doTarget("modifiedselectortest-customClasses"); + // do the checks - the buildfile stores the fileset as property + String fsFullValue = bft.getProperty("fs.full.value"); + String fsModValue = bft.getProperty("fs.mod.value"); + + assertNotNull("'fs.full.value' must be set.", fsFullValue); + assertTrue("'fs.full.value' must not be null.", !"".equals(fsFullValue)); + assertTrue("'fs.full.value' must contain ant.bat.", fsFullValue.indexOf("ant.bat")>-1); + + assertNotNull("'fs.mod.value' must be set.", fsModValue); + // must be empty according to the Mock* implementations + assertTrue("'fs.mod.value' must be empty.", "".equals(fsModValue)); + // don't catch the JUnit exceptions + } finally { + bft.doTarget("modifiedselectortest-scenario-clean"); + bft.deletePropertiesfile(); + bft.tearDown(); + } + } - // - // ***** First Run ***** - // the first call should get all files, because nothing is in - // the cache - // - performTests(s, "TTTTTTTTTTTT"); - // - // ***** Second Run ***** - // the second call should get no files, because no content - // has changed - // - performTests(s, "TFFFFFFFFFFT"); + public void classloaderProblem_testDelayUpdateTaskFinished() { + doDelayUpdateTest(1); + } - // - // ***** make some files dirty ***** - // - // these files are made dirty --> 3+4 with different content - String f2name = "tar/bz2/asf-logo-huge.tar.bz2"; - String f3name = "asf-logo.gif.md5"; - String f4name = "copy.filterset.filtered"; + public void classloaderProblem_testDelayUpdateTargetFinished() { + doDelayUpdateTest(2); + } - // AccessObject to the test-Ant-environment - bft = new BFT(); - // give some values (via property file) to that environment - bft.writeProperties("f2name="+f2name); - bft.writeProperties("f3name="+f3name); - bft.writeProperties("f4name="+f4name); - // call the target for making the files dirty - bft.doTarget("modifiedselectortest-makeDirty"); + public void classloaderProblem_testDelayUpdateBuildFinished() { + doDelayUpdateTest(3); + } - // - // ***** Third Run ***** - // third call should get only those files, which CONTENT changed - // (no timestamp changes required!) - results = selectionString(s); - // - // ***** Check the result ***** - // + public void doDelayUpdateTest(int kind) { + // no check for 1<=kind<=3 - only internal use therefore check it + // while development + + // readable form of parameter kind + String[] kinds = {"task", "target", "build"}; + + // setup the "Ant project" + MockProject project = new MockProject(); + File base = new File("base"); + File file1 = new File("file1"); + File file2 = new File("file2"); + + // setup the selector + ModifiedSelector sel = new ModifiedSelector(); + sel.setProject(project); + sel.setUpdate(true); + sel.setDelayUpdate(true); + sel.setAlgorithmClass("org.apache.tools.ant.types.selectors.MockAlgorithm"); + sel.setCacheClass("org.apache.tools.ant.types.selectors.MockCache"); + sel.configure(); + + // get the cache, so we can check our things + MockCache cache = (MockCache)sel.getCache(); + + // the test + assertFalse("Cache must not be saved before 1st selection.", cache.saved); + sel.isSelected(base, "file1", file1); + assertFalse("Cache must not be saved after 1st selection.", cache.saved); + sel.isSelected(base, "file2", file2); + assertFalse("Cache must not be saved after 2nd selection.", cache.saved); + switch (kind) { + case 1 : project.fireTaskFinished(); break; + case 2 : project.fireTargetFinished(); break; + case 3 : project.fireBuildFinished(); break; + } + assertTrue("Cache must be saved after " + kinds[kind-1] + "Finished-Event.", cache.saved); - // Mark all files which should be selected as (T)rue and all others - // as (F)alse. Directories are always selected so they always are - // (T)rue. - StringBuffer expected = new StringBuffer(); - for (int i=0; i")) algo = algo.substring(0, algo.length()-1); + // return the clean value + return algo; } + // ================ testcases for the cache implementations ================ + /** - * This scenario is based on scenario 1, but does not use any - * default value and its based on selector. Used values are:
      - *
    • Cache: Propertyfile, - * cachefile={java.io.tmpdir}/mycache.txt
    • - *
    • Algorithm: Digest - * algorithm=SHA, Provider=null
    • - *
    • Comparator: java.text.RuleBasedCollator - *
    • Update: true
    • + * Propertycache must have a set 'cachefile' attribute. + * The default in ModifiedSelector "cache.properties" is set by the selector. */ - public void testScenario2() { - ExtendSelector s = new ExtendSelector(); - BFT bft = new BFT(); - String cachefile = System.getProperty("java.io.tmpdir")+"/mycache.txt"; - try { - makeBed(); - - s.setClassname("org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"); + public void testPropcacheInvalid() { + Cache cache = new PropertiesfileCache(); + if (cache.isValid()) + fail("PropertyfilesCache does not check its configuration."); + } - s.addParam(createParam("cache.cachefile", cachefile)); - //s.addParam(createParam("algorithm.provider","---")); // i don't know any valid - s.addParam(createParam("cache","propertyfile")); - s.addParam(createParam("update","true")); - s.addParam(createParam("comparator","rule")); - s.addParam(createParam("algorithm.name","sha")); - s.addParam(createParam("algorithm","digest")); - // first and second run - performTests(s, "TTTTTTTTTTTT"); - performTests(s, "TFFFFFFFFFFT"); - // make dirty - String f2name = "tar/bz2/asf-logo-huge.tar.bz2"; - String f3name = "asf-logo.gif.md5"; - String f4name = "copy.filterset.filtered"; - bft.writeProperties("f2name="+f2name); - bft.writeProperties("f3name="+f3name); - bft.writeProperties("f4name="+f4name); - bft.doTarget("modifiedselectortest-makeDirty"); - // third run - String results = selectionString(s); - StringBuffer expected = new StringBuffer(); - for (int i=0; i5); - assertTrue("Too much files copied on second time.", to2.list().length==0); - assertTrue("Too much files copied on third time.", to3.list().length==2); - // don't catch the JUnit exceptions - } finally { - bft.doTarget("modifiedselectortest-scenario-clean"); - bft.deletePropertiesfile(); - bft.tearDown(); - cachefile.delete(); - } + public void testChecksumAlgorithmAdler() { + ChecksumAlgorithm algo = new ChecksumAlgorithm(); + algo.setAlgorithm("Adler"); + doTest(algo); } - // ==================== Test interface semantic =================== - - /** + * Test the interface semantic of Algorithms. * This method does some common test for algorithm implementations. * An algorithm must return always the same value for the same file and * it must not return null. @@ -550,50 +573,55 @@ public class ModifiedSelectorTest extends BaseSelectorTest { } - /** - * This method does some common test for cache implementations. - * A cache must return a stored value and a valid iterator. - * After calling the delete() the cache must be empty. - * - * @param algo configured test object - */ - protected void doTest(Cache cache) { - assertTrue("Cache not proper configured.", cache.isValid()); - String key1 = "key1"; - String value1 = "value1"; - String key2 = "key2"; - String value2 = "value2"; + // ============== testcases for the comparator implementations ============== - // given cache must be empty - Iterator it1 = cache.iterator(); - assertFalse("Cache is not empty", it1.hasNext()); - // cache must return a stored value - cache.put(key1, value1); - cache.put(key2, value2); - assertEquals("cache returned wrong value", value1, cache.get(key1)); - assertEquals("cache returned wrong value", value2, cache.get(key2)); + public void testEqualComparator() { + EqualComparator comp = new EqualComparator(); + doTest(comp); + } - // test the iterator - Iterator it2 = cache.iterator(); - Object returned = it2.next(); - boolean ok = (key1.equals(returned) || key2.equals(returned)); - String msg = "Iterator returned unexpected value." - + " key1.equals(returned)="+key1.equals(returned) - + " key2.equals(returned)="+key2.equals(returned) - + " returned="+returned - + " ok="+ok; - assertTrue(msg, ok); - // clear the cache - cache.delete(); - Iterator it3 = cache.iterator(); - assertFalse("Cache is not empty", it1.hasNext()); + public void testRuleComparator() { + RuleBasedCollator comp = (RuleBasedCollator)RuleBasedCollator.getInstance(); + doTest(comp); + } + + + public void testEqualComparatorViaSelector() { + ModifiedSelector s = (ModifiedSelector)getSelector(); + ModifiedSelector.ComparatorName compName = new ModifiedSelector.ComparatorName(); + compName.setValue("equal"); + s.setComparator(compName); + try { + performTests(s, "TTTTTTTTTTTT"); + } finally { + s.getCache().delete(); + } + } + + + public void _testRuleComparatorViaSelector() { //not yet supported see note in selector + ModifiedSelector s = (ModifiedSelector)getSelector(); + ModifiedSelector.ComparatorName compName = new ModifiedSelector.ComparatorName(); + compName.setValue("rule"); + s.setComparator(compName); + try { + performTests(s, "TTTTTTTTTTTT"); + } finally { + s.getCache().delete(); + } + } + + + public void _testCustomComparator() { + // same logic as on algorithm, no testcases created } /** + * Test the interface semantic of Comparators. * This method does some common test for comparator implementations. * * @param algo configured test object @@ -609,9 +637,255 @@ public class ModifiedSelectorTest extends BaseSelectorTest { } - // ======================== Helper methods ======================== + // ===================== scenario tests ===================== + + + /** + * Tests whether the seldirs attribute is used. + */ + public void testSeldirs() { + ModifiedSelector s = (ModifiedSelector)getSelector(); + try { + makeBed(); + + StringBuffer sbTrue = new StringBuffer(); + StringBuffer sbFalse = new StringBuffer(); + for (int i=0; i + *
    • try fist time --> should select all
    • + *
    • try second time --> should select no files (only directories)
    • + *
    • modify timestamp of one file and content of a nother one
    • + *
    • try third time --> should select only the file with modified + * content
    • + */ + public void testScenario1() { + BFT bft = null; + ModifiedSelector s = null; + try { + // + // ***** initialize test environment (called "bed") ***** + // + makeBed(); + String results = null; + + // Configure the selector - only defaults are used + s = (ModifiedSelector)getSelector(); + + // + // ***** First Run ***** + // the first call should get all files, because nothing is in + // the cache + // + performTests(s, "TTTTTTTTTTTT"); + + // + // ***** Second Run ***** + // the second call should get no files, because no content + // has changed + // + performTests(s, "TFFFFFFFFFFT"); + + // + // ***** make some files dirty ***** + // + + // these files are made dirty --> 3+4 with different content + String f2name = "tar/bz2/asf-logo-huge.tar.bz2"; + String f3name = "asf-logo.gif.md5"; + String f4name = "copy.filterset.filtered"; + + // AccessObject to the test-Ant-environment + bft = new BFT(); + // give some values (via property file) to that environment + bft.writeProperties("f2name="+f2name); + bft.writeProperties("f3name="+f3name); + bft.writeProperties("f4name="+f4name); + // call the target for making the files dirty + bft.doTarget("modifiedselectortest-makeDirty"); + + // + // ***** Third Run ***** + // third call should get only those files, which CONTENT changed + // (no timestamp changes required!) + results = selectionString(s); + + // + // ***** Check the result ***** + // + + // Mark all files which should be selected as (T)rue and all others + // as (F)alse. Directories are always selected so they always are + // (T)rue. + StringBuffer expected = new StringBuffer(); + for (int i=0; i selector. Used values are:
        + *
      • Cache: Propertyfile, + * cachefile={java.io.tmpdir}/mycache.txt
      • + *
      • Algorithm: Digest + * algorithm=SHA, Provider=null
      • + *
      • Comparator: java.text.RuleBasedCollator + *
      • Update: true
      • + */ + public void _testScenario2() { // RuleBasedCollator not yet supported - see Selector:375 note + ExtendSelector s = new ExtendSelector(); + BFT bft = new BFT(); + String cachefile = System.getProperty("java.io.tmpdir")+"/mycache.txt"; + try { + makeBed(); + + s.setClassname("org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"); + + s.addParam(createParam("cache.cachefile", cachefile)); + //s.addParam(createParam("algorithm.provider","---")); // i don't know any valid + s.addParam(createParam("cache","propertyfile")); + s.addParam(createParam("update","true")); + s.addParam(createParam("comparator","rule")); + s.addParam(createParam("algorithm.name","sha")); + s.addParam(createParam("algorithm","digest")); + + // first and second run + performTests(s, "TTTTTTTTTTTT"); + performTests(s, "TFFFFFFFFFFT"); + // make dirty + String f2name = "tar/bz2/asf-logo-huge.tar.bz2"; + String f3name = "asf-logo.gif.md5"; + String f4name = "copy.filterset.filtered"; + bft.writeProperties("f2name="+f2name); + bft.writeProperties("f3name="+f3name); + bft.writeProperties("f4name="+f4name); + bft.doTarget("modifiedselectortest-makeDirty"); + // third run + String results = selectionString(s); + StringBuffer expected = new StringBuffer(); + for (int i=0; i5); + assertTrue("Too much files copied on second time.", to2.list().length==0); + assertTrue("Too much files copied on third time.", to3.list().length==2); + // don't catch the JUnit exceptions + } finally { + bft.doTarget("modifiedselectortest-scenario-clean"); + bft.deletePropertiesfile(); + bft.tearDown(); + cachefile.delete(); + } + } + + + // ===================== helper methods and classes ==================== + + + /** + * Creates a configured parameter object. + * @param name name of the parameter + * @param value value of the parameter + * @return the parameter object + */ private Parameter createParam(String name, String value) { Parameter p = new Parameter(); p.setName(name); @@ -620,6 +894,11 @@ public class ModifiedSelectorTest extends BaseSelectorTest { } + /** + * The BFT class wrapps the selector test-builfile inside an + * ant project (BuildFileTest). It supports target execution + * and property transfer to that project. + */ private class BFT extends org.apache.tools.ant.BuildFileTest { BFT() { super("nothing"); } BFT(String name) { @@ -641,6 +920,10 @@ public class ModifiedSelectorTest extends BaseSelectorTest { executeTarget(target); } + public String getProperty(String property) { + return project.getProperty(property); + } + public void writeProperties(String line) { if (!isConfigured) setUp(); File dir = getProject().getBaseDir(); @@ -667,4 +950,64 @@ public class ModifiedSelectorTest extends BaseSelectorTest { } }//class-BFT -}//class-ModifiedSelectorTest + + /** + * MockProject wrappes a very small ant project (one target, one task) + * but provides public methods to fire the build events. + */ + private class MockProject extends Project { + private Task task; + private Target target; + + public MockProject() { + task = new Task(){ + public void execute() { + } + }; + task.setTaskName("testTask"); + target = new Target(); + target.setName("testTarget"); + target.setProject(this); + target.addTask(task); + task.setOwningTarget(target); + } + + public void fireBuildStarted() { + super.fireBuildStarted(); + } + public void fireBuildFinished() { + super.fireBuildFinished(null); + } + public void fireSubBuildStarted() { + super.fireSubBuildStarted(); + } + public void fireSubBuildFinished() { + super.fireSubBuildFinished(null); + } + public void fireTargetStarted() { + super.fireTargetStarted(target); + } + public void fireTargetFinished() { + super.fireTargetFinished(target, null); + } + public void fireTaskStarted() { + super.fireTaskStarted(task); + } + public void fireTaskFinished() { + super.fireTaskFinished(task, null); + } + private void fireMessageLoggedEvent(BuildEvent event, String message, int priority) { + } + private void fireMessageLoggedProject(String message) { + super.fireMessageLogged(this, message, Project.MSG_INFO); + } + private void fireMessageLoggedTarget(String message) { + super.fireMessageLogged(target, message, Project.MSG_INFO); + } + private void fireMessageLoggedTask(String message) { + super.fireMessageLogged(task, message, Project.MSG_INFO); + } + }//class-MockProject + + +}//class-ModifiedSelectorTest \ No newline at end of file
    namevaluesdescriptionrequired
    algorithm hashvalue | digest hashvalue | digest | checksum which algorithm implementation should be used *
  • hashvalue - loads the file content into a String and * uses its hashValue() method
  • *
  • digest - uses java.security.MessageDigest class + *
  • checksum - uses java.util.zip.Checksum interface *
  • no, defaults to digest
    comparator equal | role equal | rule which comparator implementation should be used *
  • equal - simple comparison using String.equals()
  • - *
  • role - uses java.text.RuleBasedCollator class + *
  • rule - uses java.text.RuleBasedCollator class *
  • no, defaults to equal
    no, defaults to true
    delayupdate true | false If set to true, the storage of the cache will be delayed until the + * next finished BuildEvent; task finished, target finished or build finished, + * whichever comes first. This is provided for increased performance. If set + * to false, the storage of the cache will happen with each change. This + * attribute depends upon the update attribute. no, defaults to true
    cacheclass classname which custom cache implementation should be used no
    algorithmclass classname which custom algorithm implementation should be used no
    comparatorclass classname which custom comparator implementation should be used no
    cache.* depends on used cache value is stored and given to the Cache-Object for initialisation