Browse Source

Preparations for PR: 21832, in particular unit tests and some

refactorings for <sync>

Changes to Sync:
* drop JDK 1.1 compatibility,
* override scan instead of buildMap in MyCopy so that we get the
  included directories even if includeEmptyDirs is false.
* use a DirectoryScanner to find orphaned files instead of the
  recursive algorithm.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277005 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 21 years ago
parent
commit
b1186f08a3
5 changed files with 210 additions and 63 deletions
  1. +64
    -0
      src/etc/testcases/taskdefs/sync.xml
  2. +38
    -62
      src/main/org/apache/tools/ant/taskdefs/Sync.java
  3. +1
    -1
      src/main/org/apache/tools/ant/types/PatternSet.java
  4. +12
    -0
      src/testcases/org/apache/tools/ant/BuildFileTest.java
  5. +95
    -0
      src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java

+ 64
- 0
src/etc/testcases/taskdefs/sync.xml View File

@@ -0,0 +1,64 @@
<?xml version="1.0"?>
<project name="sync-test" default="not-me">
<property name="scratch" location="synctest"/>

<target name="not-me">
<fail>This file must be used from a test case</fail>
</target>

<target name="cleanup">
<delete dir="${scratch}"/>
</target>

<target name="setup">
<property name="src" location="${scratch}/source"/>
<property name="dest" location="${scratch}/target"/>
<mkdir dir="${src}"/>
<mkdir dir="${dest}"/>
</target>

<target name="simplecopy" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>
<sync todir="${dest}">
<fileset dir="${src}"/>
</sync>
</target>

<target name="copyandremove" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>
<mkdir dir="${dest}/e"/>
<touch file="${dest}/e/f"/>
<sync todir="${dest}">
<fileset dir="${src}"/>
</sync>
</target>

<target name="emptycopy" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>
<sync todir="${dest}">
<fileset dir="${src}" excludes="**/d"/>
</sync>
</target>

<target name="emptydircopy" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>
<sync todir="${dest}"
includeemptydirs="true">
<fileset dir="${src}" excludes="**/d"/>
</sync>
</target>

<target name="emptydircopyandremove" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>
<mkdir dir="${dest}/e/f"/>
<sync todir="${dest}"
includeemptydirs="true">
<fileset dir="${src}" excludes="**/d"/>
</sync>
</target>
</project>

+ 38
- 62
src/main/org/apache/tools/ant/taskdefs/Sync.java View File

@@ -25,12 +25,16 @@ package org.apache.tools.ant.taskdefs;


import java.io.File; import java.io.File;


import java.util.Hashtable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;


import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project; import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task; import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.PatternSet;
import org.apache.tools.ant.util.FileNameMapper; import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.IdentityMapper; import org.apache.tools.ant.util.IdentityMapper;


