1 /**
2  * @copyright
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  * @endcopyright
22  */
23 package org.tigris.subversion.javahl;
24 
25 import java.io.File;
26 import java.io.FileOutputStream;
27 import java.io.FileNotFoundException;
28 import java.io.FileReader;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.io.ByteArrayOutputStream;
32 import java.text.ParseException;
33 import java.util.Arrays;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.Map;
40 
41 
42 /**
43  * Tests the basic functionality of javahl binding (inspired by the
44  * tests in subversion/tests/cmdline/basic_tests.py).
45  */
46 public class BasicTests extends SVNTests
47 {
48     /**
49      * Base name of all our tests.
50      */
51     public final static String testName = "basic_test";
52 
BasicTests()53     public BasicTests()
54     {
55         init();
56     }
57 
BasicTests(String name)58     public BasicTests(String name)
59     {
60         super(name);
61         init();
62     }
63 
64     /**
65      * Initialize the testBaseName and the testCounter, if this is the
66      * first test of this class.
67      */
init()68     private void init()
69     {
70         if (!testName.equals(testBaseName))
71         {
72             testCounter = 0;
73             testBaseName = testName;
74         }
75     }
76 
77     /**
78      * Test LogDate().
79      * @throws Throwable
80      */
testLogDate()81     public void testLogDate() throws Throwable
82     {
83         String goodDate = "2007-10-04T03:00:52.134992Z";
84         String badDate = "2008-01-14";
85         LogDate logDate;
86 
87         try
88         {
89             logDate = new LogDate(goodDate);
90             assertEquals(1191466852134992L, logDate.getTimeMicros());
91         } catch (ParseException e) {
92             fail("Failed to parse date " + goodDate);
93         }
94 
95         try
96         {
97             logDate = new LogDate(badDate);
98             fail("Failed to throw exception on bad date " + badDate);
99         } catch (ParseException e) {
100         }
101     }
102 
103     /**
104      * Test SVNClient.getVersion().
105      * @throws Throwable
106      */
testVersion()107     public void testVersion() throws Throwable
108     {
109         try
110         {
111             Version version = client.getVersion();
112             String versionString = version.toString();
113             if (versionString == null || versionString.trim().length() == 0)
114             {
115                 throw new Exception("Version string empty");
116             }
117         }
118         catch (Exception e)
119         {
120             fail("Version should always be available unless the " +
121                  "native libraries failed to initialize: " + e);
122         }
123     }
124 
125     /**
126      * Tests Subversion path validation.
127      */
testPathValidation()128     public void testPathValidation() throws Throwable
129     {
130         // Rather than segfaulting, JavaHL considers null an invalid path.
131         assertFalse("Path validation produced false-positive for null path",
132                     Path.isValid(null));
133 
134         String path = "valid-path";
135         assertTrue("Validation check of valid path '" + path +
136                    "' should succeed", Path.isValid(path));
137 
138         // File names cannot contain control characters.
139         path = "invalid-\u0001-path";
140         assertFalse("Validation check of invalid path '" + path +
141                     "' (which contains control characters) should fail",
142                     Path.isValid(path));
143     }
144 
145     /**
146      * Tests Subversion path as URL predicate.
147      */
testPathIsURL()148     public void testPathIsURL() throws Throwable
149     {
150         try
151         {
152             Path.isURL(null);
153             fail("A null path should raise an exception");
154         }
155         catch (IllegalArgumentException expected)
156         {
157         }
158 
159         // Subversion "paths" which aren't URLs.
160         String[] paths = { "/path", "c:\\path" };
161         for (int i = 0; i < paths.length; i++)
162         {
163             assertFalse("'" + paths[i] + "' should not be considered a URL",
164                         Path.isURL(paths[i]));
165         }
166 
167         // Subversion "paths" which are URLs.
168         paths = new String[] { "http://example.com", "svn://example.com",
169                                "svn+ssh://example.com", "file:///src/svn/" };
170         for (int i = 0; i < paths.length; i++)
171         {
172             assertTrue("'" + paths[i] + "' should be considered a URL",
173                        Path.isURL(paths[i]));
174         }
175     }
176 
177     /**
178      * Tests Mergeinfo and RevisionRange classes.
179      * @since 1.5
180      */
testMergeinfoParser()181     public void testMergeinfoParser() throws Throwable
182     {
183         String mergeInfoPropertyValue =
184             "/trunk:1-300,305,307,400-405\n/branches/branch:308-400";
185         Mergeinfo info = new Mergeinfo(mergeInfoPropertyValue);
186         String[] paths = info.getPaths();
187         assertEquals(2, paths.length);
188         RevisionRange[] trunkRange = info.getRevisionRange("/trunk");
189         assertEquals(4, trunkRange.length);
190         assertEquals("1-300", trunkRange[0].toString());
191         assertEquals("305", trunkRange[1].toString());
192         assertEquals("307", trunkRange[2].toString());
193         assertEquals("400-405", trunkRange[3].toString());
194         RevisionRange[] branchRange =
195             info.getRevisionRange("/branches/branch");
196         assertEquals(1, branchRange.length);
197     }
198 
199     /**
200      * Test the basic SVNClient.status functionality.
201      * @throws Throwable
202      */
testBasicStatus()203     public void testBasicStatus() throws Throwable
204     {
205         // build the test setup
206         OneTest thisTest = new OneTest();
207 
208         // check the status of the working copy
209         thisTest.checkStatus();
210 
211         // Test status of non-existent file
212         File fileC = new File(thisTest.getWorkingCopy() + "/A", "foo.c");
213 
214         Status s = client.singleStatus(fileToSVNPath(fileC, false), false);
215         if (s != null)
216         {
217             if (s.hasTreeConflict()
218                 || s.getTextStatus() != Status.Kind.none
219                 || s.getPropStatus() != Status.Kind.none
220                 || s.getRepositoryTextStatus() != Status.Kind.none
221                 || s.getRepositoryPropStatus() != Status.Kind.none)
222             {
223                 fail("File foo.c should not return a status.");
224             }
225         }
226 
227     }
228 
229     /**
230      * Test the "out of date" info from {@link
231      * org.tigris.subversion.javahl.SVNClient#status()}.
232      *
233      * @throws SubversionException
234      * @throws IOException
235      */
testOODStatus()236     public void testOODStatus() throws SubversionException, IOException
237     {
238         // build the test setup
239         OneTest thisTest = new OneTest();
240 
241         // Make a whole slew of changes to a WC:
242         //
243         //  (root)               r7 - prop change
244         //  iota
245         //  A
246         //  |__mu
247         //  |
248         //  |__B
249         //  |   |__lambda
250         //  |   |
251         //  |   |__E             r12 - deleted
252         //  |   |  |__alpha
253         //  |   |  |__beta
254         //  |   |
255         //  |   |__F             r9 - prop change
256         //  |   |__I             r6 - added dir
257         //  |
258         //  |__C                 r5 - deleted
259         //  |
260         //  |__D
261         //     |__gamma
262         //     |
263         //     |__G
264         //     |  |__pi          r3 - deleted
265         //     |  |__rho         r2 - modify text
266         //     |  |__tau         r4 - modify text
267         //     |
268         //     |__H
269         //        |__chi         r10-11 replaced with file
270         //        |__psi         r13-14 replaced with dir
271         //        |__omega
272         //        |__nu          r8 - added file
273         File file, dir;
274         PrintWriter pw;
275         Status status;
276         long rev;             // Resulting rev from co or update
277         long expectedRev = 2;  // Keeps track of the latest rev committed
278 
279         // ----- r2: modify file A/D/G/rho --------------------------
280         file = new File(thisTest.getWorkingCopy(), "A/D/G/rho");
281         pw = new PrintWriter(new FileOutputStream(file, true));
282         pw.print("modification to rho");
283         pw.close();
284         addExpectedCommitItem(thisTest.getWCPath(),
285                               thisTest.getUrl(), "A/D/G/rho", NodeKind.file,
286                               CommitItemStateFlags.TextMods);
287         assertEquals("wrong revision number from commit",
288                      rev = client.commit(new String[]{thisTest.getWCPath()},
289                                          "log msg", true), expectedRev++);
290         thisTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", rev);
291         thisTest.getWc().setItemContent("A/D/G/rho",
292             thisTest.getWc().getItemContent("A/D/G/rho")
293             + "modification to rho");
294 
295         status = client.singleStatus(thisTest.getWCPath() + "/A/D/G/rho",
296                                      false);
297         long rhoCommitDate = status.getLastChangedDate().getTime();
298         long rhoCommitRev = rev;
299         String rhoAuthor = status.getLastCommitAuthor();
300 
301         // ----- r3: delete file A/D/G/pi ---------------------------
302         client.remove(new String[] {thisTest.getWCPath() + "/A/D/G/pi"}, null,
303                       false);
304         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
305                               "A/D/G/pi", NodeKind.file,
306                               CommitItemStateFlags.Delete);
307         assertEquals("wrong revision number from commit",
308                      rev = client.commit(new String[] {thisTest.getWCPath()},
309                                          "log msg", true),
310                      expectedRev++);
311         thisTest.getWc().removeItem("A/D/G/pi");
312 
313         thisTest.getWc().setItemWorkingCopyRevision("A/D/G", rev);
314         assertEquals("wrong revision from update",
315                      client.update(thisTest.getWCPath() + "/A/D/G",
316                                    null, true),
317                      rev);
318         long GCommitRev = rev;
319 
320         // ----- r4: modify file A/D/G/tau --------------------------
321         file = new File(thisTest.getWorkingCopy(), "A/D/G/tau");
322         pw = new PrintWriter(new FileOutputStream(file, true));
323         pw.print("modification to tau");
324         pw.close();
325         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
326                               "A/D/G/tau",NodeKind.file,
327                               CommitItemStateFlags.TextMods);
328         assertEquals("wrong revision number from commit",
329                      rev = client.commit(new String[] {thisTest.getWCPath()},
330                                          "log msg", true),
331                      expectedRev++);
332         thisTest.getWc().setItemWorkingCopyRevision("A/D/G/tau", rev);
333         thisTest.getWc().setItemContent("A/D/G/tau",
334                 thisTest.getWc().getItemContent("A/D/G/tau")
335                 + "modification to tau");
336         status = client.singleStatus(thisTest.getWCPath() + "/A/D/G/tau",
337                                      false);
338         long tauCommitDate = status.getLastChangedDate().getTime();
339         long tauCommitRev = rev;
340         String tauAuthor = status.getLastCommitAuthor();
341 
342         // ----- r5: delete dir with no children  A/C ---------------
343         client.remove(new String[] {thisTest.getWCPath() + "/A/C"}, null,
344                       false);
345         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
346                               "A/C", NodeKind.dir,
347                               CommitItemStateFlags.Delete);
348         assertEquals("wrong revision number from commit",
349                      rev = client.commit(new String[] {thisTest.getWCPath()},
350                                          "log msg", true),
351                      expectedRev++);
352         thisTest.getWc().removeItem("A/C");
353         long CCommitRev = rev;
354 
355         // ----- r6: Add dir A/B/I ----------------------------------
356         dir = new File(thisTest.getWorkingCopy(), "A/B/I");
357         dir.mkdir();
358 
359         client.add(dir.getAbsolutePath(), true);
360         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
361                               "A/B/I", NodeKind.dir, CommitItemStateFlags.Add);
362         assertEquals("wrong revision number from commit",
363                      rev = client.commit(new String[] {thisTest.getWCPath()},
364                                          "log msg",  true),
365                      expectedRev++);
366         thisTest.getWc().addItem("A/B/I", null);
367         status = client.singleStatus(thisTest.getWCPath() + "/A/B/I", false);
368         long ICommitDate = status.getLastChangedDate().getTime();
369         long ICommitRev = rev;
370         String IAuthor = status.getLastCommitAuthor();
371 
372         // ----- r7: Update then commit prop change on root dir -----
373         thisTest.getWc().setRevision(rev);
374         assertEquals("wrong revision from update",
375                      client.update(thisTest.getWCPath(), null, true), rev);
376         thisTest.checkStatus();
377         client.propertySet(thisTest.getWCPath(), "propname", "propval", false);
378         thisTest.getWc().setItemPropStatus("", Status.Kind.modified);
379         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(), null,
380                               NodeKind.dir, CommitItemStateFlags.PropMods);
381         assertEquals("wrong revision number from commit",
382                      rev = client.commit(new String[] {thisTest.getWCPath()},
383                                          "log msg", true),
384                      expectedRev++);
385         thisTest.getWc().setItemWorkingCopyRevision("", rev);
386         thisTest.getWc().setItemPropStatus("", Status.Kind.normal);
387 
388         // ----- r8: Add a file A/D/H/nu ----------------------------
389         file = new File(thisTest.getWorkingCopy(), "A/D/H/nu");
390         pw = new PrintWriter(new FileOutputStream(file));
391         pw.print("This is the file 'nu'.");
392         pw.close();
393         client.add(file.getAbsolutePath(), false);
394         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
395                               "A/D/H/nu", NodeKind.file,
396                               CommitItemStateFlags.TextMods +
397                               CommitItemStateFlags.Add);
398         assertEquals("wrong revision number from commit",
399                      rev = client.commit(new String[] {thisTest.getWCPath()},
400                                          "log msg", true),
401                      expectedRev++);
402         thisTest.getWc().addItem("A/D/H/nu", "This is the file 'nu'.");
403         status = client.singleStatus(thisTest.getWCPath() + "/A/D/H/nu",
404                                      false);
405         long nuCommitDate = status.getLastChangedDate().getTime();
406         long nuCommitRev = rev;
407         String nuAuthor = status.getLastCommitAuthor();
408 
409         // ----- r9: Prop change on A/B/F ---------------------------
410         client.propertySet(thisTest.getWCPath() + "/A/B/F", "propname",
411                            "propval", false);
412         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
413                               "A/B/F", NodeKind.dir,
414                               CommitItemStateFlags.PropMods);
415         assertEquals("wrong revision number from commit",
416                      rev = client.commit(new String[] {thisTest.getWCPath()},
417                                          "log msg", true),
418                      expectedRev++);
419         thisTest.getWc().setItemPropStatus("A/B/F", Status.Kind.normal);
420         thisTest.getWc().setItemWorkingCopyRevision("A/B/F", rev);
421         status = client.singleStatus(thisTest.getWCPath() + "/A/B/F", false);
422         long FCommitDate = status.getLastChangedDate().getTime();
423         long FCommitRev = rev;
424         String FAuthor = status.getLastCommitAuthor();
425 
426         // ----- r10-11: Replace file A/D/H/chi with file -----------
427         client.remove(new String[] {thisTest.getWCPath() + "/A/D/H/chi"},
428                       null, false);
429         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
430                               "A/D/H/chi", NodeKind.file,
431                               CommitItemStateFlags.Delete);
432         assertEquals("wrong revision number from commit",
433                      rev = client.commit(new String[] {thisTest.getWCPath()},
434                                          "log msg", true),
435                      expectedRev++);
436         thisTest.getWc().removeItem("A/D/G/pi");
437 
438         file = new File(thisTest.getWorkingCopy(), "A/D/H/chi");
439         pw = new PrintWriter(new FileOutputStream(file));
440         pw.print("This is the replacement file 'chi'.");
441         pw.close();
442         client.add(file.getAbsolutePath(), false);
443         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
444                               "A/D/H/chi", NodeKind.file,
445                               CommitItemStateFlags.TextMods +
446                               CommitItemStateFlags.Add);
447         assertEquals("wrong revision number from commit",
448                      rev = client.commit(new String[] {thisTest.getWCPath()},
449                                          "log msg", true),
450                      expectedRev++);
451         thisTest.getWc().addItem("A/D/H/chi",
452                                  "This is the replacement file 'chi'.");
453         status = client.singleStatus(thisTest.getWCPath() + "/A/D/H/chi",
454                                      false);
455         long chiCommitDate = status.getLastChangedDate().getTime();
456         long chiCommitRev = rev;
457         String chiAuthor = status.getLastCommitAuthor();
458 
459         // ----- r12: Delete dir A/B/E with children ----------------
460         client.remove(new String[] {thisTest.getWCPath() + "/A/B/E"}, null,
461                       false);
462         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
463                               "A/B/E", NodeKind.dir,
464                               CommitItemStateFlags.Delete);
465         assertEquals("wrong revision number from commit",
466                      rev = client.commit(new String[] {thisTest.getWCPath()},
467                                          "log msg", true),
468                      expectedRev++);
469         thisTest.getWc().removeItem("A/B/E/alpha");
470         thisTest.getWc().removeItem("A/B/E/beta");
471         thisTest.getWc().removeItem("A/B/E");
472 
473         thisTest.getWc().setItemWorkingCopyRevision("A/B", rev);
474         assertEquals("wrong revision from update",
475                      client.update(thisTest.getWCPath() + "/A/B", null, true),
476                      rev);
477         Info Binfo = client.info(thisTest.getWCPath() + "/A/B");
478         long BCommitDate = Binfo.getLastChangedDate().getTime();
479         long BCommitRev = rev;
480         long ECommitRev = BCommitRev;
481         String BAuthor = Binfo.getAuthor();
482 
483         // ----- r13-14: Replace file A/D/H/psi with dir ------------
484         client.remove(new String[]{thisTest.getWCPath() + "/A/D/H/psi"}, null,
485                       false);
486         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
487                               "A/D/H/psi", NodeKind.file,
488                               CommitItemStateFlags.Delete);
489         assertEquals("wrong revision number from commit",
490                      rev = client.commit(new String[] {thisTest.getWCPath()},
491                                          "log msg", true),
492                      expectedRev++);
493         thisTest.getWc().removeItem("A/D/H/psi");
494         thisTest.getWc().setRevision(rev);
495         assertEquals("wrong revision from update",
496                      client.update(thisTest.getWCPath(), null, true), rev);
497         thisTest.getWc().addItem("A/D/H/psi", null);
498         dir = new File(thisTest.getWorkingCopy(), "A/D/H/psi");
499         dir.mkdir();
500         client.add(dir.getAbsolutePath(), true);
501         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
502                               "A/D/H/psi", NodeKind.dir,
503                               CommitItemStateFlags.Add);
504         assertEquals("wrong revision number from commit",
505                      rev = client.commit(new String[] {thisTest.getWCPath()},
506                                          "log msg", true),
507                      expectedRev++);
508         status = client.singleStatus(thisTest.getWCPath() + "/A/D/H/psi",
509                                      false);
510         long psiCommitDate = status.getLastChangedDate().getTime();
511         long psiCommitRev = rev;
512         String psiAuthor = status.getLastCommitAuthor();
513 
514         // ----- Check status of modfied WC then update it back
515         // -----  to rev 1 so it's out of date
516         thisTest.checkStatus();
517 
518         assertEquals("wrong revision from update",
519                      client.update(thisTest.getWCPath(),
520                                    Revision.getInstance(1), true),
521                      1);
522         thisTest.getWc().setRevision(1);
523 
524         thisTest.getWc().setItemOODInfo("A", psiCommitRev, psiAuthor,
525                                         psiCommitDate, NodeKind.dir);
526 
527         thisTest.getWc().setItemOODInfo("A/B", BCommitRev, BAuthor,
528                                         BCommitDate, NodeKind.dir);
529 
530         thisTest.getWc().addItem("A/B/I", null);
531         thisTest.getWc().setItemOODInfo("A/B/I", ICommitRev, IAuthor,
532                                         ICommitDate, NodeKind.dir);
533         thisTest.getWc().setItemTextStatus("A/B/I", Status.Kind.none);
534         thisTest.getWc().setItemNodeKind("A/B/I", NodeKind.unknown);
535 
536         thisTest.getWc().addItem("A/C", null);
537         thisTest.getWc().setItemReposLastCmtRevision("A/C", CCommitRev);
538         thisTest.getWc().setItemReposKind("A/C", NodeKind.dir);
539 
540         thisTest.getWc().addItem("A/B/E", null);
541         thisTest.getWc().setItemReposLastCmtRevision("A/B/E", ECommitRev);
542         thisTest.getWc().setItemReposKind("A/B/E", NodeKind.dir);
543         thisTest.getWc().addItem("A/B/E/alpha", "This is the file 'alpha'.");
544         thisTest.getWc().addItem("A/B/E/beta", "This is the file 'beta'.");
545 
546         thisTest.getWc().setItemPropStatus("A/B/F", Status.Kind.none);
547         thisTest.getWc().setItemOODInfo("A/B/F", FCommitRev, FAuthor,
548                                         FCommitDate, NodeKind.dir);
549 
550         thisTest.getWc().setItemOODInfo("A/D", psiCommitRev, psiAuthor,
551                                         psiCommitDate, NodeKind.dir);
552 
553         thisTest.getWc().setItemOODInfo("A/D/G", tauCommitRev, tauAuthor,
554                                         tauCommitDate, NodeKind.dir);
555 
556         thisTest.getWc().addItem("A/D/G/pi", "This is the file 'pi'.");
557         thisTest.getWc().setItemReposLastCmtRevision("A/D/G/pi", GCommitRev);
558         thisTest.getWc().setItemReposKind("A/D/G/pi", NodeKind.file);
559 
560         thisTest.getWc().setItemContent("A/D/G/rho",
561                                         "This is the file 'rho'.");
562         thisTest.getWc().setItemOODInfo("A/D/G/rho", rhoCommitRev, rhoAuthor,
563                                         rhoCommitDate, NodeKind.file);
564 
565         thisTest.getWc().setItemContent("A/D/G/tau",
566                                         "This is the file 'tau'.");
567         thisTest.getWc().setItemOODInfo("A/D/G/tau", tauCommitRev, tauAuthor,
568                                         tauCommitDate, NodeKind.file);
569 
570         thisTest.getWc().setItemOODInfo("A/D/H", psiCommitRev, psiAuthor,
571                                         psiCommitDate, NodeKind.dir);
572 
573         thisTest.getWc().setItemWorkingCopyRevision("A/D/H/nu",
574             Revision.SVN_INVALID_REVNUM);
575         thisTest.getWc().setItemTextStatus("A/D/H/nu", Status.Kind.none);
576         thisTest.getWc().setItemNodeKind("A/D/H/nu", NodeKind.unknown);
577         thisTest.getWc().setItemOODInfo("A/D/H/nu", nuCommitRev, nuAuthor,
578                                         nuCommitDate, NodeKind.file);
579 
580         thisTest.getWc().setItemContent("A/D/H/chi",
581                                         "This is the file 'chi'.");
582         thisTest.getWc().setItemOODInfo("A/D/H/chi", chiCommitRev, chiAuthor,
583                                         chiCommitDate, NodeKind.file);
584 
585         thisTest.getWc().removeItem("A/D/H/psi");
586         thisTest.getWc().addItem("A/D/H/psi", "This is the file 'psi'.");
587         // psi was replaced with a directory
588         thisTest.getWc().setItemOODInfo("A/D/H/psi", psiCommitRev, psiAuthor,
589                                         psiCommitDate, NodeKind.dir);
590 
591         thisTest.getWc().setItemPropStatus("", Status.Kind.none);
592         thisTest.getWc().setItemOODInfo("", psiCommitRev, psiAuthor,
593                                         psiCommitDate, NodeKind.dir);
594 
595         thisTest.checkStatus(true);
596     }
597 
598     /**
599      * Test the basic SVNClient.checkout functionality.
600      * @throws Throwable
601      */
testBasicCheckout()602     public void testBasicCheckout() throws Throwable
603     {
604         // build the test setup
605         OneTest thisTest = new OneTest();
606         try
607         {
608             // obstructed checkout must fail
609             client.checkout(thisTest.getUrl() + "/A", thisTest.getWCPath(),
610                             null, true);
611             fail("missing exception");
612         }
613         catch (ClientException expected)
614         {
615         }
616         // modify file A/mu
617         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
618         PrintWriter muWriter = new PrintWriter(new FileOutputStream(mu, true));
619         muWriter.print("appended mu text");
620         muWriter.close();
621         thisTest.getWc().setItemTextStatus("A/mu", Status.Kind.modified);
622 
623         // delete A/B/lambda without svn
624         File lambda = new File(thisTest.getWorkingCopy(), "A/B/lambda");
625         lambda.delete();
626         thisTest.getWc().setItemTextStatus("A/B/lambda", Status.Kind.missing);
627 
628         // remove A/D/G
629         client.remove(new String[]{thisTest.getWCPath() + "/A/D/G"}, null,
630                       false);
631         thisTest.getWc().setItemTextStatus("A/D/G", Status.Kind.deleted);
632         thisTest.getWc().setItemTextStatus("A/D/G/pi", Status.Kind.deleted);
633         thisTest.getWc().setItemTextStatus("A/D/G/rho", Status.Kind.deleted);
634         thisTest.getWc().setItemTextStatus("A/D/G/tau", Status.Kind.deleted);
635 
636         // check the status of the working copy
637         thisTest.checkStatus();
638 
639         // recheckout the working copy
640         client.checkout(thisTest.getUrl(), thisTest.getWCPath(), null, true);
641 
642         // deleted file should reapear
643         thisTest.getWc().setItemTextStatus("A/B/lambda", Status.Kind.normal);
644 
645         // check the status of the working copy
646         thisTest.checkStatus();
647     }
648 
649     /**
650      * Test the basic SVNClient.commit functionality.
651      * @throws Throwable
652      */
testBasicCommit()653     public void testBasicCommit() throws Throwable
654     {
655         // build the test setup
656         OneTest thisTest = new OneTest();
657 
658         // modify file A/mu
659         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
660         PrintWriter muWriter = new PrintWriter(new FileOutputStream(mu, true));
661         muWriter.print("appended mu text");
662         muWriter.close();
663         thisTest.getWc().setItemWorkingCopyRevision("A/mu", 2);
664         thisTest.getWc().setItemContent("A/mu",
665                 thisTest.getWc().getItemContent("A/mu") + "appended mu text");
666         addExpectedCommitItem(thisTest.getWCPath(),
667                 thisTest.getUrl(), "A/mu",NodeKind.file,
668                 CommitItemStateFlags.TextMods);
669 
670         // modify file A/D/G/rho
671         File rho = new File(thisTest.getWorkingCopy(), "A/D/G/rho");
672         PrintWriter rhoWriter =
673             new PrintWriter(new FileOutputStream(rho, true));
674         rhoWriter.print("new appended text for rho");
675         rhoWriter.close();
676         thisTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 2);
677         thisTest.getWc().setItemContent("A/D/G/rho",
678                 thisTest.getWc().getItemContent("A/D/G/rho")
679                 + "new appended text for rho");
680         addExpectedCommitItem(thisTest.getWCPath(),
681                 thisTest.getUrl(), "A/D/G/rho",NodeKind.file,
682                 CommitItemStateFlags.TextMods);
683 
684         // commit the changes
685         assertEquals("wrong revision number from commit",
686                      client.commit(new String[]{thisTest.getWCPath()},
687                                    "log msg",
688                                    true),
689                      2);
690 
691         // check the status of the working copy
692         thisTest.checkStatus();
693     }
694 
695     /**
696      * Test the basic property setting/getting functionality.
697      * @throws Throwable
698      */
testBasicProperties()699     public void testBasicProperties() throws Throwable
700     {
701         OneTest thisTest = new OneTest();
702         WC wc = thisTest.getWc();
703 
704         // Check getting properties the non-callback way
705         String itemPath = fileToSVNPath(new File(thisTest.getWCPath(),
706                                                  "iota"),
707                                         false);
708 
709         client.propertySet(itemPath, "abc", "def", false);
710         PropertyData[] properties = client.properties(itemPath);
711 
712         PropertyData prop = properties[0];
713         assertEquals("abc", prop.getName());
714         assertEquals("def", prop.getValue());
715 
716         wc.setItemPropStatus("iota", Status.Kind.modified);
717         thisTest.checkStatus();
718 
719         // Check getting properties the callback way
720         itemPath = fileToSVNPath(new File(thisTest.getWCPath(),
721                                           "/A/B/E/alpha"),
722                                  false);
723         client.propertyCreate(itemPath, "cqcq", "qrz", false, false);
724         ProplistCallbackImpl callback = new ProplistCallbackImpl();
725 
726         client.properties(itemPath, null, null, Depth.empty, null, callback);
727         Map propMap = callback.getProperties(itemPath);
728         Iterator it = propMap.keySet().iterator();
729 
730         while (it.hasNext())
731         {
732             String key = (String) it.next();
733             assertEquals("cqcq", key);
734             assertEquals("qrz", (String) propMap.get(key));
735         }
736 
737         wc.setItemPropStatus("A/B/E/alpha", Status.Kind.modified);
738         thisTest.checkStatus();
739     }
740 
741     /**
742      * Test the basic SVNClient.update functionality.
743      * @throws Throwable
744      */
testBasicUpdate()745     public void testBasicUpdate() throws Throwable
746     {
747         // build the test setup. Used for the changes
748         OneTest thisTest = new OneTest();
749 
750         // build the backup test setup. That is the one that will be updated
751         OneTest backupTest = thisTest.copy(".backup");
752 
753         // modify A/mu
754         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
755         PrintWriter muWriter = new PrintWriter(new FileOutputStream(mu, true));
756         muWriter.print("appended mu text");
757         muWriter.close();
758         thisTest.getWc().setItemWorkingCopyRevision("A/mu", 2);
759         thisTest.getWc().setItemContent("A/mu",
760                 thisTest.getWc().getItemContent("A/mu") + "appended mu text");
761         addExpectedCommitItem(thisTest.getWCPath(),
762                 thisTest.getUrl(), "A/mu",NodeKind.file,
763                 CommitItemStateFlags.TextMods);
764 
765         // modify A/D/G/rho
766         File rho = new File(thisTest.getWorkingCopy(), "A/D/G/rho");
767         PrintWriter rhoWriter =
768             new PrintWriter(new FileOutputStream(rho, true));
769         rhoWriter.print("new appended text for rho");
770         rhoWriter.close();
771         thisTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 2);
772         thisTest.getWc().setItemContent("A/D/G/rho",
773                 thisTest.getWc().getItemContent("A/D/G/rho")
774                 + "new appended text for rho");
775         addExpectedCommitItem(thisTest.getWCPath(),
776                 thisTest.getUrl(), "A/D/G/rho",NodeKind.file,
777                 CommitItemStateFlags.TextMods);
778 
779         // commit the changes
780         assertEquals("wrong revision number from commit",
781                      client.commit(new String[]{thisTest.getWCPath()},
782                                    "log msg",
783                                    true),
784                      2);
785 
786         // check the status of the working copy
787         thisTest.checkStatus();
788 
789         // update the backup test
790         assertEquals("wrong revision number from update",
791                      client.update(backupTest.getWCPath(), null, true),
792                      2);
793 
794         // set the expected working copy layout for the backup test
795         backupTest.getWc().setItemWorkingCopyRevision("A/mu", 2);
796         backupTest.getWc().setItemContent("A/mu",
797                 backupTest.getWc().getItemContent("A/mu") + "appended mu text");
798         backupTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 2);
799         backupTest.getWc().setItemContent("A/D/G/rho",
800                 backupTest.getWc().getItemContent("A/D/G/rho")
801                 + "new appended text for rho");
802 
803         // check the status of the working copy of the backup test
804         backupTest.checkStatus();
805     }
806 
807     /**
808      * Test basic SVNClient.mkdir with URL parameter functionality.
809      * @throws Throwable
810      */
testBasicMkdirUrl()811     public void testBasicMkdirUrl() throws Throwable
812     {
813         // build the test setup.
814         OneTest thisTest = new OneTest();
815 
816         // create Y and Y/Z directories in the repository
817         addExpectedCommitItem(null, thisTest.getUrl(), "Y", NodeKind.none,
818                               CommitItemStateFlags.Add);
819         addExpectedCommitItem(null, thisTest.getUrl(), "Y/Z", NodeKind.none,
820                               CommitItemStateFlags.Add);
821         client.mkdir(new String[]{thisTest.getUrl() + "/Y",
822                                   thisTest.getUrl() + "/Y/Z"}, "log_msg");
823 
824         // add the new directories the expected working copy layout
825         thisTest.getWc().addItem("Y", null);
826         thisTest.getWc().setItemWorkingCopyRevision("Y", 2);
827         thisTest.getWc().addItem("Y/Z", null);
828         thisTest.getWc().setItemWorkingCopyRevision("Y/Z", 2);
829 
830         // update the working copy
831         assertEquals("wrong revision from update",
832                      client.update(thisTest.getWCPath(), null, true),
833                      2);
834 
835         // check the status of the working copy
836         thisTest.checkStatus();
837     }
838 
839     /**
840      * Test the {@link SVNClientInterface.copy()} API.
841      * @since 1.5
842      */
testCopy()843     public void testCopy()
844         throws SubversionException, IOException
845     {
846         OneTest thisTest = new OneTest();
847 
848         WC wc = thisTest.getWc();
849         final Revision firstRevision = Revision.getInstance(1);
850         final Revision pegRevision = null;  // Defaults to Revision.HEAD.
851 
852         // Copy files from A/B/E to A/B/F.
853         String[] srcPaths = { "alpha", "beta" };
854         CopySource[] sources = new CopySource[srcPaths.length];
855         for (int i = 0; i < srcPaths.length; i++)
856         {
857             String fileName = srcPaths[i];
858             sources[i] =
859                 new CopySource(new File(thisTest.getWorkingCopy(),
860                                         "A/B/E/" + fileName).getPath(),
861                                firstRevision, pegRevision);
862             wc.addItem("A/B/F/" + fileName,
863                        wc.getItemContent("A/B/E/" + fileName));
864             wc.setItemWorkingCopyRevision("A/B/F/" + fileName, 2);
865             addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
866                                   "A/B/F/" + fileName, NodeKind.file,
867                                   CommitItemStateFlags.Add |
868                                   CommitItemStateFlags.IsCopy);
869         }
870         client.copy(sources,
871                     new File(thisTest.getWorkingCopy(), "A/B/F").getPath(),
872                     null, true, false, null);
873 
874         // Commit the changes, and check the state of the WC.
875         assertEquals("Unexpected WC revision number after commit",
876                      client.commit(new String[] { thisTest.getWCPath() },
877                                    "Copy files", true),
878                      2);
879         thisTest.checkStatus();
880 
881         assertExpectedSuggestion(thisTest.getUrl() + "/A/B/E/alpha", "A/B/F/alpha", thisTest);
882 
883         // Now test a WC to URL copy
884         CopySource wcSource[] = new CopySource[1];
885         wcSource[0] = new CopySource(new File(thisTest.getWorkingCopy(),
886                                         "A/B").getPath(), Revision.WORKING, Revision.WORKING);
887         client.commitMessageHandler(null);
888         client.copy(wcSource,
889                     thisTest.getUrl() + "/parent/A/B",
890                     "Copy WC to URL", true, true, null);
891 
892         // update the WC to get new folder and confirm the copy
893         assertEquals("wrong revision number from update",
894                      client.update(thisTest.getWCPath(), null, true),
895                      3);
896     }
897 
898     /**
899      * Test the {@link SVNClientInterface.move()} API.
900      * @since 1.5
901      */
testMove()902     public void testMove()
903         throws SubversionException, IOException
904     {
905         OneTest thisTest = new OneTest();
906         WC wc = thisTest.getWc();
907 
908         // Move files from A/B/E to A/B/F.
909         String[] srcPaths = { "alpha", "beta" };
910         for (int i = 0; i < srcPaths.length; i++)
911         {
912             String fileName = srcPaths[i];
913             srcPaths[i] = new File(thisTest.getWorkingCopy(),
914                                    "A/B/E/" + fileName).getPath();
915 
916             wc.addItem("A/B/F/" + fileName,
917                        wc.getItemContent("A/B/E/" + fileName));
918             wc.setItemWorkingCopyRevision("A/B/F/" + fileName, 2);
919             addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
920                                   "A/B/F/" + fileName, NodeKind.file,
921                                   CommitItemStateFlags.Add |
922                                   CommitItemStateFlags.IsCopy);
923 
924             wc.removeItem("A/B/E/" + fileName);
925             addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
926                                   "A/B/E/" + fileName, NodeKind.file,
927                                   CommitItemStateFlags.Delete);
928         }
929         client.move(srcPaths,
930                     new File(thisTest.getWorkingCopy(), "A/B/F").getPath(),
931                     null, false, true, false, null);
932 
933         // Commit the changes, and check the state of the WC.
934         assertEquals("Unexpected WC revision number after commit",
935                      client.commit(new String[] { thisTest.getWCPath() },
936                                    "Move files", true), 2);
937         thisTest.checkStatus();
938 
939         assertExpectedSuggestion(thisTest.getUrl() + "/A/B/E/alpha", "A/B/F/alpha", thisTest);
940     }
941 
942     /**
943      * Assert that the first merge source suggested for
944      * <code>destPath</code> at {@link Revision#WORKING} and {@link
945      * Revision#HEAD} is equivalent to <code>expectedSrc</code>.
946      * @exception SubversionException If retrieval of the copy source fails.
947      * @since 1.5
948      */
assertExpectedSuggestion(String expectedSrc, String destPath, OneTest thisTest)949     private void assertExpectedSuggestion(String expectedSrc,
950                                           String destPath, OneTest thisTest)
951         throws SubversionException
952     {
953         String wcPath = fileToSVNPath(new File(thisTest.getWCPath(),
954                                                destPath), false);
955         String[] suggestions = client.suggestMergeSources(wcPath,
956                                                           Revision.WORKING);
957         assertNotNull(suggestions);
958         assertTrue(suggestions.length >= 1);
959         assertTrue("Unexpected copy source path, expected " +
960                    expectedSrc + ", got " + suggestions[0],
961                    expectedSrc.equals(suggestions[0]));
962 
963         // Same test using URL
964         String url = thisTest.getUrl() + "/" + destPath;
965         suggestions = client.suggestMergeSources(url, Revision.HEAD);
966         assertNotNull(suggestions);
967         assertTrue(suggestions.length >= 1);
968         assertTrue("Unexpected copy source path, expected " +
969                    expectedSrc + ", got " + suggestions[0],
970                    expectedSrc.equals(suggestions[0]));
971 
972     }
973 
974     /**
975      * Tests that the passed start and end revision are contained
976      * within the array of revisions.
977      * @since 1.5
978      */
assertExpectedMergeRange(long start, long end, long[] revisions)979     private void assertExpectedMergeRange(long start, long end,
980                                           long[] revisions)
981     {
982         Arrays.sort(revisions);
983         for (int i = 0; i < revisions.length; i++) {
984             if (revisions[i] <= start) {
985                 for (int j = i; j < revisions.length; j++)
986                 {
987                     if (end <= revisions[j])
988                         return;
989                 }
990                 fail("End revision: " + end + " was not in range: " + revisions[0] +
991                         " : " + revisions[revisions.length - 1]);
992                 return;
993             }
994         }
995         fail("Start revision: " + start + " was not in range: " + revisions[0] +
996                 " : " + revisions[revisions.length - 1]);
997     }
998 
999     /**
1000      * Test the basic SVNClient.update functionality with concurrent
1001      * changes in the repository and the working copy.
1002      * @throws Throwable
1003      */
testBasicMergingUpdate()1004     public void testBasicMergingUpdate() throws Throwable
1005     {
1006         // build the first working copy
1007         OneTest thisTest = new OneTest();
1008 
1009         // append 10 lines to A/mu
1010         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
1011         PrintWriter muWriter = new PrintWriter(new FileOutputStream(mu, true));
1012         String muContent = thisTest.getWc().getItemContent("A/mu");
1013         for (int i = 2; i < 11; i++)
1014         {
1015             muWriter.print("\nThis is line " + i + " in mu");
1016             muContent = muContent + "\nThis is line " + i + " in mu";
1017         }
1018         muWriter.close();
1019         thisTest.getWc().setItemWorkingCopyRevision("A/mu", 2);
1020         thisTest.getWc().setItemContent("A/mu", muContent);
1021         addExpectedCommitItem(thisTest.getWorkingCopy().getAbsolutePath(),
1022                               thisTest.getUrl(), "A/mu", NodeKind.file,
1023                               CommitItemStateFlags.TextMods);
1024 
1025         // append 10 line to A/D/G/rho
1026         File rho = new File(thisTest.getWorkingCopy(), "A/D/G/rho");
1027         PrintWriter rhoWriter =
1028             new PrintWriter(new FileOutputStream(rho, true));
1029         String rhoContent = thisTest.getWc().getItemContent("A/D/G/rho");
1030         for (int i = 2; i < 11; i++)
1031         {
1032             rhoWriter.print("\nThis is line " + i + " in rho");
1033             rhoContent = rhoContent + "\nThis is line " + i + " in rho";
1034         }
1035         rhoWriter.close();
1036         thisTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 2);
1037         thisTest.getWc().setItemContent("A/D/G/rho", rhoContent);
1038         addExpectedCommitItem(thisTest.getWCPath(),
1039                               thisTest.getUrl(), "A/D/G/rho", NodeKind.file,
1040                               CommitItemStateFlags.TextMods);
1041 
1042         // commit the changes
1043         assertEquals("wrong revision number from commit",
1044                      client.commit(new String[]{thisTest.getWCPath()},
1045                                    "log msg",
1046                                    true),
1047                      2);
1048 
1049         // check the status of the first working copy
1050         thisTest.checkStatus();
1051 
1052         // create a backup copy of the working copy
1053         OneTest backupTest = thisTest.copy(".backup");
1054 
1055         // change the last line of A/mu in the first working copy
1056         muWriter = new PrintWriter(new FileOutputStream(mu, true));
1057         muContent = thisTest.getWc().getItemContent("A/mu");
1058         muWriter.print(" Appended to line 10 of mu");
1059         muContent = muContent + " Appended to line 10 of mu";
1060         muWriter.close();
1061         thisTest.getWc().setItemWorkingCopyRevision("A/mu", 3);
1062         thisTest.getWc().setItemContent("A/mu", muContent);
1063         addExpectedCommitItem(thisTest.getWCPath(),
1064                               thisTest.getUrl(), "A/mu", NodeKind.file,
1065                               CommitItemStateFlags.TextMods);
1066 
1067         // change the last line of A/mu in the first working copy
1068         rhoWriter = new PrintWriter(new FileOutputStream(rho, true));
1069         rhoContent = thisTest.getWc().getItemContent("A/D/G/rho");
1070         rhoWriter.print(" Appended to line 10 of rho");
1071         rhoContent = rhoContent + " Appended to line 10 of rho";
1072         rhoWriter.close();
1073         thisTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 3);
1074         thisTest.getWc().setItemContent("A/D/G/rho", rhoContent);
1075         addExpectedCommitItem(thisTest.getWCPath(),
1076                               thisTest.getUrl(), "A/D/G/rho", NodeKind.file,
1077                               CommitItemStateFlags.TextMods);
1078 
1079         // commit these changes to the repository
1080         assertEquals("wrong revision number from commit",
1081                      client.commit(new String[]{thisTest.getWCPath()},
1082                                    "log msg",
1083                                    true),
1084                      3);
1085 
1086         // check the status of the first working copy
1087         thisTest.checkStatus();
1088 
1089         // modify the first line of A/mu in the backup working copy
1090         mu = new File(backupTest.getWorkingCopy(), "A/mu");
1091         muWriter = new PrintWriter(new FileOutputStream(mu));
1092         muWriter.print("This is the new line 1 in the backup copy of mu");
1093         muContent = "This is the new line 1 in the backup copy of mu";
1094         for (int i = 2; i < 11; i++)
1095         {
1096             muWriter.print("\nThis is line " + i + " in mu");
1097             muContent = muContent + "\nThis is line " + i + " in mu";
1098         }
1099         muWriter.close();
1100         backupTest.getWc().setItemWorkingCopyRevision("A/mu", 3);
1101         muContent = muContent + " Appended to line 10 of mu";
1102         backupTest.getWc().setItemContent("A/mu", muContent);
1103         backupTest.getWc().setItemTextStatus("A/mu", Status.Kind.modified);
1104 
1105         // modify the first line of A/D/G/rho in the backup working copy
1106         rho = new File(backupTest.getWorkingCopy(), "A/D/G/rho");
1107         rhoWriter = new PrintWriter(new FileOutputStream(rho));
1108         rhoWriter.print("This is the new line 1 in the backup copy of rho");
1109         rhoContent = "This is the new line 1 in the backup copy of rho";
1110         for (int i = 2; i < 11; i++)
1111         {
1112             rhoWriter.print("\nThis is line " + i + " in rho");
1113             rhoContent = rhoContent + "\nThis is line " + i + " in rho";
1114         }
1115         rhoWriter.close();
1116         backupTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 3);
1117         rhoContent = rhoContent + " Appended to line 10 of rho";
1118         backupTest.getWc().setItemContent("A/D/G/rho", rhoContent);
1119         backupTest.getWc().setItemTextStatus("A/D/G/rho", Status.Kind.modified);
1120 
1121         // update the backup working copy
1122         assertEquals("wrong revision number from update",
1123                      client.update(backupTest.getWCPath(), null, true),
1124                      3);
1125 
1126         // check the status of the backup working copy
1127         backupTest.checkStatus();
1128     }
1129 
1130     /**
1131      * Test the basic SVNClient.update functionality with concurrent
1132      * changes in the repository and the working copy that generate
1133      * conflicts.
1134      * @throws Throwable
1135      */
testBasicConflict()1136     public void testBasicConflict() throws Throwable
1137     {
1138         // build the first working copy
1139         OneTest thisTest = new OneTest();
1140 
1141         // copy the first working copy to the backup working copy
1142         OneTest backupTest = thisTest.copy(".backup");
1143 
1144         // append a line to A/mu in the first working copy
1145         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
1146         PrintWriter muWriter = new PrintWriter(new FileOutputStream(mu, true));
1147         String muContent = thisTest.getWc().getItemContent("A/mu");
1148         muWriter.print("\nOriginal appended text for mu");
1149         muContent = muContent + "\nOriginal appended text for mu";
1150         muWriter.close();
1151         thisTest.getWc().setItemWorkingCopyRevision("A/mu", 2);
1152         thisTest.getWc().setItemContent("A/mu", muContent);
1153         addExpectedCommitItem(thisTest.getWCPath(),
1154                               thisTest.getUrl(), "A/mu", NodeKind.file,
1155                               CommitItemStateFlags.TextMods);
1156 
1157         // append a line to A/D/G/rho in the first working copy
1158         File rho = new File(thisTest.getWorkingCopy(), "A/D/G/rho");
1159         PrintWriter rhoWriter =
1160             new PrintWriter(new FileOutputStream(rho, true));
1161         String rhoContent = thisTest.getWc().getItemContent("A/D/G/rho");
1162         rhoWriter.print("\nOriginal appended text for rho");
1163         rhoContent = rhoContent + "\nOriginal appended text for rho";
1164         rhoWriter.close();
1165         thisTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 2);
1166         thisTest.getWc().setItemContent("A/D/G/rho", rhoContent);
1167         addExpectedCommitItem(thisTest.getWCPath(),
1168                               thisTest.getUrl(), "A/D/G/rho", NodeKind.file,
1169                               CommitItemStateFlags.TextMods);
1170 
1171         // commit the changes in the first working copy
1172         assertEquals("wrong revision number from commit",
1173                      client.commit(new String[]{thisTest.getWCPath()},
1174                                    "log msg",
1175                                    true),
1176                      2);
1177 
1178         // test the status of the working copy after the commit
1179         thisTest.checkStatus();
1180 
1181         // append a different line to A/mu in the backup working copy
1182         mu = new File(backupTest.getWorkingCopy(), "A/mu");
1183         muWriter = new PrintWriter(new FileOutputStream(mu, true));
1184         muWriter.print("\nConflicting appended text for mu");
1185         muContent = "<<<<<<< .mine\nThis is the file 'mu'.\n"+
1186                     "Conflicting appended text for mu=======\n"+
1187                     "This is the file 'mu'.\n"+
1188                     "Original appended text for mu>>>>>>> .r2";
1189         muWriter.close();
1190         backupTest.getWc().setItemWorkingCopyRevision("A/mu", 2);
1191         backupTest.getWc().setItemContent("A/mu", muContent);
1192         backupTest.getWc().setItemTextStatus("A/mu", Status.Kind.conflicted);
1193         backupTest.getWc().addItem("A/mu.r1", "");
1194         backupTest.getWc().setItemNodeKind("A/mu.r1", NodeKind.unknown);
1195         backupTest.getWc().setItemTextStatus("A/mu.r1",
1196                                              Status.Kind.unversioned);
1197         backupTest.getWc().addItem("A/mu.r2", "");
1198         backupTest.getWc().setItemNodeKind("A/mu.r2", NodeKind.unknown);
1199         backupTest.getWc().setItemTextStatus("A/mu.r2",
1200                                              Status.Kind.unversioned);
1201         backupTest.getWc().addItem("A/mu.mine", "");
1202         backupTest.getWc().setItemNodeKind("A/mu.mine", NodeKind.unknown);
1203         backupTest.getWc().setItemTextStatus("A/mu.mine",
1204                                              Status.Kind.unversioned);
1205 
1206         // append a different line to A/D/G/rho in the backup working copy
1207         rho = new File(backupTest.getWorkingCopy(), "A/D/G/rho");
1208         rhoWriter = new PrintWriter(new FileOutputStream(rho, true));
1209         rhoWriter.print("\nConflicting appended text for rho");
1210         rhoContent = "<<<<<<< .mine\nThis is the file 'rho'.\n"+
1211                     "Conflicting appended text for rho=======\n"+
1212                     "his is the file 'rho'.\n"+
1213                     "Original appended text for rho>>>>>>> .r2";
1214         rhoWriter.close();
1215         backupTest.getWc().setItemWorkingCopyRevision("A/D/G/rho", 2);
1216         backupTest.getWc().setItemContent("A/D/G/rho", rhoContent);
1217         backupTest.getWc().setItemTextStatus("A/D/G/rho",
1218                                              Status.Kind.conflicted);
1219         backupTest.getWc().addItem("A/D/G/rho.r1", "");
1220         backupTest.getWc().setItemNodeKind("A/D/G/rho.r1", NodeKind.unknown);
1221         backupTest.getWc().setItemTextStatus("A/D/G/rho.r1",
1222                                              Status.Kind.unversioned);
1223         backupTest.getWc().addItem("A/D/G/rho.r2", "");
1224         backupTest.getWc().setItemNodeKind("A/D/G/rho.r2", NodeKind.unknown);
1225         backupTest.getWc().setItemTextStatus("A/D/G/rho.r2",
1226                                              Status.Kind.unversioned);
1227         backupTest.getWc().addItem("A/D/G/rho.mine", "");
1228         backupTest.getWc().setItemNodeKind("A/D/G/rho.mine", NodeKind.unknown);
1229         backupTest.getWc().setItemTextStatus("A/D/G/rho.mine",
1230                                              Status.Kind.unversioned);
1231 
1232         // update the backup working copy from the repository
1233         assertEquals("wrong revision number from update",
1234                      client.update(backupTest.getWCPath(), null, true),
1235                      2);
1236 
1237         // check the status of the backup working copy
1238         backupTest.checkStatus();
1239 
1240         // flag A/mu as resolved
1241         client.resolved(backupTest.getWCPath()+"/A/mu", false);
1242         backupTest.getWc().setItemTextStatus("A/mu", Status.Kind.modified);
1243         backupTest.getWc().removeItem("A/mu.r1");
1244         backupTest.getWc().removeItem("A/mu.r2");
1245         backupTest.getWc().removeItem("A/mu.mine");
1246 
1247         // flag A/D/G/rho as resolved
1248         client.resolved(backupTest.getWCPath()+"/A/D/G/rho", false);
1249         backupTest.getWc().setItemTextStatus("A/D/G/rho",
1250                                              Status.Kind.modified);
1251         backupTest.getWc().removeItem("A/D/G/rho.r1");
1252         backupTest.getWc().removeItem("A/D/G/rho.r2");
1253         backupTest.getWc().removeItem("A/D/G/rho.mine");
1254 
1255         // check the status after the conflicts are flaged as resolved
1256         backupTest.checkStatus();
1257     }
1258 
1259     /**
1260      * Test the basic SVNClient.cleanup functionality.
1261      * Without a way to force a lock, this test just verifies
1262      * the method can be called successfully.
1263      * @throws Throwable
1264      */
testBasicCleanup()1265     public void testBasicCleanup() throws Throwable
1266     {
1267         // create a test working copy
1268         OneTest thisTest = new OneTest();
1269 
1270         // run cleanup
1271         client.cleanup(thisTest.getWCPath());
1272 
1273     }
1274 
1275     /**
1276      * Test the basic SVNClient.revert functionality.
1277      * @throws Throwable
1278      */
testBasicRevert()1279     public void testBasicRevert() throws Throwable
1280     {
1281         // create a test working copy
1282         OneTest thisTest = new OneTest();
1283 
1284         // modify A/B/E/beta
1285         File file = new File(thisTest.getWorkingCopy(), "A/B/E/beta");
1286         PrintWriter pw = new PrintWriter(new FileOutputStream(file, true));
1287         pw.print("Added some text to 'beta'.");
1288         pw.close();
1289         thisTest.getWc().setItemTextStatus("A/B/E/beta", Status.Kind.modified);
1290 
1291         // modify iota
1292         file = new File(thisTest.getWorkingCopy(), "iota");
1293         pw = new PrintWriter(new FileOutputStream(file, true));
1294         pw.print("Added some text to 'iota'.");
1295         pw.close();
1296         thisTest.getWc().setItemTextStatus("iota", Status.Kind.modified);
1297 
1298         // modify A/D/G/rho
1299         file = new File(thisTest.getWorkingCopy(), "A/D/G/rho");
1300         pw = new PrintWriter(new FileOutputStream(file, true));
1301         pw.print("Added some text to 'rho'.");
1302         pw.close();
1303         thisTest.getWc().setItemTextStatus("A/D/G/rho", Status.Kind.modified);
1304 
1305         // create new file A/D/H/zeta and add it to subversion
1306         file = new File(thisTest.getWorkingCopy(), "A/D/H/zeta");
1307         pw = new PrintWriter(new FileOutputStream(file, true));
1308         pw.print("Added some text to 'zeta'.");
1309         pw.close();
1310         thisTest.getWc().addItem("A/D/H/zeta", "Added some text to 'zeta'.");
1311         thisTest.getWc().setItemTextStatus("A/D/H/zeta", Status.Kind.added);
1312         client.add(file.getAbsolutePath(), false);
1313 
1314         // test the status of the working copy
1315         thisTest.checkStatus();
1316 
1317         // revert the changes
1318         client.revert(thisTest.getWCPath()+"/A/B/E/beta", false);
1319         thisTest.getWc().setItemTextStatus("A/B/E/beta", Status.Kind.normal);
1320         client.revert(thisTest.getWCPath()+"/iota", false);
1321         thisTest.getWc().setItemTextStatus("iota", Status.Kind.normal);
1322         client.revert(thisTest.getWCPath()+"/A/D/G/rho", false);
1323         thisTest.getWc().setItemTextStatus("A/D/G/rho", Status.Kind.normal);
1324         client.revert(thisTest.getWCPath()+"/A/D/H/zeta", false);
1325         thisTest.getWc().setItemTextStatus("A/D/H/zeta",
1326                 Status.Kind.unversioned);
1327         thisTest.getWc().setItemNodeKind("A/D/H/zeta", NodeKind.unknown);
1328 
1329         // test the status of the working copy
1330         thisTest.checkStatus();
1331 
1332         // delete A/B/E/beta and revert the change
1333         file = new File(thisTest.getWorkingCopy(), "A/B/E/beta");
1334         file.delete();
1335         client.revert(file.getAbsolutePath(), false);
1336 
1337         // resurected file should not be readonly
1338         assertTrue("reverted file is not readonly",
1339                 file.canWrite()&& file.canRead());
1340 
1341         // test the status of the working copy
1342         thisTest.checkStatus();
1343 
1344         // create & add the directory X
1345         client.mkdir(new String[] {thisTest.getWCPath()+"/X"}, null);
1346         thisTest.getWc().addItem("X", null);
1347         thisTest.getWc().setItemTextStatus("X", Status.Kind.added);
1348 
1349         // test the status of the working copy
1350         thisTest.checkStatus();
1351 
1352         // remove & revert X
1353         removeDirOrFile(new File(thisTest.getWorkingCopy(), "X"));
1354         client.revert(thisTest.getWCPath()+"/X", false);
1355         thisTest.getWc().removeItem("X");
1356 
1357         // test the status of the working copy
1358         thisTest.checkStatus();
1359 
1360         // delete the directory A/B/E
1361         client.remove(new String[] {thisTest.getWCPath()+"/A/B/E"}, null, true);
1362         removeDirOrFile(new File(thisTest.getWorkingCopy(), "A/B/E"));
1363         thisTest.getWc().setItemTextStatus("A/B/E", Status.Kind.deleted);
1364         thisTest.getWc().setItemTextStatus("A/B/E/alpha", Status.Kind.deleted);
1365         thisTest.getWc().setItemTextStatus("A/B/E/beta", Status.Kind.deleted);
1366 
1367         // test the status of the working copy
1368         thisTest.checkStatus();
1369 
1370         // revert A/B/E -> this will resurect it
1371         client.revert(thisTest.getWCPath()+"/A/B/E", true);
1372         thisTest.getWc().setItemTextStatus("A/B/E", Status.Kind.normal);
1373         thisTest.getWc().setItemTextStatus("A/B/E/alpha", Status.Kind.normal);
1374         thisTest.getWc().setItemTextStatus("A/B/E/beta", Status.Kind.normal);
1375 
1376         // test the status of the working copy
1377         thisTest.checkStatus();
1378     }
1379 
1380     /**
1381      * Test the basic SVNClient.switch functionality.
1382      * @throws Throwable
1383      */
testBasicSwitch()1384     public void testBasicSwitch() throws Throwable
1385     {
1386         // create the test working copy
1387         OneTest thisTest = new OneTest();
1388 
1389         // switch iota to A/D/gamma
1390         String iotaPath = thisTest.getWCPath() + "/iota";
1391         String gammaUrl = thisTest.getUrl() + "/A/D/gamma";
1392         thisTest.getWc().setItemContent("iota",
1393                 greekWC.getItemContent("A/D/gamma"));
1394         thisTest.getWc().setItemIsSwitched("iota", true);
1395         client.doSwitch(iotaPath, gammaUrl, null, true);
1396 
1397         // check the status of the working copy
1398         thisTest.checkStatus();
1399 
1400         // switch A/D/H to /A/D/G
1401         String adhPath = thisTest.getWCPath() + "/A/D/H";
1402         String adgURL = thisTest.getUrl() + "/A/D/G";
1403         thisTest.getWc().setItemIsSwitched("A/D/H",true);
1404         thisTest.getWc().removeItem("A/D/H/chi");
1405         thisTest.getWc().removeItem("A/D/H/omega");
1406         thisTest.getWc().removeItem("A/D/H/psi");
1407         thisTest.getWc().addItem("A/D/H/pi",
1408                 thisTest.getWc().getItemContent("A/D/G/pi"));
1409         thisTest.getWc().addItem("A/D/H/rho",
1410                 thisTest.getWc().getItemContent("A/D/G/rho"));
1411         thisTest.getWc().addItem("A/D/H/tau",
1412                 thisTest.getWc().getItemContent("A/D/G/tau"));
1413         client.doSwitch(adhPath, adgURL, null, true);
1414 
1415         // check the status of the working copy
1416         thisTest.checkStatus();
1417     }
1418 
1419     /**
1420      * Test the basic SVNClient.remove functionality.
1421      * @throws Throwable
1422      */
testBasicDelete()1423     public void testBasicDelete() throws Throwable
1424     {
1425         // create the test working copy
1426         OneTest thisTest = new OneTest();
1427 
1428         // modify A/D/H/chi
1429         File file = new File(thisTest.getWorkingCopy(), "A/D/H/chi");
1430         PrintWriter pw = new PrintWriter(new FileOutputStream(file, true));
1431         pw.print("added to chi");
1432         pw.close();
1433         thisTest.getWc().setItemTextStatus("A/D/H/chi", Status.Kind.modified);
1434 
1435         // set a property on A/D/G/rho file
1436         client.propertySet(thisTest.getWCPath()+"/A/D/G/rho", "abc", "def",
1437                 true);
1438         thisTest.getWc().setItemPropStatus("A/D/G/rho", Status.Kind.modified);
1439 
1440         // set a property on A/B/F directory
1441         client.propertySet(thisTest.getWCPath()+"/A/B/F", "abc", "def", false);
1442         thisTest.getWc().setItemPropStatus("A/B/F", Status.Kind.modified);
1443 
1444         // create a unversioned A/C/sigma file
1445         file = new File(thisTest.getWCPath(),"A/C/sigma");
1446         pw = new PrintWriter(new FileOutputStream(file));
1447         pw.print("unversioned sigma");
1448         pw.close();
1449         thisTest.getWc().addItem("A/C/sigma", "unversioned sigma");
1450         thisTest.getWc().setItemTextStatus("A/C/sigma", Status.Kind.unversioned);
1451         thisTest.getWc().setItemNodeKind("A/C/sigma", NodeKind.unknown);
1452 
1453         // create unversioned directory A/C/Q
1454         file = new File(thisTest.getWCPath(), "A/C/Q");
1455         file.mkdir();
1456         thisTest.getWc().addItem("A/C/Q", null);
1457         thisTest.getWc().setItemNodeKind("A/C/Q", NodeKind.unknown);
1458         thisTest.getWc().setItemTextStatus("A/C/Q", Status.Kind.unversioned);
1459 
1460         // create & add the directory A/B/X
1461         file = new File(thisTest.getWCPath(), "A/B/X");
1462         client.mkdir(new String[] {file.getAbsolutePath()}, null);
1463         thisTest.getWc().addItem("A/B/X", null);
1464         thisTest.getWc().setItemTextStatus("A/B/X", Status.Kind.added);
1465 
1466         // create & add the file A/B/X/xi
1467         file = new File(file, "xi");
1468         pw = new PrintWriter(new FileOutputStream(file));
1469         pw.print("added xi");
1470         pw.close();
1471         client.add(file.getAbsolutePath(), false);
1472         thisTest.getWc().addItem("A/B/X/xi", "added xi");
1473         thisTest.getWc().setItemTextStatus("A/B/X/xi", Status.Kind.added);
1474 
1475         // create & add the directory A/B/Y
1476         file = new File(thisTest.getWCPath(), "A/B/Y");
1477         client.mkdir(new String[] {file.getAbsolutePath()}, null);
1478         thisTest.getWc().addItem("A/B/Y", null);
1479         thisTest.getWc().setItemTextStatus("A/B/Y", Status.Kind.added);
1480 
1481         // test the status of the working copy
1482         thisTest.checkStatus();
1483 
1484         // the following removes should all fail without force
1485 
1486         try
1487         {
1488             // remove of A/D/H/chi without force should fail, because it is
1489             // modified
1490             client.remove(new String[] {thisTest.getWCPath()+"/A/D/H/chi"},
1491                     null, false);
1492             fail("missing exception");
1493         }
1494         catch(ClientException expected)
1495         {
1496         }
1497 
1498         try
1499         {
1500             // remove of A/D/H without force should fail, because A/D/H/chi is
1501             // modified
1502             client.remove(new String[] {thisTest.getWCPath()+"/A/D/H"}, null,
1503                     false);
1504             fail("missing exception");
1505         }
1506         catch(ClientException expected)
1507         {
1508         }
1509 
1510         try
1511         {
1512             // remove of A/D/G/rho without force should fail, because it has
1513             // a new property
1514             client.remove(new String[] {thisTest.getWCPath()+"/A/D/G/rho"},
1515                     null, false);
1516             fail("missing exception");
1517         }
1518         catch(ClientException expected)
1519         {
1520         }
1521 
1522         try
1523         {
1524             // remove of A/D/G without force should fail, because A/D/G/rho has
1525             // a new property
1526             client.remove(new String[] {thisTest.getWCPath()+"/A/D/G"}, null,
1527                     false);
1528             fail("missing exception");
1529         }
1530         catch(ClientException expected)
1531         {
1532         }
1533 
1534         try
1535         {
1536             // remove of A/B/F without force should fail, because it has
1537             // a new property
1538             client.remove(new String[] {thisTest.getWCPath()+"/A/B/F"}, null,
1539                     false);
1540             fail("missing exception");
1541         }
1542         catch(ClientException expected)
1543         {
1544         }
1545 
1546         try
1547         {
1548             // remove of A/B without force should fail, because A/B/F has
1549             // a new property
1550             client.remove(new String[] {thisTest.getWCPath()+"/A/B"}, null,
1551                     false);
1552             fail("missing exception");
1553         }
1554         catch(ClientException expected)
1555         {
1556         }
1557 
1558         try
1559         {
1560             // remove of A/C/sigma without force should fail, because it is
1561             // unversioned
1562             client.remove(new String[] {thisTest.getWCPath()+"/A/C/sigma"},
1563                     null, false);
1564             fail("missing exception");
1565         }
1566         catch(ClientException expected)
1567         {
1568         }
1569 
1570         try
1571         {
1572             // remove of A/C without force should fail, because A/C/sigma is
1573             // unversioned
1574             client.remove(new String[] {thisTest.getWCPath()+"/A/C"}, null,
1575                     false);
1576             fail("missing exception");
1577         }
1578         catch(ClientException expected)
1579         {
1580         }
1581 
1582         try
1583         {
1584             // remove of A/B/X without force should fail, because it is new
1585             client.remove(new String[] {thisTest.getWCPath()+"/A/B/X"}, null,
1586                     false);
1587             fail("missing exception");
1588         }
1589         catch(ClientException expected)
1590         {
1591         }
1592 
1593         // check the status of the working copy
1594         thisTest.checkStatus();
1595 
1596         // the following removes should all work
1597         client.remove(new String[] {thisTest.getWCPath()+"/A/B/E"}, null,
1598                 false);
1599         thisTest.getWc().setItemTextStatus("A/B/E",Status.Kind.deleted);
1600         thisTest.getWc().setItemTextStatus("A/B/E/alpha",Status.Kind.deleted);
1601         thisTest.getWc().setItemTextStatus("A/B/E/beta",Status.Kind.deleted);
1602         client.remove(new String[] {thisTest.getWCPath()+"/A/D/H"}, null, true);
1603         thisTest.getWc().setItemTextStatus("A/D/H",Status.Kind.deleted);
1604         thisTest.getWc().setItemTextStatus("A/D/H/chi",Status.Kind.deleted);
1605         thisTest.getWc().setItemTextStatus("A/D/H/omega",Status.Kind.deleted);
1606         thisTest.getWc().setItemTextStatus("A/D/H/psi",Status.Kind.deleted);
1607         client.remove(new String[] {thisTest.getWCPath()+"/A/D/G"}, null, true);
1608         thisTest.getWc().setItemTextStatus("A/D/G",Status.Kind.deleted);
1609         thisTest.getWc().setItemTextStatus("A/D/G/rho",Status.Kind.deleted);
1610         thisTest.getWc().setItemPropStatus("A/D/G/rho", Status.Kind.none);
1611         thisTest.getWc().setItemTextStatus("A/D/G/pi",Status.Kind.deleted);
1612         thisTest.getWc().setItemTextStatus("A/D/G/tau",Status.Kind.deleted);
1613         client.remove(new String[] {thisTest.getWCPath()+"/A/B/F"}, null, true);
1614         thisTest.getWc().setItemTextStatus("A/B/F",Status.Kind.deleted);
1615         thisTest.getWc().setItemPropStatus("A/B/F", Status.Kind.none);
1616         client.remove(new String[] {thisTest.getWCPath()+"/A/C"}, null, true);
1617         thisTest.getWc().setItemTextStatus("A/C",Status.Kind.deleted);
1618         client.remove(new String[] {thisTest.getWCPath()+"/A/B/X"}, null, true);
1619         file = new File(thisTest.getWorkingCopy(), "iota");
1620         file.delete();
1621         client.remove(new String[] {file.getAbsolutePath()}, null, true);
1622         thisTest.getWc().setItemTextStatus("iota",Status.Kind.deleted);
1623         file = new File(thisTest.getWorkingCopy(), "A/D/gamma");
1624         file.delete();
1625         client.remove(new String[] {file.getAbsolutePath()}, null, false);
1626         thisTest.getWc().setItemTextStatus("A/D/gamma",Status.Kind.deleted);
1627         client.remove(new String[] {file.getAbsolutePath()}, null, true);
1628         client.remove(new String[] {thisTest.getWCPath()+"/A/B/E"}, null,
1629                 false);
1630         thisTest.getWc().removeItem("A/B/X");
1631         thisTest.getWc().removeItem("A/B/X/xi");
1632         thisTest.getWc().removeItem("A/C/sigma");
1633         thisTest.getWc().removeItem("A/C/Q");
1634         thisTest.checkStatus();
1635         client.remove(new String[] {thisTest.getWCPath()+"/A/D"},null, true);
1636         thisTest.getWc().setItemTextStatus("A/D", Status.Kind.deleted);
1637         thisTest.getWc().removeItem("A/D/Y");
1638 
1639         // check the status of the working copy
1640         thisTest.checkStatus();
1641 
1642         // confirm that the file are really deleted
1643         assertFalse("failed to remove text modified file",
1644                 new File(thisTest.getWorkingCopy(), "A/D/G/rho").exists());
1645         assertFalse("failed to remove prop modified file",
1646                 new File(thisTest.getWorkingCopy(), "A/D/H/chi").exists());
1647         assertFalse("failed to remove unversioned file",
1648                 new File(thisTest.getWorkingCopy(), "A/C/sigma").exists());
1649         assertFalse("failed to remove unmodified file",
1650                 new File(thisTest.getWorkingCopy(), "A/B/E/alpha").exists());
1651         file = new File(thisTest.getWorkingCopy(),"A/B/F");
1652         assertFalse("failed to remove versioned dir", file.exists());
1653         assertFalse("failed to remove unversioned dir",
1654                 new File(thisTest.getWorkingCopy(), "A/C/Q").exists());
1655         assertFalse("failed to remove added dir",
1656                 new File(thisTest.getWorkingCopy(), "A/B/X").exists());
1657 
1658         // delete unversioned file foo
1659         file = new File(thisTest.getWCPath(),"foo");
1660         pw = new PrintWriter(new FileOutputStream(file));
1661         pw.print("unversioned foo");
1662         pw.close();
1663         client.remove(new String[] {file.getAbsolutePath()}, null, true);
1664         assertFalse("failed to remove unversioned file foo", file.exists());
1665 
1666         try
1667         {
1668             // delete non-existent file foo
1669             client.remove(new String[] {file.getAbsolutePath()}, null, true);
1670             fail("missing exception");
1671         }
1672         catch(ClientException expected)
1673         {
1674         }
1675 
1676         // delete file iota in the repository
1677         addExpectedCommitItem(null, thisTest.getUrl(), "iota", NodeKind.none,
1678                 CommitItemStateFlags.Delete);
1679         client.remove(new String[] {thisTest.getUrl()+"/iota"},
1680                 "delete iota URL", false);
1681     }
1682 
testBasicCheckoutDeleted()1683     public void testBasicCheckoutDeleted() throws Throwable
1684     {
1685         // create working copy
1686         OneTest thisTest = new OneTest();
1687 
1688         // delete A/D and its content
1689         client.remove(new String[] {thisTest.getWCPath()+"/A/D"}, null, true);
1690         thisTest.getWc().setItemTextStatus("A/D", Status.Kind.deleted);
1691         thisTest.getWc().setItemTextStatus("A/D/G", Status.Kind.deleted);
1692         thisTest.getWc().setItemTextStatus("A/D/G/rho", Status.Kind.deleted);
1693         thisTest.getWc().setItemTextStatus("A/D/G/pi", Status.Kind.deleted);
1694         thisTest.getWc().setItemTextStatus("A/D/G/tau", Status.Kind.deleted);
1695         thisTest.getWc().setItemTextStatus("A/D/H", Status.Kind.deleted);
1696         thisTest.getWc().setItemTextStatus("A/D/H/chi", Status.Kind.deleted);
1697         thisTest.getWc().setItemTextStatus("A/D/H/psi", Status.Kind.deleted);
1698         thisTest.getWc().setItemTextStatus("A/D/H/omega", Status.Kind.deleted);
1699         thisTest.getWc().setItemTextStatus("A/D/gamma", Status.Kind.deleted);
1700 
1701         // check the working copy status
1702         thisTest.checkStatus();
1703 
1704         // commit the change
1705         addExpectedCommitItem(thisTest.getWCPath(),
1706                 thisTest.getUrl(), "A/D", NodeKind.dir,
1707                 CommitItemStateFlags.Delete);
1708         assertEquals("wrong revision from commit",
1709                 client.commit(new String[]{thisTest.getWCPath()}, "log message",
1710                         true),2);
1711         thisTest.getWc().removeItem("A/D");
1712         thisTest.getWc().removeItem("A/D/G");
1713         thisTest.getWc().removeItem("A/D/G/rho");
1714         thisTest.getWc().removeItem("A/D/G/pi");
1715         thisTest.getWc().removeItem("A/D/G/tau");
1716         thisTest.getWc().removeItem("A/D/H");
1717         thisTest.getWc().removeItem("A/D/H/chi");
1718         thisTest.getWc().removeItem("A/D/H/psi");
1719         thisTest.getWc().removeItem("A/D/H/omega");
1720         thisTest.getWc().removeItem("A/D/gamma");
1721 
1722         // check the working copy status
1723         thisTest.checkStatus();
1724 
1725         // check out the previous revision
1726         client.checkout(thisTest.getUrl()+"/A/D", thisTest.getWCPath()+"/new_D",
1727                 new Revision.Number(1), true);
1728     }
1729 
1730     /**
1731      * Test the basic SVNClient.import functionality.
1732      * @throws Throwable
1733      */
testBasicImport()1734     public void testBasicImport() throws Throwable
1735     {
1736         // create the working copy
1737         OneTest thisTest = new OneTest();
1738 
1739         // create new_file
1740         File file = new File(thisTest.getWCPath(),"new_file");
1741         PrintWriter pw = new PrintWriter(new FileOutputStream(file));
1742         pw.print("some text");
1743         pw.close();
1744 
1745         // import new_file info dirA/dirB/newFile
1746         addExpectedCommitItem(thisTest.getWCPath(),
1747                 null, "new_file", NodeKind.none, CommitItemStateFlags.Add);
1748         client.doImport(file.getAbsolutePath(),
1749                 thisTest.getUrl()+"/dirA/dirB/new_file",
1750                 "log message for new import", true);
1751 
1752         // delete new_file
1753         file.delete();
1754 
1755         // update the working
1756         assertEquals("wrong revision from update",
1757                 client.update(thisTest.getWCPath(), null, true),2);
1758         thisTest.getWc().addItem("dirA", null);
1759         thisTest.getWc().setItemWorkingCopyRevision("dirA",2);
1760         thisTest.getWc().addItem("dirA/dirB", null);
1761         thisTest.getWc().setItemWorkingCopyRevision("dirA/dirB",2);
1762         thisTest.getWc().addItem("dirA/dirB/new_file", "some text");
1763         thisTest.getWc().setItemWorkingCopyRevision("dirA/dirB/new_file",2);
1764 
1765         // test the working copy status
1766         thisTest.checkStatus();
1767     }
1768 
1769     /**
1770      * Test the basic SVNClient.fileContent functionality.
1771      * @throws Throwable
1772      */
testBasicCat()1773     public void testBasicCat() throws Throwable
1774     {
1775         // create the working copy
1776         OneTest thisTest = new OneTest();
1777 
1778         // modify A/mu
1779         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
1780         PrintWriter pw = new PrintWriter(new FileOutputStream(mu, true));
1781         pw.print("some text");
1782         pw.close();
1783         // get the content from the repository
1784         byte[] content = client.fileContent(thisTest.getWCPath()+"/A/mu", null);
1785         byte[] testContent = thisTest.getWc().getItemContent("A/mu").getBytes();
1786 
1787         // the content should be the same
1788         assertTrue("content changed", Arrays.equals(content, testContent));
1789     }
1790 
1791     /**
1792      * Test the basic SVNClient.fileContent functionality.
1793      * @throws Throwable
1794      */
testBasicCatStream()1795     public void testBasicCatStream() throws Throwable
1796     {
1797         // create the working copy
1798         OneTest thisTest = new OneTest();
1799 
1800         // modify A/mu
1801         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
1802         PrintWriter pw = new PrintWriter(new FileOutputStream(mu, true));
1803         pw.print("some text");
1804         pw.close();
1805         // get the content from the repository
1806         ByteArrayOutputStream baos = new ByteArrayOutputStream();
1807         client.streamFileContent(thisTest.getWCPath() + "/A/mu", null, null,
1808                                  100, baos);
1809 
1810         byte[] content = baos.toByteArray();
1811         byte[] testContent = thisTest.getWc().getItemContent("A/mu").getBytes();
1812 
1813         // the content should be the same
1814         assertTrue("content changed", Arrays.equals(content, testContent));
1815     }
1816 
1817     /**
1818      * Test the basic SVNClient.list functionality.
1819      * @throws Throwable
1820      */
testBasicLs()1821     public void testBasicLs() throws Throwable
1822     {
1823         // create the working copy
1824         OneTest thisTest = new OneTest();
1825 
1826         // list the repository root dir
1827         DirEntry[] entries = client.list(thisTest.getWCPath(), null, false);
1828         thisTest.getWc().check(entries, "", false);
1829 
1830         // list directory A
1831         entries = client.list(thisTest.getWCPath() + "/A", null, false);
1832         thisTest.getWc().check(entries, "A", false);
1833 
1834         // list directory A in BASE revision
1835         entries = client.list(thisTest.getWCPath() + "/A", Revision.BASE,
1836                               false);
1837         thisTest.getWc().check(entries, "A", false);
1838 
1839         // list file A/mu
1840         entries = client.list(thisTest.getWCPath() + "/A/mu", null, false);
1841         thisTest.getWc().check(entries, "A/mu");
1842     }
1843 
1844     /**
1845      * Test the basis SVNClient.add functionality with files that
1846      * should be ignored.
1847      * @throws Throwable
1848      */
testBasicAddIgnores()1849     public void testBasicAddIgnores() throws Throwable
1850     {
1851         // create working copy
1852         OneTest thisTest = new OneTest();
1853 
1854         // create dir
1855         File dir = new File(thisTest.getWorkingCopy(), "dir");
1856         dir.mkdir();
1857 
1858         // create dir/foo.c
1859         File fileC = new File(dir, "foo.c");
1860         new FileOutputStream(fileC).close();
1861 
1862         // create dir/foo.o (should be ignored)
1863         File fileO = new File(dir, "foo.o");
1864         new FileOutputStream(fileO).close();
1865 
1866         // add dir
1867         client.add(dir.getAbsolutePath(), true);
1868         thisTest.getWc().addItem("dir", null);
1869         thisTest.getWc().setItemTextStatus("dir",Status.Kind.added);
1870         thisTest.getWc().addItem("dir/foo.c", "");
1871         thisTest.getWc().setItemTextStatus("dir/foo.c",Status.Kind.added);
1872         thisTest.getWc().addItem("dir/foo.o", "");
1873         thisTest.getWc().setItemTextStatus("dir/foo.o",Status.Kind.ignored);
1874         thisTest.getWc().setItemNodeKind("dir/foo.o", NodeKind.unknown);
1875 
1876         // test the working copy status
1877         thisTest.checkStatus();
1878     }
1879 
1880     /**
1881      * Test the basis SVNClient.import functionality with files that
1882      * should be ignored.
1883      * @throws Throwable
1884      */
testBasicImportIgnores()1885     public void testBasicImportIgnores() throws Throwable
1886     {
1887         // create working copy
1888         OneTest thisTest = new OneTest();
1889 
1890         // create dir
1891         File dir = new File(thisTest.getWorkingCopy(), "dir");
1892         dir.mkdir();
1893 
1894         // create dir/foo.c
1895         File fileC = new File(dir, "foo.c");
1896         new FileOutputStream(fileC).close();
1897 
1898         // create dir/foo.o (should be ignored)
1899         File fileO = new File(dir, "foo.o");
1900         new FileOutputStream(fileO).close();
1901 
1902         // import dir
1903         addExpectedCommitItem(thisTest.getWCPath(),
1904                 null, "dir", NodeKind.none, CommitItemStateFlags.Add);
1905         client.doImport(dir.getAbsolutePath(), thisTest.getUrl()+"/dir",
1906                 "log message for import", true);
1907 
1908         // remove dir
1909         removeDirOrFile(dir);
1910 
1911         // udpate the working copy
1912         assertEquals("wrong revision from update", 2,
1913                 client.update(thisTest.getWCPath(), null, true));
1914         thisTest.getWc().addItem("dir", null);
1915         thisTest.getWc().addItem("dir/foo.c", "");
1916 
1917         // test the working copy status
1918         thisTest.checkStatus();
1919     }
1920 
1921     /**
1922      * Test the basic SVNClient.info functionality.
1923      * @throws Throwable
1924      */
testBasicInfo()1925     public void testBasicInfo() throws Throwable
1926     {
1927         // create the working copy
1928         OneTest thisTest = new OneTest();
1929 
1930         // get the item information and test it
1931         Info info = client.info(thisTest.getWCPath()+"/A/mu");
1932         assertEquals("wrong revision from info", 1,
1933                      info.getLastChangedRevision());
1934         assertEquals("wrong schedule kind from info", ScheduleKind.normal,
1935                      info.getSchedule());
1936         assertEquals("wrong node kind from info", NodeKind.file,
1937                      info.getNodeKind());
1938     }
1939 
1940     /**
1941      * Test the basic SVNClient.logMessages functionality.
1942      * @throws Throwable
1943      */
testBasicLogMessage()1944     public void testBasicLogMessage() throws Throwable
1945     {
1946         // create the working copy
1947         OneTest thisTest = new OneTest();
1948 
1949         // get the commit message of the initial import and test it
1950         LogMessage lm[] = client.logMessages(thisTest.getWCPath(), null,
1951                                              null, false, true);
1952         assertEquals("wrong number of objects", 1, lm.length);
1953         assertEquals("wrong message", "Log Message", lm[0].getMessage());
1954         assertEquals("wrong revision", 1, lm[0].getRevisionNumber());
1955         assertEquals("wrong user", "jrandom", lm[0].getAuthor());
1956         assertNotNull("changed paths set", lm[0].getChangedPaths());
1957         ChangePath cp[] = lm[0].getChangedPaths();
1958         assertEquals("wrong number of chang pathes", 20, cp.length);
1959         assertEquals("wrong path", "/A", cp[0].getPath());
1960         assertEquals("wrong copy source rev", -1, cp[0].getCopySrcRevision());
1961         assertNull("wrong copy source path", cp[0].getCopySrcPath());
1962         assertEquals("wrong action", 'A', cp[0].getAction());
1963         assertEquals("wrong time with getTimeMicros()",
1964                      lm[0].getTimeMicros()/1000,
1965                      lm[0].getDate().getTime());
1966         assertEquals("wrong time with getTimeMillis()",
1967                      lm[0].getTimeMillis(),
1968                      lm[0].getDate().getTime());
1969         assertEquals("wrong date with getTimeMicros()",
1970                      lm[0].getDate(),
1971                      new java.util.Date(lm[0].getTimeMicros()/1000));
1972         assertEquals("wrong date with getTimeMillis()",
1973                      lm[0].getDate(),
1974                      new java.util.Date(lm[0].getTimeMillis()));
1975 
1976         // Ensure that targets get canonicalized
1977         String non_canonical = thisTest.getUrl().toString() + "/";
1978         LogMessage lm2[] = client.logMessages(non_canonical, null,
1979                                               null, false, true);
1980     }
1981 
1982     /**
1983      * Test the basic SVNClient.getVersionInfo functionality.
1984      * @throws Throwable
1985      * @since 1.2
1986      */
testBasicVersionInfo()1987     public void testBasicVersionInfo() throws Throwable
1988     {
1989         // create the working copy
1990         OneTest thisTest = new OneTest();
1991         assertEquals("wrong version info",
1992                      "1",
1993                      client.getVersionInfo(thisTest.getWCPath(), null, false));
1994     }
1995 
1996     /**
1997      * Test the basic SVNClient locking functionality.
1998      * @throws Throwable
1999      * @since 1.2
2000      */
testBasicLocking()2001     public void testBasicLocking() throws Throwable
2002     {
2003         // build the first working copy
2004         OneTest thisTest = new OneTest();
2005 
2006         client.propertySet(thisTest.getWCPath()+"/A/mu",
2007                            PropertyData.NEEDS_LOCK, "*", false);
2008 
2009         addExpectedCommitItem(thisTest.getWCPath(),
2010                               thisTest.getUrl(), "A/mu",NodeKind.file,
2011                               CommitItemStateFlags.PropMods);
2012         assertEquals("bad revision number on commit", 2,
2013                      client.commit(new String[] {thisTest.getWCPath()},
2014                                    "message", true));
2015         File f = new File(thisTest.getWCPath()+"/A/mu");
2016         assertEquals("file should be read only now", false, f.canWrite());
2017         client.lock(new String[] {thisTest.getWCPath()+"/A/mu"},
2018                     "comment", false);
2019         assertEquals("file should be read write now", true, f.canWrite());
2020         client.unlock(new String[]{thisTest.getWCPath()+"/A/mu"}, false);
2021         assertEquals("file should be read only now", false, f.canWrite());
2022         client.lock(new String[]{thisTest.getWCPath()+"/A/mu"},
2023                     "comment", false);
2024         assertEquals("file should be read write now", true, f.canWrite());
2025         addExpectedCommitItem(thisTest.getWCPath(),
2026                               thisTest.getUrl(), "A/mu",NodeKind.file,
2027                               0);
2028         assertEquals("rev number from commit", -1,
2029                      client.commit(new String[]{thisTest.getWCPath()},
2030                                    "message", true));
2031         assertEquals("file should be read write now", true, f.canWrite());
2032 
2033         try
2034         {
2035             // Attempt to lock an invalid path
2036             client.lock(new String[]{thisTest.getWCPath()+"/A/mu2"}, "comment",
2037                         false);
2038             fail("missing exception");
2039         }
2040         catch (ClientException expected)
2041         {
2042         }
2043     }
2044 
2045     /**
2046      * Test the basic SVNClient.info2 functionality.
2047      * @throws Throwable
2048      * @since 1.2
2049      */
testBasicInfo2()2050     public void testBasicInfo2() throws Throwable
2051     {
2052         // build the first working copy
2053         OneTest thisTest = new OneTest();
2054 
2055         final String failureMsg = "Incorrect number of info objects";
2056         Info2[] infos = client.info2(thisTest.getWCPath(), null, null, false);
2057         assertEquals(failureMsg, 1, infos.length);
2058         infos = client.info2(thisTest.getWCPath(), null, null, true);
2059         assertEquals(failureMsg, 21, infos.length);
2060         for (int i = 0; i < infos.length; i++)
2061         {
2062             Info2 info = infos[i];
2063             assertNull("Unexpected changelist present",
2064                        info.getChangelistName());
2065 
2066             boolean isFile = info.getKind() == NodeKind.file;
2067             assertTrue("Unexpected working file size " + info.getWorkingSize()
2068                        + " for '" + info + '\'',
2069                        (isFile ? info.getWorkingSize() > -1 :
2070                         info.getWorkingSize() == -1));
2071             // We shouldn't know the repository file size when only
2072             // examining the WC.
2073             assertEquals("Unexpected repos file size for '" + info + '\'',
2074                          -1, info.getReposSize());
2075 
2076            // Examine depth
2077            assertEquals("Unexpected depth for '" + info + "'",
2078                         (isFile ? Depth.unknown : Depth.infinity),
2079                         info.getDepth());
2080         }
2081 
2082         // Create wc with a depth of Depth.empty
2083         String secondWC = thisTest.getWCPath() + ".empty";
2084         removeDirOrFile(new File(secondWC));
2085 
2086         client.checkout(thisTest.getUrl(), secondWC, null, null, Depth.empty,
2087                         false, true);
2088 
2089         infos = client.info2(secondWC, null, null, false);
2090 
2091         // Examine that depth is Depth.empty
2092         assertEquals(Depth.empty, infos[0].getDepth());
2093     }
2094 
2095     /**
2096      * Test basic changelist functionality.
2097      * @throws Throwable
2098      * @since 1.5
2099      */
testBasicChangelist()2100     public void testBasicChangelist() throws Throwable
2101     {
2102         // build the working copy
2103         OneTest thisTest = new OneTest();
2104         String changelistName = "changelist1";
2105         String[] changelists = new String[] { changelistName };
2106         MyChangelistCallback clCallback = new MyChangelistCallback();
2107 
2108         String[] paths = new String[]
2109           {fileToSVNPath(new File(thisTest.getWCPath(), "iota"), true)};
2110         // Add a path to a changelist, and check to see if it got added
2111         client.addToChangelist(paths, changelistName, Depth.infinity, null);
2112         String[] cl = new String[1];
2113         client.getChangelists(thisTest.getWCPath(), changelists,
2114                               Depth.infinity, clCallback);
2115         cl[0] = (String) clCallback.get(paths[0]).get(0);
2116         assertTrue(java.util.Arrays.equals(cl, changelists));
2117         // Does status report this changelist?
2118         Status[] status = client.status(paths[0], false, false, false, false,
2119                                         false);
2120         assertEquals(status[0].getChangelist(), changelistName);
2121 
2122         // Remove the path from the changelist, and check to see if the path is
2123         // actually removed.
2124         client.removeFromChangelists(paths, Depth.infinity, changelists);
2125         clCallback.clear();
2126         client.getChangelists(thisTest.getWCPath(), changelists,
2127                               Depth.infinity, clCallback);
2128         assertTrue(clCallback.isEmpty());
2129     }
2130 
2131     /**
2132      * Helper method for testing mergeinfo retrieval.  Assumes
2133      * that <code>targetPath</code> has both merge history and
2134      * available merges.
2135      * @param expectedMergedStart The expected start revision from the
2136      * merge history for <code>mergeSrc</code>.
2137      * @param expectedMergedEnd The expected end revision from the
2138      * merge history for <code>mergeSrc</code>.
2139      * @param expectedAvailableStart The expected start available revision
2140      * from the merge history for <code>mergeSrc</code>.  Zero if no need
2141      * to test the available range.
2142      * @param expectedAvailableEnd The expected end available revision
2143      * from the merge history for <code>mergeSrc</code>.
2144      * @param targetPath The path for which to acquire mergeinfo.
2145      * @param mergeSrc The URL from which to consider merges.
2146      */
acquireMergeinfoAndAssertEquals(long expectedMergeStart, long expectedMergeEnd, long expectedAvailableStart, long expectedAvailableEnd, String targetPath, String mergeSrc)2147     private void acquireMergeinfoAndAssertEquals(long expectedMergeStart,
2148                                                  long expectedMergeEnd,
2149                                                  long expectedAvailableStart,
2150                                                  long expectedAvailableEnd,
2151                                                  String targetPath,
2152                                                  String mergeSrc)
2153         throws SubversionException
2154     {
2155         // Verify expected merge history.
2156         Mergeinfo mergeInfo = client.getMergeinfo(targetPath, Revision.HEAD);
2157         assertNotNull("Missing merge info on '" + targetPath + '\'',
2158                       mergeInfo);
2159         List ranges = mergeInfo.getRevisions(mergeSrc);
2160         assertTrue("Missing merge info for source '" + mergeSrc + "' on '" +
2161                    targetPath + '\'', ranges != null && !ranges.isEmpty());
2162         RevisionRange range = (RevisionRange) ranges.get(0);
2163         String expectedMergedRevs = expectedMergeStart + "-" + expectedMergeEnd;
2164         assertEquals("Unexpected first merged revision range for '" +
2165                      mergeSrc + "' on '" + targetPath + '\'',
2166                      expectedMergedRevs, range.toString());
2167 
2168         // Verify expected available merges.
2169         if (expectedAvailableStart > 0)
2170         {
2171             long[] availableRevs =
2172                     getMergeinfoRevisions(MergeinfoLogKind.eligible, targetPath,
2173                                           Revision.HEAD, mergeSrc,
2174                                           Revision.HEAD);
2175             assertNotNull("Missing eligible merge info on '"+targetPath + '\'',
2176                           availableRevs);
2177             assertExpectedMergeRange(expectedAvailableStart,
2178                                      expectedAvailableEnd, availableRevs);
2179             }
2180     }
2181 
2182     /**
2183      * Calls the API to get mergeinfo revisions and returns
2184      * the revision numbers in a sorted array, or null if there
2185      * are no revisions to return.
2186      * @since 1.5
2187      */
2188     @SuppressWarnings("unchecked")
getMergeinfoRevisions(int kind, String pathOrUrl, Revision pegRevision, String mergeSourceUrl, Revision srcPegRevision)2189     private long[] getMergeinfoRevisions(int kind, String pathOrUrl,
2190                                          Revision pegRevision,
2191                                          String mergeSourceUrl,
2192                                          Revision srcPegRevision)
2193         throws SubversionException
2194     {
2195         class Callback implements LogMessageCallback {
2196 
2197             List revList = new ArrayList();
2198 
2199             public void singleMessage(ChangePath[] changedPaths, long revision,
2200                     Map revprops, boolean hasChildren) {
2201                 revList.add(new Long(revision));
2202             }
2203 
2204             public long[] getRevisions() {
2205                 long[] revisions = new long[revList.size()];
2206                 int i = 0;
2207                 for (Iterator iter = revList.iterator(); iter.hasNext();) {
2208                     Long revision = (Long) iter.next();
2209                     revisions[i] = revision.longValue();
2210                     i++;
2211                 }
2212                 return revisions;
2213             }
2214         }
2215         Callback callback = new Callback();
2216         client.getMergeinfoLog(kind, pathOrUrl, pegRevision, mergeSourceUrl,
2217                                srcPegRevision, false, null, callback);
2218         return callback.getRevisions();
2219     }
2220 
2221     /**
2222      * Append the text <code>toAppend</code> to the WC file at
2223      * <code>path</code>, and update the expected WC state
2224      * accordingly.
2225      *
2226      * @param thisTest The test whose expected WC to tweak.
2227      * @param path The working copy-relative path to change.
2228      * @param toAppend The text to append to <code>path</code>.
2229      * @param rev The expected revision number for thisTest's WC.
2230      * @return The file created during the setup.
2231      * @since 1.5
2232      */
appendText(OneTest thisTest, String path, String toAppend, int rev)2233     private File appendText(OneTest thisTest, String path, String toAppend,
2234                             int rev)
2235         throws FileNotFoundException
2236     {
2237         File f = new File(thisTest.getWorkingCopy(), path);
2238         PrintWriter writer = new PrintWriter(new FileOutputStream(f, true));
2239         writer.print(toAppend);
2240         writer.close();
2241         if (rev > 0)
2242         {
2243             WC wc = thisTest.getWc();
2244             wc.setItemWorkingCopyRevision(path, rev);
2245             wc.setItemContent(path, wc.getItemContent(path) + toAppend);
2246         }
2247         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(), path,
2248                               NodeKind.file, CommitItemStateFlags.TextMods);
2249         return f;
2250     }
2251 
2252     /**
2253      * Test the basic functionality of SVNClient.merge().
2254      * @throws Throwable
2255      * @since 1.2
2256      */
testBasicMerge()2257     public void testBasicMerge() throws Throwable
2258     {
2259         OneTest thisTest = setupAndPerformMerge();
2260 
2261         // Verify that there are now potential merge sources.
2262         String[] suggestedSrcs =
2263             client.suggestMergeSources(thisTest.getWCPath() + "/branches/A",
2264                                        Revision.WORKING);
2265         assertNotNull(suggestedSrcs);
2266         assertEquals(1, suggestedSrcs.length);
2267 
2268         // Test that getMergeinfo() returns null.
2269         assertNull(client.getMergeinfo(new File(thisTest.getWCPath(), "A")
2270                                        .toString(), Revision.HEAD));
2271 
2272         // Merge and commit some changes (r4).
2273         appendText(thisTest, "A/mu", "xxx", 4);
2274         appendText(thisTest, "A/D/G/rho", "yyy", 4);
2275         assertEquals("wrong revision number from commit",
2276                      client.commit(new String[] { thisTest.getWCPath() },
2277                                    "log msg", true),
2278                      4);
2279 
2280         // Add a "begin merge" notification handler.
2281         final Revision[] actualRange = new Revision[2];
2282         Notify2 notify = new Notify2()
2283         {
2284             public void onNotify(NotifyInformation info)
2285             {
2286                 if (info.getAction() == NotifyAction.merge_begin)
2287                 {
2288                     RevisionRange r = info.getMergeRange();
2289                     actualRange[0] = r.getFromRevision();
2290                     actualRange[1] = r.getToRevision();
2291                 }
2292             }
2293         };
2294         client.notification2(notify);
2295 
2296         // merge changes in A to branches/A
2297         String branchPath = thisTest.getWCPath() + "/branches/A";
2298         String modUrl = thisTest.getUrl() + "/A";
2299         // test --dry-run
2300         client.merge(modUrl, new Revision.Number(2), modUrl, Revision.HEAD,
2301                      branchPath, false, true, false, true);
2302         assertEquals("Notification of beginning of merge reported incorrect " +
2303                      "start revision", new Revision.Number(2), actualRange[0]);
2304         assertEquals("Notification of beginning of merge reported incorrect " +
2305                      "end revision", new Revision.Number(4), actualRange[1]);
2306 
2307         // now do the real merge
2308         client.merge(modUrl, new Revision.Number(2), modUrl, Revision.HEAD,
2309                      branchPath, false, true, false, false);
2310         assertEquals("Notification of beginning of merge reported incorrect " +
2311                      "start revision", new Revision.Number(2), actualRange[0]);
2312         assertEquals("Notification of beginning of merge reported incorrect " +
2313                      "end revision", new Revision.Number(4), actualRange[1]);
2314 
2315         // commit the changes so that we can verify merge
2316         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2317                               "branches/A", NodeKind.dir,
2318                               CommitItemStateFlags.PropMods);
2319         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2320                               "branches/A/mu", NodeKind.file,
2321                               CommitItemStateFlags.TextMods);
2322         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2323                               "branches/A/D/G/rho", NodeKind.file,
2324                               CommitItemStateFlags.TextMods);
2325         assertEquals("wrong revision number from commit",
2326                      client.commit(new String[] { thisTest.getWCPath() },
2327                                    "log msg", true), 5);
2328 
2329         // Merge and commit some more changes (r6).
2330         appendText(thisTest, "A/mu", "xxxr6", 6);
2331         appendText(thisTest, "A/D/G/rho", "yyyr6", 6);
2332         assertEquals("wrong revision number from commit",
2333                      client.commit(new String[] { thisTest.getWCPath() },
2334                                    "log msg", true),
2335                      6);
2336 
2337         // Test retrieval of mergeinfo from a WC path.
2338         String targetPath =
2339             new File(thisTest.getWCPath(), "branches/A/mu").getPath();
2340         final String mergeSrc = thisTest.getUrl() + "/A/mu";
2341         acquireMergeinfoAndAssertEquals(2, 4, 6, 6, targetPath, mergeSrc);
2342 
2343         // Test retrieval of mergeinfo from the repository.
2344         targetPath = thisTest.getUrl() + "/branches/A/mu";
2345         acquireMergeinfoAndAssertEquals(2, 4, 6, 6, targetPath, mergeSrc);
2346     }
2347 
2348     /**
2349      * Test merge with automatic source and revision determination
2350      * (e.g. 'svn merge -g').
2351      * @throws Throwable
2352      * @since 1.5
2353      */
testMergeUsingHistory()2354     public void testMergeUsingHistory() throws Throwable
2355     {
2356         OneTest thisTest = setupAndPerformMerge();
2357 
2358         // Test that getMergeinfo() returns null.
2359         assertNull(client.getMergeinfo(new File(thisTest.getWCPath(), "A")
2360                                        .toString(), Revision.HEAD));
2361 
2362         // Merge and commit some changes (r4).
2363         appendText(thisTest, "A/mu", "xxx", 4);
2364         assertEquals("wrong revision number from commit",
2365                      client.commit(new String[] { thisTest.getWCPath() },
2366                                    "log msg", true),
2367                      4);
2368 
2369         String branchPath = thisTest.getWCPath() + "/branches/A";
2370         String modUrl = thisTest.getUrl() + "/A";
2371         Revision unspec = new Revision(RevisionKind.unspecified);
2372         client.merge(modUrl, Revision.HEAD,
2373                      new RevisionRange[] { new RevisionRange(unspec, unspec) },
2374                      branchPath, true, Depth.infinity, false, false, false);
2375 
2376         // commit the changes so that we can verify merge
2377         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2378                               "branches/A", NodeKind.dir,
2379                               CommitItemStateFlags.PropMods);
2380         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2381                               "branches/A/mu", NodeKind.file,
2382                               CommitItemStateFlags.TextMods);
2383         assertEquals("wrong revision number from commit",
2384                      client.commit(new String[] { thisTest.getWCPath() },
2385                                    "log msg", true), 5);
2386     }
2387 
2388     /**
2389      * Test reintegrating a branch with trunk
2390      * (e.g. 'svn merge --reintegrate').
2391      * @throws Throwable
2392      * @since 1.5
2393      */
testMergeReintegrate()2394     public void testMergeReintegrate() throws Throwable
2395     {
2396         OneTest thisTest = setupAndPerformMerge();
2397 
2398         // Test that getMergeinfo() returns null.
2399         assertNull(client.getMergeinfo(new File(thisTest.getWCPath(), "A")
2400                                        .toString(), Revision.HEAD));
2401 
2402         // Merge and commit some changes to main (r4).
2403         appendText(thisTest, "A/mu", "xxx", 4);
2404         assertEquals("wrong revision number from main commit",
2405                      client.commit(new String[] { thisTest.getWCPath() },
2406                                    "log msg", true),
2407                      4);
2408         // Merge and commit some changes to branch (r5).
2409         appendText(thisTest, "branches/A/D/G/rho", "yyy", -1);
2410         assertEquals("wrong revision number from branch commit",
2411                      client.commit(new String[] { thisTest.getWCPath() },
2412                                    "log msg", true),
2413                      5);
2414 
2415         // update the branch WC (to r5) before merge
2416         client.update(thisTest.getWCPath() + "/branches", Revision.HEAD, true);
2417 
2418         String branchPath = thisTest.getWCPath() + "/branches/A";
2419         String modUrl = thisTest.getUrl() + "/A";
2420         Revision unspec = new Revision(RevisionKind.unspecified);
2421         client.merge(modUrl, Revision.HEAD,
2422                      new RevisionRange[] { new RevisionRange(unspec, unspec) },
2423                      branchPath, true, Depth.infinity, false, false, false);
2424 
2425         // commit the changes so that we can verify merge
2426         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2427                               "branches/A", NodeKind.dir,
2428                               CommitItemStateFlags.PropMods);
2429         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2430                               "branches/A/mu", NodeKind.file,
2431                               CommitItemStateFlags.TextMods);
2432         assertEquals("wrong revision number from commit",
2433                      client.commit(new String[] { thisTest.getWCPath() },
2434                                    "log msg", true), 6);
2435 
2436         // now we --reintegrate the branch with main
2437         String branchUrl = thisTest.getUrl() + "/branches/A";
2438         try
2439         {
2440             client.mergeReintegrate(branchUrl, Revision.HEAD,
2441                                     thisTest.getWCPath() + "/A", false);
2442             fail("reintegrate merged into a mixed-revision WC");
2443         }
2444         catch(ClientException e)
2445         {
2446             // update the WC (to r6) and try again
2447             client.update(thisTest.getWCPath(), Revision.HEAD, true);
2448             client.mergeReintegrate(branchUrl, Revision.HEAD,
2449                                     thisTest.getWCPath() + "/A", false);
2450         }
2451         // commit the changes so that we can verify merge
2452         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2453                               "A", NodeKind.dir,
2454                               CommitItemStateFlags.PropMods);
2455         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2456                               "A/D/G/rho", NodeKind.file,
2457                               CommitItemStateFlags.TextMods);
2458         assertEquals("wrong revision number from commit",
2459                      client.commit(new String[] { thisTest.getWCPath() },
2460                                    "log msg", true), 7);
2461 
2462     }
2463 
2464     /**
2465      * Test automatic merge conflict resolution.
2466      * @throws Throwable
2467      * @since 1.5
2468      */
testMergeConflictResolution()2469     public void testMergeConflictResolution() throws Throwable
2470     {
2471         // Add a conflict resolution callback which always chooses the
2472         // user's version of a conflicted file.
2473         client.setConflictResolver(new ConflictResolverCallback()
2474             {
2475                 public ConflictResult resolve(ConflictDescriptor descrip)
2476                 {
2477                     return new ConflictResult(ConflictResult.chooseTheirsConflict,
2478                                               null);
2479                 }
2480             });
2481 
2482         OneTest thisTest = new OneTest();
2483         String originalContents = thisTest.getWc().getItemContent("A/mu");
2484         String expectedContents = originalContents + "xxx";
2485 
2486         // Merge and commit a change (r2).
2487         File mu = appendText(thisTest, "A/mu", "xxx", 2);
2488         assertEquals("wrong revision number from commit", 2,
2489                      client.commit(new String[] { thisTest.getWCPath() },
2490                                    "log msg", true));
2491 
2492         // Backdate the WC to the previous revision (r1).
2493         client.update(thisTest.getWCPath(), Revision.getInstance(1), true);
2494 
2495         // Prep for a merge conflict by changing A/mu in a different
2496         // way.
2497         mu = appendText(thisTest, "A/mu", "yyy", 1);
2498 
2499         // Merge in the previous changes to A/mu (from r2).
2500         RevisionRange[] ranges = new RevisionRange[1];
2501         ranges[0] = new RevisionRange(new Revision.Number(1),
2502                                       new Revision.Number(2));
2503         client.merge(thisTest.getUrl(), Revision.HEAD, ranges,
2504                      thisTest.getWCPath(), false, Depth.infinity, false,
2505                      false, false);
2506 
2507         assertFileContentsEquals("Unexpected conflict resolution",
2508                                  expectedContents, mu);
2509     }
2510 
2511     /**
2512      * Test merge --record-only
2513      * @throws Throwable
2514      * @since 1.5
2515      */
testRecordOnlyMerge()2516     public void testRecordOnlyMerge() throws Throwable
2517     {
2518         OneTest thisTest = setupAndPerformMerge();
2519 
2520         // Verify that there are now potential merge sources.
2521         String[] suggestedSrcs =
2522             client.suggestMergeSources(thisTest.getWCPath() + "/branches/A",
2523                                        Revision.WORKING);
2524         assertNotNull(suggestedSrcs);
2525         assertEquals(1, suggestedSrcs.length);
2526 
2527         // Test that getMergeinfo() returns null.
2528         assertNull(client.getMergeinfo(new File(thisTest.getWCPath(), "A")
2529                                        .toString(), Revision.HEAD));
2530 
2531         // Merge and commit some changes (r4).
2532         appendText(thisTest, "A/mu", "xxx", 4);
2533         appendText(thisTest, "A/D/G/rho", "yyy", 4);
2534         assertEquals("wrong revision number from commit",
2535                      client.commit(new String[] { thisTest.getWCPath() },
2536                                    "log msg", true),
2537                      4);
2538 
2539         // --record-only merge changes in A to branches/A
2540         String branchPath = thisTest.getWCPath() + "/branches/A";
2541         String modUrl = thisTest.getUrl() + "/A";
2542 
2543         RevisionRange[] ranges = new RevisionRange[1];
2544         ranges[0] = new RevisionRange(new Revision.Number(2),
2545                                       new Revision.Number(4));
2546         client.merge(modUrl, Revision.HEAD, ranges,
2547                      branchPath, true, Depth.infinity, false, false, true);
2548 
2549         // commit the changes so that we can verify merge
2550         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2551                               "branches/A", NodeKind.dir,
2552                               CommitItemStateFlags.PropMods);
2553         assertEquals("wrong revision number from commit",
2554                      client.commit(new String[] { thisTest.getWCPath() },
2555                                    "log msg", true), 5);
2556 
2557         // Test retrieval of mergeinfo from a WC path.
2558         String targetPath =
2559             new File(thisTest.getWCPath(), "branches/A").getPath();
2560         final String mergeSrc = thisTest.getUrl() + "/A";
2561         acquireMergeinfoAndAssertEquals(2, 4, 0, 0, targetPath, mergeSrc);
2562     }
2563 
2564     /**
2565      * Setup a test with a WC.  In the repository, create a
2566      * "/branches" directory, with a branch of "/A" underneath it.
2567      * Update the WC to reflect these modifications.
2568      * @return This test.
2569      */
setupAndPerformMerge()2570     private OneTest setupAndPerformMerge()
2571         throws Exception
2572     {
2573         OneTest thisTest = new OneTest();
2574 
2575         // Verify that there are initially no potential merge sources.
2576         String[] suggestedSrcs =
2577             client.suggestMergeSources(thisTest.getWCPath(),
2578                                        Revision.WORKING);
2579         assertNotNull(suggestedSrcs);
2580         assertEquals(0, suggestedSrcs.length);
2581 
2582         // create branches directory in the repository (r2)
2583         addExpectedCommitItem(null, thisTest.getUrl(), "branches",
2584                               NodeKind.none, CommitItemStateFlags.Add);
2585         client.mkdir(new String[]{thisTest.getUrl() + "/branches"}, "log_msg");
2586 
2587         // copy A to branches (r3)
2588         addExpectedCommitItem(null, thisTest.getUrl(), "branches/A",
2589                               NodeKind.none, CommitItemStateFlags.Add);
2590         client.copy(thisTest.getUrl() + "/A", thisTest.getUrl() +
2591                     "/branches/A", "create A branch", Revision.HEAD);
2592 
2593         // update the WC (to r3) so that it has the branches folder
2594         client.update(thisTest.getWCPath(), Revision.HEAD, true);
2595 
2596         return thisTest;
2597     }
2598 
2599     /**
2600      * Test the {@link SVNClientInterface.diff()} APIs.
2601      * @since 1.5
2602      */
testDiff()2603     public void testDiff()
2604         throws SubversionException, IOException
2605     {
2606         OneTest thisTest = new OneTest(true);
2607         File diffOutput = new File(super.localTmp, thisTest.testName);
2608         final String NL = System.getProperty("line.separator");
2609         final String sepLine =
2610             "===================================================================" + NL;
2611         final String underSepLine =
2612             "___________________________________________________________________" + NL;
2613         final String expectedDiffBody =
2614             "@@ -1 +1 @@" + NL +
2615             "-This is the file 'iota'." + NL +
2616             "\\ No newline at end of file" + NL +
2617             "+This is the file 'mu'." + NL +
2618             "\\ No newline at end of file" + NL;
2619 
2620         final String iotaPath = thisTest.getWCPath().replace('\\', '/') + "/iota";
2621         final String wcPath = fileToSVNPath(new File(thisTest.getWCPath()),
2622                 false);
2623 
2624         // make edits to iota
2625         PrintWriter writer = new PrintWriter(new FileOutputStream(iotaPath));
2626         writer.print("This is the file 'mu'.");
2627         writer.flush();
2628         writer.close();
2629 
2630         /*
2631          * This test does tests with and without svn:eol-style set to native
2632          * We will first run all of the tests where this does not matter so
2633          * that they are not run twice.
2634          */
2635 
2636         // Two-path diff of URLs.
2637         String expectedDiffOutput = "Index: iota" + NL + sepLine +
2638             "--- iota\t(.../iota)\t(revision 1)" + NL +
2639             "+++ iota\t(.../A/mu)\t(revision 1)" + NL +
2640             expectedDiffBody;
2641         client.diff(thisTest.getUrl() + "/iota", Revision.HEAD,
2642                     thisTest.getUrl() + "/A/mu", Revision.HEAD,
2643                     diffOutput.getPath(), false, true, true, false);
2644         assertFileContentsEquals("Unexpected diff output in file '" +
2645                                  diffOutput.getPath() + '\'',
2646                                  expectedDiffOutput, diffOutput);
2647 
2648         // Test relativeToDir fails with urls. */
2649         try
2650         {
2651             client.diff(thisTest.getUrl() + "/iota", Revision.HEAD,
2652                         thisTest.getUrl() + "/A/mu", Revision.HEAD,
2653                         thisTest.getUrl(), diffOutput.getPath(),
2654                         Depth.infinity, null, true, true, false);
2655 
2656             fail("This test should fail because the relativeToDir parameter " +
2657                  "does not work with URLs");
2658         }
2659         catch (Exception ignored)
2660         {
2661         }
2662 
2663         /* Testing the expected failure when relativeToDir is not a parent
2664            path of the target. */
2665         try
2666         {
2667             client.diff(iotaPath, Revision.BASE, iotaPath, Revision.WORKING,
2668                         "/non/existent/path", diffOutput.getPath(),
2669                         Depth.infinity, null, true, true, false);
2670 
2671             fail("This test should fail because iotaPath is not a child of " +
2672                  "the relativeToDir parameter");
2673         }
2674         catch (Exception ignored)
2675         {
2676         }
2677 
2678         // Test diff with a relative path on a directory with prop
2679         // changes.
2680         String aPath = fileToSVNPath(new File(thisTest.getWCPath() + "/A"),
2681                                      false);
2682 
2683         expectedDiffOutput = "Index: A" + NL + sepLine +
2684             "--- A\t(revision 1)" + NL +
2685             "+++ A\t(working copy)" + NL +
2686             NL + "Property changes on: A" + NL +
2687             underSepLine +
2688             "Added: testprop" + NL +
2689             "## -0,0 +1 ##" + NL +
2690             "+Test property value." + NL;
2691 
2692         client.propertySet(aPath, "testprop", "Test property value." + NL,
2693                            false);
2694         client.diff(aPath, Revision.BASE, aPath, Revision.WORKING, wcPath,
2695                     diffOutput.getPath(), Depth.infinity, null, true, true,
2696                     false);
2697         assertFileContentsEquals("Unexpected diff output in file '" +
2698                                  diffOutput.getPath() + '\'',
2699                                  expectedDiffOutput, diffOutput);
2700 
2701         // Test diff where relativeToDir and path are the same.
2702         expectedDiffOutput = "Index: ." + NL + sepLine +
2703             "--- .\t(revision 1)" + NL +
2704             "+++ .\t(working copy)" + NL +
2705             NL + "Property changes on: ." + NL +
2706             underSepLine +
2707             "Added: testprop" + NL +
2708             "## -0,0 +1 ##" + NL +
2709             "+Test property value." + NL;
2710 
2711         client.propertySet(aPath, "testprop", "Test property value." + NL,
2712                            false);
2713         client.diff(aPath, Revision.BASE, aPath, Revision.WORKING, aPath,
2714                     diffOutput.getPath(), Depth.infinity, null, true, true,
2715                     false);
2716         assertFileContentsEquals("Unexpected diff output in file '" +
2717                                  diffOutput.getPath() + '\'',
2718                                  expectedDiffOutput, diffOutput);
2719 
2720 
2721         /*
2722          * The rest of these tests are run twice.  The first time
2723          * without svn:eol-style set and the second time with the
2724          * property set to native.  This is tracked by the int named
2725          * operativeRevision.  It will have a value = 2 after the
2726          * commit which sets the property
2727          */
2728 
2729         for (int operativeRevision = 1; operativeRevision < 3; operativeRevision++)
2730          {
2731                 String revisionPrefix = "While processing operativeRevison=" + operativeRevision + ". ";
2732                 String assertPrefix = revisionPrefix + "Unexpected diff output in file '";
2733 
2734                 // Undo previous edits to working copy
2735                 client.revert(wcPath, true);
2736 
2737                 if (operativeRevision == 2) {
2738                     // Set svn:eol-style=native on iota
2739                     client.propertyCreate(iotaPath, "svn:eol-style", "native", false);
2740                     String[] paths = new String[] {iotaPath};
2741                     addExpectedCommitItem(thisTest.getWCPath(),
2742                             thisTest.getUrl(), "iota",NodeKind.file,
2743                             CommitItemStateFlags.PropMods);
2744                     client.commit(paths, "Set svn:eol-style to native", false);
2745                 }
2746 
2747                 // make edits to iota and set expected output.
2748                 writer = new PrintWriter(new FileOutputStream(iotaPath));
2749                 writer.print("This is the file 'mu'.");
2750                 writer.flush();
2751                 writer.close();
2752                 expectedDiffOutput = "Index: " + iotaPath + NL + sepLine +
2753                     "--- " + iotaPath + "\t(revision " + operativeRevision + ")" + NL +
2754                     "+++ " + iotaPath + "\t(working copy)" + NL +
2755                     expectedDiffBody;
2756 
2757                 try
2758                 {
2759                     // Two-path diff of WC paths.
2760                     client.diff(iotaPath, Revision.BASE,
2761                                 iotaPath, Revision.WORKING,
2762                                 diffOutput.getPath(), false, true, true, false);
2763                     assertFileContentsEquals(assertPrefix +
2764                                              diffOutput.getPath() + '\'',
2765                                              expectedDiffOutput, diffOutput);
2766                     diffOutput.delete();
2767                 }
2768                 catch (ClientException e)
2769                 {
2770                     fail(revisionPrefix + e.getMessage());
2771                 }
2772 
2773                 try
2774                 {
2775                     // Peg revision diff of a single file.
2776                     client.diff(thisTest.getUrl() + "/iota", Revision.HEAD,
2777                                 new Revision.Number(operativeRevision), Revision.HEAD,
2778                                 diffOutput.getPath(), false, true, true, false);
2779                     assertFileContentsEquals(assertPrefix +
2780                                              diffOutput.getPath() + '\'',
2781                                              "", diffOutput);
2782 
2783                     diffOutput.delete();
2784                 }
2785                 catch (ClientException e)
2786                 {
2787                     fail(revisionPrefix + e.getMessage());
2788                 }
2789 
2790                // Test svn diff with a relative path.
2791                 expectedDiffOutput = "Index: iota" + NL + sepLine +
2792                     "--- iota\t(revision " + operativeRevision + ")" + NL +
2793                     "+++ iota\t(working copy)" + NL +
2794                     expectedDiffBody;
2795                 try
2796                 {
2797                     client.diff(iotaPath, Revision.BASE, iotaPath,
2798                                 Revision.WORKING, wcPath, diffOutput.getPath(),
2799                                 Depth.infinity, null, true, true, false);
2800                     assertFileContentsEquals(assertPrefix +
2801                                              diffOutput.getPath() + '\'',
2802                                              expectedDiffOutput, diffOutput);
2803                     diffOutput.delete();
2804                 }
2805                 catch (ClientException e)
2806                 {
2807                     fail(revisionPrefix + e.getMessage());
2808                 }
2809 
2810                 try
2811                 {
2812                     // Test svn diff with a relative path and trailing slash.
2813                     client.diff(iotaPath, Revision.BASE, iotaPath,
2814                                 Revision.WORKING, wcPath + "/",
2815                                 diffOutput.getPath(), Depth.infinity, null,
2816                                 true, true, false);
2817                     assertFileContentsEquals(assertPrefix +
2818                                              diffOutput.getPath() + '\'',
2819                                              expectedDiffOutput, diffOutput);
2820                     diffOutput.delete();
2821                 }
2822                 catch (ClientException e)
2823                 {
2824                     fail(revisionPrefix + e.getMessage());
2825                 }
2826 
2827             }
2828 
2829     }
2830 
assertFileContentsEquals(String msg, String expected, File actual)2831     private void assertFileContentsEquals(String msg, String expected,
2832                                           File actual)
2833         throws IOException
2834     {
2835         FileReader reader = new FileReader(actual);
2836         StringBuffer buf = new StringBuffer();
2837         int ch;
2838         while ((ch = reader.read()) != -1)
2839         {
2840             buf.append((char) ch);
2841         }
2842         assertEquals(msg, expected, buf.toString());
2843     }
2844 
2845     /**
2846      * Test the {@link SVNClientInterface.diffSummarize()} API.
2847      * @since 1.5
2848      */
testDiffSummarize()2849     public void testDiffSummarize()
2850         throws SubversionException, IOException
2851     {
2852         OneTest thisTest = new OneTest(false);
2853         DiffSummaries summaries = new DiffSummaries();
2854         // Perform a recursive diff summary, ignoring ancestry.
2855         client.diffSummarize(thisTest.getUrl(), new Revision.Number(0),
2856                              thisTest.getUrl(), Revision.HEAD, Depth.infinity,
2857                              null, false, summaries);
2858         assertExpectedDiffSummaries(summaries);
2859 
2860         summaries.clear();
2861         // Perform a recursive diff summary with a peg revision,
2862         // ignoring ancestry.
2863         client.diffSummarize(thisTest.getUrl(), Revision.HEAD,
2864                              new Revision.Number(0), Revision.HEAD,
2865                              Depth.infinity, null, false, summaries);
2866         assertExpectedDiffSummaries(summaries);
2867     }
2868 
assertExpectedDiffSummaries(DiffSummaries summaries)2869     private void assertExpectedDiffSummaries(DiffSummaries summaries)
2870     {
2871         assertEquals("Wrong number of diff summary descriptors", 20,
2872                      summaries.size());
2873 
2874         // Rigorously inspect one of our DiffSummary notifications.
2875         final String BETA_PATH = "A/B/E/beta";
2876         DiffSummary betaDiff = (DiffSummary) summaries.get(BETA_PATH);
2877         assertNotNull("No diff summary for " + BETA_PATH, betaDiff);
2878         assertEquals("Incorrect path for " + BETA_PATH, BETA_PATH,
2879                      betaDiff.getPath());
2880         assertTrue("Incorrect diff kind for " + BETA_PATH,
2881                    DiffSummary.DiffKind.ADDED.equals(betaDiff.getDiffKind()));
2882         assertEquals("Incorrect props changed notice for " + BETA_PATH,
2883                      false, betaDiff.propsChanged());
2884         assertEquals("Incorrect node kind for " + BETA_PATH, 1,
2885                      betaDiff.getNodeKind());
2886     }
2887 
2888     /**
2889      * test the basic SVNClient.isAdminDirectory functionality
2890      * @throws Throwable
2891      * @since 1.2
2892      */
testBasicIsAdminDirectory()2893     public void testBasicIsAdminDirectory() throws Throwable
2894     {
2895         // build the test setup
2896         OneTest thisTest = new OneTest();
2897         Notify2 notify = new Notify2()
2898         {
2899             public void onNotify(NotifyInformation info)
2900             {
2901                 client.isAdminDirectory(".svn");
2902             }
2903         };
2904         client.notification2(notify);
2905         // update the test
2906         assertEquals("wrong revision number from update",
2907                 client.update(thisTest.getWCPath(), null, true), 1);
2908     }
2909 
testBasicCancelOperation()2910     public void testBasicCancelOperation() throws Throwable
2911     {
2912         // build the test setup
2913         OneTest thisTest = new OneTest();
2914         Notify2 notify = new Notify2()
2915         {
2916             public void onNotify(NotifyInformation info)
2917             {
2918                 try
2919                 {
2920                     client.cancelOperation();
2921                 }
2922                 catch (ClientException e)
2923                 {
2924                     fail(e.getMessage());
2925                 }
2926             }
2927         };
2928         client.notification2(notify);
2929         // update the test to try to cancel an operation
2930         try
2931         {
2932             client.update(thisTest.getWCPath(), null, true);
2933             fail("missing exception for canceled operation");
2934         }
2935         catch (ClientException e)
2936         {
2937             // this is expected
2938         }
2939     }
2940 
2941     private static class CountingProgressListener implements ProgressListener
2942     {
onProgress(ProgressEvent event)2943         public void onProgress(ProgressEvent event)
2944         {
2945             // TODO: Examine the byte counts from "event".
2946             gotProgress = true;
2947         }
2948         public boolean gotProgress = false;
2949     }
2950 
testDataTransferProgressReport()2951     public void testDataTransferProgressReport() throws Throwable
2952     {
2953         // ### FIXME: This isn't working over ra_local, because
2954         // ### ra_local is not invoking the progress callback.
2955         if (SVNTests.rootUrl.startsWith("file://"))
2956             return;
2957 
2958         // build the test setup
2959         OneTest thisTest = new OneTest();
2960         CountingProgressListener listener = new CountingProgressListener();
2961         client.setProgressListener(listener);
2962 
2963         // Perform an update to exercise the progress notification.
2964         client.update(thisTest.getWCPath(), null, true);
2965         if (!listener.gotProgress)
2966             fail("No progress reported");
2967     }
2968 
2969     /**
2970      * Test the basic tree conflict functionality.
2971      * @throws Throwable
2972      */
testTreeConflict()2973     public void testTreeConflict() throws Throwable
2974     {
2975         // build the test setup. Used for the changes
2976         OneTest thisTest = new OneTest();
2977         WC wc = thisTest.getWc();
2978 
2979         // build the backup test setup. That is the one that will be updated
2980         OneTest tcTest = thisTest.copy(".tree-conflict");
2981 
2982 
2983         // Move files from A/B/E to A/B/F.
2984         String[] srcPaths = { "alpha" };
2985         for (int i = 0; i < srcPaths.length; i++)
2986         {
2987             String fileName = srcPaths[i];
2988             srcPaths[i] = new File(thisTest.getWorkingCopy(),
2989                                    "A/B/E/" + fileName).getPath();
2990 
2991             wc.addItem("A/B/F/" + fileName,
2992                        wc.getItemContent("A/B/E/" + fileName));
2993             wc.setItemWorkingCopyRevision("A/B/F/" + fileName, 2);
2994             addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
2995                                   "A/B/F/" + fileName, NodeKind.file,
2996                                   CommitItemStateFlags.Add |
2997                                   CommitItemStateFlags.IsCopy);
2998 
2999             wc.removeItem("A/B/E/" + fileName);
3000             addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
3001                                   "A/B/E/" + fileName, NodeKind.file,
3002                                   CommitItemStateFlags.Delete);
3003         }
3004         client.move(srcPaths,
3005                     new File(thisTest.getWorkingCopy(), "A/B/F").getPath(),
3006                     null, false, true, false, null);
3007 
3008         // Commit the changes, and check the state of the WC.
3009         assertEquals("Unexpected WC revision number after commit",
3010                      client.commit(new String[] { thisTest.getWCPath() },
3011                                    "Move files", true), 2);
3012         thisTest.checkStatus();
3013 
3014         // modify A/B/E/alpha in second working copy
3015         File alpha = new File(tcTest.getWorkingCopy(), "A/B/E/alpha");
3016         PrintWriter alphaWriter = new PrintWriter(new FileOutputStream(alpha, true));
3017         alphaWriter.print("appended alpha text");
3018         alphaWriter.close();
3019 
3020         // update the tc test
3021         assertEquals("wrong revision number from update",
3022                      client.update(tcTest.getWCPath(), null, true),
3023                      2);
3024 
3025         // set the expected working copy layout for the tc test
3026         tcTest.getWc().addItem("A/B/F/alpha",
3027                 tcTest.getWc().getItemContent("A/B/E/alpha"));
3028         tcTest.getWc().setItemWorkingCopyRevision("A/B/F/alpha", 2);
3029         // we expect the tree conflict to turn the existing item into
3030         // a scheduled-add with history.
3031         tcTest.getWc().setItemTextStatus("A/B/E/alpha", StatusKind.added);
3032         tcTest.getWc().setItemTextStatus("A/B/F/alpha", StatusKind.normal);
3033 
3034         // check the status of the working copy of the tc test
3035         tcTest.checkStatus();
3036 
3037         // get the Info2 of the tree conflict
3038         MyInfoCallback callback = new MyInfoCallback();
3039         client.info2(tcTest.getWCPath() + "/A/B/E/alpha", null,
3040                 null, Depth.unknown, null, callback);
3041         ConflictDescriptor conflict = callback.getInfo().getConflictDescriptor();
3042 
3043         assertNotNull("Conflict should not be null", conflict);
3044 
3045         assertEquals(conflict.getSrcLeftVersion().getNodeKind(), NodeKind.file);
3046         assertEquals(conflict.getSrcLeftVersion().getReposURL() + "/" +
3047                 conflict.getSrcLeftVersion().getPathInRepos(), tcTest.getUrl() + "/A/B/E/alpha");
3048         assertEquals(conflict.getSrcLeftVersion().getPegRevision(), 1L);
3049 
3050         if (conflict.getSrcRightVersion() != null)
3051         {
3052             assertEquals(conflict.getSrcRightVersion().getNodeKind(), NodeKind.none);
3053             assertEquals(conflict.getSrcRightVersion().getReposURL(), tcTest.getUrl());
3054             assertEquals(conflict.getSrcRightVersion().getPegRevision(), 2L);
3055         }
3056     }
3057 
3058     /**
3059      * Test tolerance of unversioned obstructions when adding paths with
3060      * {@link org.tigris.subversion.javahl.SVNClient#checkout()},
3061      * {@link org.tigris.subversion.javahl.SVNClient#update()}, and
3062      * {@link org.tigris.subversion.javahl.SVNClient#doSwitch()}
3063      * @throws IOException
3064      * @throws SubversionException
3065      */
3066     /*
3067       This is currently commented out, because we don't have an XFail method
3068       for JavaHL.  The resolution is pending the result of issue #3680:
3069       https://issues.apache.org/jira/browse/SVN-3680
3070     public void testObstructionTolerance()
3071             throws SubversionException, IOException
3072     {
3073         // build the test setup
3074         OneTest thisTest = new OneTest();
3075 
3076         File file;
3077         PrintWriter pw;
3078 
3079         // ----- TEST CHECKOUT -----
3080         // Use export to make unversioned obstructions for a second
3081         // WC checkout (deleting export target from previous tests
3082         // first if it exists).
3083         String secondWC = thisTest.getWCPath() + ".backup1";
3084         removeDirOrFile(new File(secondWC));
3085         client.doExport(thisTest.getUrl(), secondWC, null, false);
3086 
3087         // Make an obstructing file that conflicts with add coming from repos
3088         file = new File(secondWC, "A/B/lambda");
3089         pw = new PrintWriter(new FileOutputStream(file));
3090         pw.print("This is the conflicting obstructiong file 'lambda'.");
3091         pw.close();
3092 
3093         // Attempt to checkout backup WC without "--force"...
3094         try
3095         {
3096             // ...should fail
3097             client.checkout(thisTest.getUrl(), secondWC, null, null,
3098                             Depth.infinity, false, false);
3099             fail("obstructed checkout should fail by default");
3100         }
3101         catch (ClientException expected)
3102         {
3103         }
3104 
3105         // Attempt to checkout backup WC with "--force"
3106         // so obstructions are tolerated
3107         client.checkout(thisTest.getUrl(), secondWC, null, null,
3108                         Depth.infinity, false, true);
3109 
3110         // Check the WC status, the only status should be a text
3111         // mod to lambda.  All the other obstructing files were identical
3112         Status[] secondWCStatus = client.status(secondWC, true, false,
3113                                                 false, false, false);
3114         if (!(secondWCStatus.length == 1 &&
3115             secondWCStatus[0].getPath().endsWith("A/B/lambda") &&
3116             secondWCStatus[0].getTextStatus() == StatusKind.modified &&
3117             secondWCStatus[0].getPropStatus() == StatusKind.none))
3118         {
3119             fail("Unexpected WC status after co with " +
3120                  "unversioned obstructions");
3121         }
3122 
3123         // Make a third WC to test obstruction tolerance of sw and up.
3124         OneTest backupTest = thisTest.copy(".backup2");
3125 
3126         // ----- TEST UPDATE -----
3127         // r2: Add a file A/D/H/nu
3128         file = new File(thisTest.getWorkingCopy(), "A/D/H/nu");
3129         pw = new PrintWriter(new FileOutputStream(file));
3130         pw.print("This is the file 'nu'.");
3131         pw.close();
3132         client.add(file.getAbsolutePath(), false);
3133         addExpectedCommitItem(thisTest.getWCPath(), thisTest.getUrl(),
3134                               "A/D/H/nu", NodeKind.file,
3135                               CommitItemStateFlags.TextMods +
3136                               CommitItemStateFlags.Add);
3137         assertEquals("wrong revision number from commit",
3138                      client.commit(new String[] {thisTest.getWCPath()},
3139                      "log msg", true), 2);
3140         thisTest.getWc().addItem("A/D/H/nu", "This is the file 'nu'.");
3141         Status status = client.singleStatus(thisTest.getWCPath() +
3142                                             "/A/D/H/nu",
3143                                             false);
3144 
3145         // Add an unversioned file A/D/H/nu to the backup WC
3146         file = new File(backupTest.getWorkingCopy(), "A/D/H/nu");
3147         pw = new PrintWriter(new FileOutputStream(file));
3148         pw.print("This is the file 'nu'.");
3149         pw.close();
3150 
3151         // Attempt to update backup WC without "--force"
3152         try
3153         {
3154             // obstructed update should fail
3155             client.update(backupTest.getWCPath(), null, true);
3156             fail("obstructed update should fail by default");
3157         }
3158         catch (ClientException expected)
3159         {
3160         }
3161 
3162         // Attempt to update backup WC with "--force"
3163         assertEquals("wrong revision from update",
3164                      client.update(backupTest.getWCPath(),
3165                                    null, Depth.infinity, false, false, true),
3166                      2);
3167 
3168         // ----- TEST SWITCH -----
3169         // Add an unversioned file A/B/E/nu to the backup WC
3170         // The file differs from A/D/H/nu
3171         file = new File(backupTest.getWorkingCopy(), "A/B/E/nu");
3172         pw = new PrintWriter(new FileOutputStream(file));
3173         pw.print("This is yet another file 'nu'.");
3174         pw.close();
3175 
3176         // Add an unversioned file A/B/E/chi to the backup WC
3177         // The file is identical to A/D/H/chi.
3178         file = new File(backupTest.getWorkingCopy(), "A/B/E/chi");
3179         pw = new PrintWriter(new FileOutputStream(file));
3180         pw.print("This is the file 'chi'.");
3181         pw.close();
3182 
3183         // Attempt to switch A/B/E to A/D/H without "--force"
3184         try
3185         {
3186             // obstructed switch should fail
3187             client.doSwitch(backupTest.getWCPath() + "/A/B/E",
3188                             backupTest.getUrl() + "/A/D/H",
3189                             null, true);
3190             fail("obstructed switch should fail by default");
3191         }
3192         catch (ClientException expected)
3193         {
3194         }
3195 
3196         // Complete the switch using "--force" and check the status
3197         client.doSwitch(backupTest.getWCPath() + "/A/B/E",
3198                         backupTest.getUrl() + "/A/D/H",
3199                         Revision.HEAD, Revision.HEAD, Depth.infinity,
3200                         false, false, true);
3201 
3202         backupTest.getWc().setItemIsSwitched("A/B/E",true);
3203         backupTest.getWc().removeItem("A/B/E/alpha");
3204         backupTest.getWc().removeItem("A/B/E/beta");
3205         backupTest.getWc().addItem("A/B/E/nu",
3206                                    "This is yet another file 'nu'.");
3207         backupTest.getWc().setItemTextStatus("A/B/E/nu", Status.Kind.modified);
3208         backupTest.getWc().addItem("A/D/H/nu",
3209                                    "This is the file 'nu'.");
3210         backupTest.getWc().addItem("A/B/E/chi",
3211                                    backupTest.getWc().getItemContent("A/D/H/chi"));
3212         backupTest.getWc().addItem("A/B/E/psi",
3213                                    backupTest.getWc().getItemContent("A/D/H/psi"));
3214         backupTest.getWc().addItem("A/B/E/omega",
3215                                    backupTest.getWc().getItemContent("A/D/H/omega"));
3216 
3217         backupTest.checkStatus();
3218     }*/
3219 
3220     /**
3221      * Test basic blame functionality.  This test marginally tests blame
3222      * correctness, mainly just that the blame APIs link correctly.
3223      * @throws Throwable
3224      * @since 1.5
3225      */
testBasicBlame()3226     public void testBasicBlame() throws Throwable
3227     {
3228         OneTest thisTest = new OneTest();
3229         // Test the old interface to be sure it still works
3230         byte[] result = client.blame(thisTest.getWCPath() + "/iota", Revision
3231                                      .getInstance(1), Revision.getInstance(1));
3232         assertEquals("     1    jrandom This is the file 'iota'.\n",
3233                      new String(result));
3234 
3235         // Test the current interface
3236         BlameCallbackImpl callback = new BlameCallbackImpl();
3237         client.blame(thisTest.getWCPath() + "/iota", Revision.getInstance(1),
3238                      Revision.getInstance(1), callback);
3239         assertEquals(1, callback.numberOfLines());
3240         BlameCallbackImpl.BlameLine line = callback.getBlameLine(0);
3241         if (line != null)
3242         {
3243             assertEquals(1, line.getRevision());
3244             assertEquals("jrandom", line.getAuthor());
3245         }
3246     }
3247 
3248     /**
3249      * Test commit of arbitrary revprops.
3250      * @throws Throwable
3251      * @since 1.5
3252      */
3253     @SuppressWarnings("unchecked")
testCommitRevprops()3254     public void testCommitRevprops() throws Throwable
3255     {
3256 
3257         class RevpropLogCallback implements LogMessageCallback
3258         {
3259             Map revprops;
3260 
3261             public void singleMessage(ChangePath[] changedPaths,
3262                                       long revision,
3263                                       Map revprops,
3264                                       boolean hasChildren)
3265             {
3266                 this.revprops = revprops;
3267             }
3268 
3269             public Map getRevprops()
3270             {
3271                 return revprops;
3272             }
3273         }
3274 
3275         // build the test setup
3276         OneTest thisTest = new OneTest();
3277 
3278         // modify file A/mu
3279         File mu = new File(thisTest.getWorkingCopy(), "A/mu");
3280         PrintWriter muWriter = new PrintWriter(new FileOutputStream(mu, true));
3281         muWriter.print("appended mu text");
3282         muWriter.close();
3283         thisTest.getWc().setItemWorkingCopyRevision("A/mu", 2);
3284         thisTest.getWc().setItemContent("A/mu",
3285                 thisTest.getWc().getItemContent("A/mu") + "appended mu text");
3286         addExpectedCommitItem(thisTest.getWCPath(),
3287                 thisTest.getUrl(), "A/mu",NodeKind.file,
3288                 CommitItemStateFlags.TextMods);
3289 
3290         // commit the changes, with some extra revprops
3291         Map revprops = new HashMap();
3292         revprops.put("kfogel", "rockstar");
3293         revprops.put("cmpilato", "theman");
3294         assertEquals("wrong revision number from commit",
3295                      client.commit(new String[]{thisTest.getWCPath()},
3296                                    "log msg", Depth.infinity, true, true,
3297                                    null, revprops),
3298                      2);
3299 
3300         // check the status of the working copy
3301         thisTest.checkStatus();
3302 
3303         // Fetch our revprops from the server
3304         RevpropLogCallback callback = new RevpropLogCallback();
3305         client.logMessages(thisTest.getWCPath(), Revision.getInstance(2),
3306                            Revision.getInstance(2),
3307                            Revision.getInstance(2), false, false, false,
3308                            new String[] {"kfogel", "cmpilato"}, 0,
3309                            callback);
3310         Map fetchedProps = callback.getRevprops();
3311 
3312         assertEquals("wrong number of fetched revprops", revprops.size(),
3313                      fetchedProps.size());
3314         Set keys = fetchedProps.keySet();
3315         for (Iterator it = keys.iterator(); it.hasNext(); )
3316           {
3317             String key = (String) it.next();
3318             assertEquals("revprops check", revprops.get(key),
3319                          fetchedProps.get(key));
3320           }
3321     }
3322 
3323     /**
3324      * @return <code>file</code> converted into a -- possibly
3325      * <code>canonical</code>-ized -- Subversion-internal path
3326      * representation.
3327      */
fileToSVNPath(File file, boolean canonical)3328     private String fileToSVNPath(File file, boolean canonical)
3329     {
3330         // JavaHL need paths with '/' separators
3331         if (canonical)
3332         {
3333             try
3334             {
3335                 return file.getCanonicalPath().replace('\\', '/');
3336             }
3337             catch (IOException e)
3338             {
3339                 return null;
3340             }
3341         }
3342         else
3343         {
3344             return file.getPath().replace('\\', '/');
3345         }
3346     }
3347 
3348 
3349     /**
3350      * A DiffSummaryReceiver implementation which collects all DiffSummary
3351      * notifications.
3352      */
3353     private static class DiffSummaries extends HashMap
3354         implements DiffSummaryReceiver
3355     {
3356         // Update the serialVersionUID when there is a incompatible
3357         // change made to this class.
3358         private static final long serialVersionUID = 1L;
3359 
3360         @SuppressWarnings("unchecked")
onSummary(DiffSummary descriptor)3361         public void onSummary(DiffSummary descriptor)
3362         {
3363             super.put(descriptor.getPath(), descriptor);
3364         }
3365     }
3366 
3367     private class MyChangelistCallback extends HashMap
3368         implements ChangelistCallback
3369     {
3370         private static final long serialVersionUID = 1L;
3371 
3372         @SuppressWarnings("unchecked")
doChangelist(String path, String changelist)3373         public void doChangelist(String path, String changelist)
3374         {
3375             path = fileToSVNPath(new File(path), true);
3376             if (super.containsKey(path))
3377             {
3378                 // Append the changelist to the existing list
3379                 List changelistList = (List) super.get(path);
3380                 changelistList.add(changelist);
3381             }
3382             else
3383             {
3384                 // Create a new changelist with that list
3385                 List changelistList = new ArrayList();
3386                 changelistList.add(changelist);
3387                 super.put(path, changelistList);
3388             }
3389         }
3390 
get(String path)3391         public List get(String path)
3392         {
3393             return (List) super.get(path);
3394         }
3395     }
3396 
3397     private class MyInfoCallback implements InfoCallback {
3398         private Info2 info;
3399 
singleInfo(Info2 info)3400         public void singleInfo(Info2 info) {
3401             this.info = info;
3402         }
3403 
getInfo()3404         public Info2 getInfo() {
3405             return info;
3406         }
3407 
3408     }
3409 }
3410