@@ -82,7 +86,7 @@ public class Sync extends Task {
File toDir = _copy.getToDir(); File toDir = _copy.getToDir();


// The complete list of files to copy // The complete list of files to copy
Hashtable allFiles = _copy._dest2src;
Set allFiles = _copy.nonOrphans;


// If the destination directory didn't already exist, // If the destination directory didn't already exist,
// or was empty, then no previous file removal is necessary! // or was empty, then no previous file removal is necessary!
@@ -143,54 +147,29 @@ public class Sync extends Task {
* @return the number of orphaned files and directories actually removed. * @return the number of orphaned files and directories actually removed.
* Position 0 of the array is the number of orphaned directories. * Position 0 of the array is the number of orphaned directories.
* Position 1 of the array is the number or orphaned files. * Position 1 of the array is the number or orphaned files.
* Position 2 is meaningless.
*/ */
private int[] removeOrphanFiles(Hashtable nonOrphans, File file) {
int[] removedCount = new int[] {0, 0, 0};
if (file.isDirectory()) {
File[] children = file.listFiles();
for (int i = 0; i < children.length; ++i) {
int[] temp = removeOrphanFiles(nonOrphans, children[i]);
removedCount[0] += temp[0];
removedCount[1] += temp[1];
removedCount[2] += temp[2];
}

if (nonOrphans.get(file) == null && removedCount[2] == 0) {
log("Removing orphan directory: " + file, Project.MSG_DEBUG);
file.delete();
++removedCount[0];
} else {
/*
Contrary to what is said above, position 2 is not
meaningless inside the recursion.
Position 2 is used to carry information back up the
recursion about whether or not a directory contains
a directory or file at any depth that is not an
orphan
This has to be done, because if you have the
following directory structure: c:\src\a\file and
your mapper src files were constructed like so:
<include name="**\a\**\*"/>
The folder 'a' will not be in the hashtable of
nonorphans. So, before deleting it as an orphan, we
have to know whether or not any of its children at
any level are orphans.
If no, then this folder is also an orphan, and may
be deleted. I do this by changing position 2 to a
'1'.
*/
removedCount[2] = 1;
}

} else {
if (nonOrphans.get(file) == null) {
log("Removing orphan file: " + file, Project.MSG_DEBUG);
file.delete();
++removedCount[1];
} else {
removedCount[2] = 1;
}
private int[] removeOrphanFiles(Set nonOrphans, File toDir) {
int[] removedCount = new int[] {0, 0};
DirectoryScanner ds = new DirectoryScanner();
ds.setBasedir(toDir);
Set s = new HashSet(nonOrphans);
s.add("");
String[] excls = (String[]) s.toArray(new String[s.size()]);
ds.setExcludes(excls);
ds.scan();
String[] files = ds.getIncludedFiles();
for (int i = 0; i < files.length; i++) {
File f = new File(toDir, files[i]);
log("Removing orphan file: " + f, Project.MSG_DEBUG);
f.delete();
++removedCount[1];
}
String[] dirs = ds.getIncludedDirectories();
for (int i = dirs.length - 1 ; i >= 0 ; --i) {
File f = new File(toDir, dirs[i]);
log("Removing orphan directory: " + f, Project.MSG_DEBUG);
f.delete();
++removedCount[0];
} }
return removedCount; return removedCount;
} }
@@ -303,25 +282,22 @@ public class Sync extends Task {


// List of files that must be copied, irrelevant from the // List of files that must be copied, irrelevant from the
// fact that they are newer or not than the destination. // fact that they are newer or not than the destination.
private Hashtable _dest2src = new Hashtable();
private Set nonOrphans = new HashSet();


public MyCopy() { public MyCopy() {
} }


protected void buildMap(File fromDir, File toDir, String[] names,
FileNameMapper mapper, Hashtable map) {
assertTrue("No mapper", mapper instanceof IdentityMapper);
protected void scan(File fromDir, File toDir, String[] files,
String[] dirs) {
assertTrue("No mapper", mapperElement == null);


super.buildMap(fromDir, toDir, names, mapper, map);
super.scan(fromDir, toDir, files, dirs);


for (int i = 0; i < names.length; ++i) {
String name = names[i];
File dest = new File(toDir, name);
// No need to instantiate the src file, as we use the
// table as a set (to remain Java 1.1 compatible!!!).
//File src = new File(fromDir, name);
//_dest2src.put(dest, src);
_dest2src.put(dest, fromDir);
for (int i = 0; i < files.length; ++i) {
nonOrphans.add(files[i]);
}
for (int i = 0; i < dirs.length; ++i) {
nonOrphans.add(dirs[i]);
} }
} }




+ 1
- 1
src/main/org/apache/tools/ant/types/PatternSet.java View File

@@ -434,7 +434,7 @@ public class PatternSet extends DataType implements Cloneable {
*/ */
private String[] makeArray(Vector list, Project p) { private String[] makeArray(Vector list, Project p) {
if (list.size() == 0) { if (list.size() == 0) {
return null;
return null;
} }


Vector tmpNames = new Vector(); Vector tmpNames = new Vector();


+ 12
- 0
src/testcases/org/apache/tools/ant/BuildFileTest.java View File

@@ -111,6 +111,18 @@ public abstract class BuildFileTest extends TestCase {
assertEquals(log, realLog); assertEquals(log, realLog);
} }


/**
* Assert that the given substring is in the log messages
*/

protected void assertDebuglogContaining(String substring) {
String realLog = getFullLog();
assertTrue("expecting debug log to contain \"" + substring
+ "\" log was \""
+ realLog + "\"",
realLog.indexOf(substring) >= 0);
}

/** /**
* Gets the log the BuildFileTest object. * Gets the log the BuildFileTest object.
* only valid if configureProject() has * only valid if configureProject() has


+ 95
- 0
src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java View File

@@ -0,0 +1,95 @@
/*
* Copyright 2000-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.taskdefs;

import org.apache.tools.ant.BuildFileTest;
import java.io.File;

public class SyncTest extends BuildFileTest {

public SyncTest(String name) {
super(name);
}

public void setUp() {
configureProject("src/etc/testcases/taskdefs/sync.xml");
}

public void tearDown() {
executeTarget("cleanup");
}

public void testSimpleCopy() {
executeTarget("simplecopy");
String d = getProject().getProperty("dest") + "/a/b/c/d";
assertFileIsPresent(d);
assertTrue(getFullLog().indexOf("dangling") == -1);
}

public void testEmptyCopy() {
executeTarget("emptycopy");
String d = getProject().getProperty("dest") + "/a/b/c/d";
assertFileIsNotPresent(d);
String c = getProject().getProperty("dest") + "/a/b/c";
assertFileIsNotPresent(c);
assertTrue(getFullLog().indexOf("dangling") == -1);
}

public void testEmptyDirCopy() {
executeTarget("emptydircopy");
String d = getProject().getProperty("dest") + "/a/b/c/d";
assertFileIsNotPresent(d);
String c = getProject().getProperty("dest") + "/a/b/c";
assertFileIsPresent(c);
assertTrue(getFullLog().indexOf("dangling") == -1);
}

public void testCopyAndRemove() {
executeTarget("copyandremove");
String d = getProject().getProperty("dest") + "/a/b/c/d";
assertFileIsPresent(d);
String f = getProject().getProperty("dest") + "/e/f";
assertFileIsNotPresent(f);
assertTrue(getFullLog().indexOf("Removing orphan file:") > -1);
assertDebuglogContaining("Removed 1 dangling file from");
assertDebuglogContaining("Removed 1 dangling directory from");
}

public void testEmptyDirCopyAndRemove() {
executeTarget("emptydircopyandremove");
String d = getProject().getProperty("dest") + "/a/b/c/d";
assertFileIsNotPresent(d);
String c = getProject().getProperty("dest") + "/a/b/c";
assertFileIsPresent(c);
String f = getProject().getProperty("dest") + "/e/f";
assertFileIsNotPresent(f);
assertTrue(getFullLog().indexOf("Removing orphan directory:") > -1);
assertDebuglogContaining("NO dangling file to remove from");
assertDebuglogContaining("Removed 2 dangling directories from");
}

public void assertFileIsPresent(String f) {
assertTrue("Expected file " + f,
getProject().resolveFile(f).exists());
}

public void assertFileIsNotPresent(String f) {
assertTrue("Didn't expect file " + f,
!getProject().resolveFile(f).exists());
}
}

Loading…
Cancel
Save