~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.apache.subversion.javahl;
 
24
 
 
25
import org.apache.subversion.javahl.*;
 
26
import org.apache.subversion.javahl.remote.*;
 
27
import org.apache.subversion.javahl.callback.*;
 
28
import org.apache.subversion.javahl.types.*;
 
29
 
 
30
import java.util.Arrays;
 
31
import java.util.ArrayList;
 
32
import java.util.Collections;
 
33
import java.util.Date;
 
34
import java.util.List;
 
35
import java.util.Set;
 
36
import java.util.Map;
 
37
import java.util.HashMap;
 
38
import java.util.HashSet;
 
39
import java.io.ByteArrayInputStream;
 
40
import java.io.ByteArrayOutputStream;
 
41
import java.io.File;
 
42
import java.io.FileInputStream;
 
43
import java.io.FileOutputStream;
 
44
import java.io.IOException;
 
45
import java.nio.charset.Charset;
 
46
import java.security.MessageDigest;
 
47
import java.security.NoSuchAlgorithmException;
 
48
 
 
49
/**
 
50
 * This class is used for testing the SVNReposAccess class
 
51
 *
 
52
 * More methodes for testing are still needed
 
53
 */
 
54
public class SVNRemoteTests extends SVNTests
 
55
{
 
56
    protected OneTest thisTest;
 
57
 
 
58
    public SVNRemoteTests()
 
59
    {
 
60
    }
 
61
 
 
62
    public SVNRemoteTests(String name)
 
63
    {
 
64
        super(name);
 
65
    }
 
66
 
 
67
    protected void setUp() throws Exception
 
68
    {
 
69
        super.setUp();
 
70
 
 
71
        thisTest = new OneTest();
 
72
    }
 
73
 
 
74
    public static ISVNRemote getSession(String url, String configDirectory)
 
75
    {
 
76
        try
 
77
        {
 
78
            RemoteFactory factory = new RemoteFactory();
 
79
            factory.setConfigDirectory(configDirectory);
 
80
            factory.setUsername(USERNAME);
 
81
            // Do not set default password, exercise prompter instead.
 
82
            if (DefaultAuthn.useDeprecated())
 
83
                factory.setPrompt(DefaultAuthn.getDeprecated());
 
84
            else
 
85
                factory.setPrompt(DefaultAuthn.getDefault());
 
86
 
 
87
            ISVNRemote raSession = factory.openRemoteSession(url);
 
88
            assertNotNull("Null session was returned by factory", raSession);
 
89
            return raSession;
 
90
        }
 
91
        catch (Exception ex)
 
92
        {
 
93
            throw new RuntimeException(ex);
 
94
        }
 
95
    }
 
96
 
 
97
    private ISVNRemote getSession()
 
98
    {
 
99
        return getSession(getTestRepoUrl(), super.conf.getAbsolutePath());
 
100
    }
 
101
 
 
102
    /**
 
103
     * Test the basic SVNAdmin.create functionality
 
104
     * @throws SubversionException
 
105
     */
 
106
    public void testCreate()
 
107
        throws SubversionException, IOException
 
108
    {
 
109
        assertTrue("repository exists", thisTest.getRepository().exists());
 
110
    }
 
111
 
 
112
    public void testGetSession_ConfigConstructor() throws Exception
 
113
    {
 
114
        ISVNRemote session;
 
115
        try
 
116
        {
 
117
            if (DefaultAuthn.useDeprecated())
 
118
                session = new RemoteFactory(
 
119
                    super.conf.getAbsolutePath(),
 
120
                    USERNAME, null, // Do not set default password.
 
121
                    DefaultAuthn.getDeprecated(),
 
122
                    null, null, null)
 
123
                    .openRemoteSession(getTestRepoUrl());
 
124
            else
 
125
                session = new RemoteFactory(
 
126
                    super.conf.getAbsolutePath(),
 
127
                    USERNAME, null, // Do not set default password.
 
128
                    DefaultAuthn.getDefault(),
 
129
                    null, null, null)
 
130
                    .openRemoteSession(getTestRepoUrl());
 
131
        }
 
132
        catch (ClientException ex)
 
133
        {
 
134
            throw new RuntimeException(ex);
 
135
        }
 
136
        assertNotNull("Null session was returned by factory", session);
 
137
        assertEquals(getTestRepoUrl(), session.getSessionUrl());
 
138
    }
 
139
 
 
140
    public void testDispose() throws Exception
 
141
    {
 
142
        ISVNRemote session = getSession();
 
143
        session.dispose();
 
144
    }
 
145
 
 
146
    public void testSessionGC() throws Exception
 
147
    {
 
148
        int svnErrorCode = 0;
 
149
        try {
 
150
            try {
 
151
                String prefix = getTestRepoUrl().substring(
 
152
                    0, 1 + getTestRepoUrl().lastIndexOf("/"));
 
153
 
 
154
            if (DefaultAuthn.useDeprecated())
 
155
                new RemoteFactory(
 
156
                    super.conf.getAbsolutePath(),
 
157
                    USERNAME, null, // Do not set default password.
 
158
                    DefaultAuthn.getDeprecated(),
 
159
                    null, null, null)
 
160
                    .openRemoteSession(prefix + "repositorydoesnotexisthere");
 
161
            else
 
162
                new RemoteFactory(
 
163
                    super.conf.getAbsolutePath(),
 
164
                    USERNAME, null, // Do not set default password.
 
165
                    DefaultAuthn.getDefault(),
 
166
                    null, null, null)
 
167
                    .openRemoteSession(prefix + "repositorydoesnotexisthere");
 
168
            }
 
169
            finally
 
170
            {
 
171
                for(int i = 0; i < 100; i++)
 
172
                {
 
173
                    Runtime.getRuntime().gc(); // GC should run finalize
 
174
 
 
175
                    // Do something
 
176
                    byte[] memEater = new byte[1024 * 1024];
 
177
                    Arrays.fill(memEater, (byte) i);
 
178
 
 
179
                    // Do some more javahl activity (this url is OK)
 
180
                    final ISVNRemote session = getSession();
 
181
                    session.getLatestRevision();
 
182
                    session.dispose();
 
183
                }
 
184
            }
 
185
        }
 
186
        catch (ClientException ex)
 
187
        {
 
188
            List<ClientException.ErrorMessage> msgs = ex.getAllMessages();
 
189
            svnErrorCode = msgs.get(msgs.size() - 1).getCode();
 
190
        }
 
191
 
 
192
        assertTrue(svnErrorCode == 180001    // file:
 
193
                   || svnErrorCode == 210005 // svn:
 
194
                   || svnErrorCode == 2);    // http:
 
195
    }
 
196
 
 
197
    public void testDatedRev() throws Exception
 
198
    {
 
199
        ISVNRemote session = getSession();
 
200
 
 
201
        long revision = session.getRevisionByDate(new Date());
 
202
        assertEquals(revision, 1);
 
203
    }
 
204
 
 
205
    public void testGetLocks() throws Exception
 
206
    {
 
207
        ISVNRemote session = getSession();
 
208
 
 
209
        Set<String> iotaPathSet = new HashSet<String>(1);
 
210
        String iotaPath = thisTest.getWCPath() + "/iota";
 
211
        iotaPathSet.add(iotaPath);
 
212
 
 
213
        client.lock(iotaPathSet, "foo", false);
 
214
 
 
215
        Map<String, Lock> locks = session.getLocks("iota", Depth.infinity);
 
216
 
 
217
        assertEquals(locks.size(), 1);
 
218
        Lock lock = locks.get("/iota");
 
219
        assertNotNull(lock);
 
220
        assertEquals(lock.getOwner(), "jrandom");
 
221
    }
 
222
 
 
223
    public void testCheckPath() throws Exception
 
224
    {
 
225
        ISVNRemote session = getSession();
 
226
 
 
227
        NodeKind kind = session.checkPath("iota", 1);
 
228
        assertEquals(NodeKind.file, kind);
 
229
 
 
230
        kind = session.checkPath("iota", 0);
 
231
        assertEquals(NodeKind.none, kind);
 
232
 
 
233
        kind = session.checkPath("A", 1);
 
234
        assertEquals(NodeKind.dir, kind);
 
235
    }
 
236
 
 
237
    public void testStat() throws Exception
 
238
    {
 
239
        ISVNRemote session = getSession();
 
240
 
 
241
        DirEntry dirent = session.stat("iota", 1);
 
242
        assertEquals(NodeKind.file, dirent.getNodeKind());
 
243
 
 
244
        dirent = session.stat("iota", 0);
 
245
        assertNull(dirent);
 
246
 
 
247
        dirent = session.stat("A", 1);
 
248
        assertEquals(NodeKind.dir, dirent.getNodeKind());
 
249
    }
 
250
 
 
251
    private String getTestRepoUrl()
 
252
    {
 
253
        return thisTest.getUrl().toASCIIString();
 
254
    }
 
255
 
 
256
    public void testGetLatestRevision() throws Exception
 
257
    {
 
258
        ISVNRemote session = getSession();
 
259
        long revision = session.getLatestRevision();
 
260
        assertEquals(revision, 1);
 
261
    }
 
262
 
 
263
    public void testGetUUID() throws Exception
 
264
    {
 
265
        ISVNRemote session = getSession();
 
266
 
 
267
        /*
 
268
         * Test UUID
 
269
         * TODO: Test for actual UUID once test dump file has
 
270
         * fixed UUID
 
271
         */
 
272
        assertNotNull(session.getReposUUID());
 
273
    }
 
274
 
 
275
    public void testGetUrl() throws Exception
 
276
    {
 
277
        ISVNRemote session = getSession();
 
278
 
 
279
        assertEquals(getTestRepoUrl(), session.getSessionUrl());
 
280
    }
 
281
 
 
282
    public void testGetRootUrl() throws Exception
 
283
    {
 
284
        ISVNRemote session = getSession();
 
285
        session.reparent(session.getSessionUrl() + "/A/B/E");
 
286
        assertEquals(getTestRepoUrl(), session.getReposRootUrl());
 
287
    }
 
288
 
 
289
    public void testGetUrl_viaSVNClient() throws Exception
 
290
    {
 
291
        ISVNRemote session = client.openRemoteSession(getTestRepoUrl());
 
292
 
 
293
        assertEquals(getTestRepoUrl(), session.getSessionUrl());
 
294
    }
 
295
 
 
296
    public void testGetUrl_viaSVNClientWorkingCopy() throws Exception
 
297
    {
 
298
        ISVNRemote session = client.openRemoteSession(thisTest.getWCPath());
 
299
 
 
300
        assertEquals(getTestRepoUrl(), session.getSessionUrl());
 
301
    }
 
302
 
 
303
    public void testReparent() throws Exception
 
304
    {
 
305
        ISVNRemote session = getSession();
 
306
        String newUrl = session.getSessionUrl() + "/A/B/E";
 
307
        session.reparent(newUrl);
 
308
        assertEquals(newUrl, session.getSessionUrl());
 
309
    }
 
310
 
 
311
    public void testGetRelativePath() throws Exception
 
312
    {
 
313
        ISVNRemote session = getSession();
 
314
        String baseUrl = session.getSessionUrl() + "/A/B/E";
 
315
        session.reparent(baseUrl);
 
316
 
 
317
        String relPath = session.getSessionRelativePath(baseUrl + "/alpha");
 
318
        assertEquals("alpha", relPath);
 
319
 
 
320
        relPath = session.getReposRelativePath(baseUrl + "/beta");
 
321
        assertEquals("A/B/E/beta", relPath);
 
322
    }
 
323
 
 
324
    public void testGetCommitEditor() throws Exception
 
325
    {
 
326
        ISVNRemote session = getSession();
 
327
        session.getCommitEditor(null, null, null, false);
 
328
    }
 
329
 
 
330
    public void testDisposeCommitEditor() throws Exception
 
331
    {
 
332
        ISVNRemote session = getSession();
 
333
        session.getCommitEditor(null, null, null, false);
 
334
        session.dispose();
 
335
    }
 
336
 
 
337
    public void testHasCapability() throws Exception
 
338
    {
 
339
        ISVNRemote session = getSession();
 
340
        assert(session.hasCapability(ISVNRemote.Capability.depth));
 
341
    }
 
342
 
 
343
    public void testChangeRevpropNoAtomic() throws Exception
 
344
    {
 
345
        Charset UTF8 = Charset.forName("UTF-8");
 
346
        ISVNRemote session = getSession();
 
347
 
 
348
        boolean atomic =
 
349
            session.hasCapability(ISVNRemote.Capability.atomic_revprops);
 
350
 
 
351
        if (atomic)
 
352
            return;
 
353
 
 
354
        boolean exceptioned = false;
 
355
        try
 
356
        {
 
357
            byte[] oldValue = "bumble".getBytes(UTF8);
 
358
            byte[] newValue = "bee".getBytes(UTF8);
 
359
            session.changeRevisionProperty(1, "svn:author",
 
360
                                           oldValue, newValue);
 
361
        }
 
362
        catch (IllegalArgumentException ex)
 
363
        {
 
364
            exceptioned = true;
 
365
        }
 
366
        assert(exceptioned);
 
367
    }
 
368
 
 
369
    public void testChangeRevpropAtomic() throws Exception
 
370
    {
 
371
        Charset UTF8 = Charset.forName("UTF-8");
 
372
        ISVNRemote session = getSession();
 
373
 
 
374
        boolean atomic =
 
375
            session.hasCapability(ISVNRemote.Capability.atomic_revprops);
 
376
 
 
377
        if (!atomic)
 
378
            return;
 
379
 
 
380
        byte[] oldValue = client.revProperty(getTestRepoUrl(), "svn:author",
 
381
                                             Revision.getInstance(1));
 
382
        byte[] newValue = "rayjandom".getBytes(UTF8);
 
383
        try
 
384
        {
 
385
            session.changeRevisionProperty(1, "svn:author",
 
386
                                           oldValue, newValue);
 
387
        }
 
388
        catch (ClientException ex)
 
389
        {
 
390
            ClientException.ErrorMessage error = null;
 
391
            for (ClientException.ErrorMessage m : ex.getAllMessages())
 
392
                if (!m.isGeneric()) {
 
393
                    error = m;
 
394
                    break;
 
395
                }
 
396
 
 
397
            if (error == null)
 
398
                fail("Failed with no error message");
 
399
 
 
400
            if (error.getCode() != 175002 && // SVN_ERR_RA_DAV_REQUEST_FAILED
 
401
                error.getCode() != 165006)   // SVN_ERR_REPOS_DISABLED_FEATURE
 
402
                fail(error.getMessage());
 
403
 
 
404
            return;
 
405
        }
 
406
 
 
407
        byte[] check = client.revProperty(getTestRepoUrl(), "svn:author",
 
408
                                          Revision.getInstance(1));
 
409
        assertTrue(Arrays.equals(check, newValue));
 
410
    }
 
411
 
 
412
    public void testGetRevpropList() throws Exception
 
413
    {
 
414
        Charset UTF8 = Charset.forName("UTF-8");
 
415
        ISVNRemote session = getSession();
 
416
 
 
417
        Map<String, byte[]> proplist = session.getRevisionProperties(1);
 
418
        assertTrue(Arrays.equals(proplist.get("svn:author"),
 
419
                                 USERNAME.getBytes(UTF8)));
 
420
    }
 
421
 
 
422
    public void testGetRevprop() throws Exception
 
423
    {
 
424
        Charset UTF8 = Charset.forName("UTF-8");
 
425
        ISVNRemote session = getSession();
 
426
 
 
427
        byte[] propval = session.getRevisionProperty(1, "svn:author");
 
428
        assertTrue(Arrays.equals(propval, USERNAME.getBytes(UTF8)));
 
429
    }
 
430
 
 
431
    public void testGetFile() throws Exception
 
432
    {
 
433
        Charset UTF8 = Charset.forName("UTF-8");
 
434
        ISVNRemote session = getSession();
 
435
 
 
436
        ByteArrayOutputStream contents = new ByteArrayOutputStream();
 
437
        HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
 
438
        properties.put("fakename", "fakecontents".getBytes(UTF8));
 
439
        long fetched_rev =
 
440
            session.getFile(Revision.SVN_INVALID_REVNUM, "A/B/lambda",
 
441
                            contents, properties);
 
442
        assertEquals(fetched_rev, 1);
 
443
        assertEquals(contents.toString("UTF-8"),
 
444
                     "This is the file 'lambda'.");
 
445
        for (Map.Entry<String, byte[]> e : properties.entrySet()) {
 
446
            final String key = e.getKey();
 
447
            assertTrue(key.startsWith("svn:entry:")
 
448
                       || key.startsWith("svn:wc:"));
 
449
        }
 
450
    }
 
451
 
 
452
    public void testGetDirectory() throws Exception
 
453
    {
 
454
        Charset UTF8 = Charset.forName("UTF-8");
 
455
        ISVNRemote session = getSession();
 
456
 
 
457
        HashMap<String, DirEntry> dirents = new HashMap<String, DirEntry>();
 
458
        dirents.put("E", null);
 
459
        dirents.put("F", null);
 
460
        dirents.put("lambda", null);
 
461
        HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
 
462
        properties.put("fakename", "fakecontents".getBytes(UTF8));
 
463
        long fetched_rev =
 
464
            session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/B",
 
465
                                 DirEntry.Fields.all, dirents, properties);
 
466
        assertEquals(fetched_rev, 1);
 
467
        assertEquals(dirents.get("E").getPath(), "E");
 
468
        assertEquals(dirents.get("F").getPath(), "F");
 
469
        assertEquals(dirents.get("lambda").getPath(), "lambda");
 
470
        for (Map.Entry<String, byte[]> e : properties.entrySet()) {
 
471
            final String key = e.getKey();
 
472
            assertTrue(key.startsWith("svn:entry:")
 
473
                       || key.startsWith("svn:wc:"));
 
474
        }
 
475
    }
 
476
 
 
477
    private static final class CommitContext implements CommitCallback
 
478
    {
 
479
        public final ISVNEditor editor;
 
480
        public CommitContext(ISVNRemote session, String logstr,
 
481
                             ISVNEditor.ProvideBaseCallback getBase,
 
482
                             ISVNEditor.ProvidePropsCallback getProps,
 
483
                             ISVNEditor.GetNodeKindCallback getKind)
 
484
            throws ClientException
 
485
        {
 
486
            Charset UTF8 = Charset.forName("UTF-8");
 
487
            byte[] log = (logstr == null
 
488
                          ? new byte[0]
 
489
                          : logstr.getBytes(UTF8));
 
490
            HashMap<String, byte[]> revprops = new HashMap<String, byte[]>();
 
491
            revprops.put("svn:log", log);
 
492
 
 
493
            // Run the getCommitEditor overloads through their paces, too.
 
494
            if (getBase == null && getProps == null && getKind == null)
 
495
                editor = session.getCommitEditor(revprops, this, null, false);
 
496
            else
 
497
                editor = session.getCommitEditor(revprops, this, null, false,
 
498
                                                 getBase, getProps, getKind);
 
499
        }
 
500
 
 
501
        public CommitContext(ISVNRemote session, String logstr)
 
502
            throws ClientException
 
503
        {
 
504
            this(session, logstr, null, null, null);
 
505
        }
 
506
 
 
507
        public void commitInfo(CommitInfo info) { this.info = info; }
 
508
        public long getRevision() { return info.getRevision(); }
 
509
 
 
510
        private CommitInfo info;
 
511
    }
 
512
 
 
513
    private static final class EditorCallbacks
 
514
    {
 
515
        private final String wcpath;
 
516
        private final long revision;
 
517
        private final Map<String, byte[]> props;
 
518
        private final NodeKind kind;
 
519
 
 
520
        public EditorCallbacks(String wcpath, long revision,
 
521
                               Map<String, byte[]> props,
 
522
                               NodeKind kind)
 
523
        {
 
524
            this.wcpath = wcpath;
 
525
            this.revision = revision;
 
526
            this.props = props;
 
527
            this.kind = kind;
 
528
        }
 
529
 
 
530
        public final ISVNEditor.ProvideBaseCallback getBase =
 
531
            new ISVNEditor.ProvideBaseCallback()
 
532
            {
 
533
                public ISVNEditor.ProvideBaseCallback.ReturnValue
 
534
                getContents(String relpath)
 
535
                {
 
536
                    try {
 
537
                        return new ISVNEditor.ProvideBaseCallback.ReturnValue(
 
538
                            new FileInputStream(wcpath + relpath), revision);
 
539
                    } catch (java.io.FileNotFoundException e) {
 
540
                        throw new RuntimeException(e);
 
541
                    }
 
542
                }
 
543
            };
 
544
 
 
545
        public final ISVNEditor.ProvidePropsCallback getProps =
 
546
            new ISVNEditor.ProvidePropsCallback()
 
547
            {
 
548
                public ISVNEditor.ProvidePropsCallback.ReturnValue
 
549
                getProperties(String relpath)
 
550
                {
 
551
                    return new ISVNEditor.ProvidePropsCallback.ReturnValue(
 
552
                        props, revision);
 
553
                }
 
554
            };
 
555
 
 
556
        public final ISVNEditor.GetNodeKindCallback getKind =
 
557
            new ISVNEditor.GetNodeKindCallback()
 
558
            {
 
559
                public NodeKind getKind(String relpath, long revision)
 
560
                {
 
561
                    return kind;
 
562
                }
 
563
            };
 
564
    };
 
565
 
 
566
    private void testEditorCopy(EditorCallbacks cb) throws Exception
 
567
    {
 
568
        ISVNRemote session = getSession();
 
569
        CommitContext cc =
 
570
            (cb != null
 
571
             ? new CommitContext(session, "Copy A/B/lambda -> A/B/omega",
 
572
                                 cb.getBase, cb.getProps, cb.getKind)
 
573
             : new CommitContext(session, "Copy A/B/lambda -> A/B/omega"));
 
574
 
 
575
        try {
 
576
            // FIXME: alter dir A/B first
 
577
            cc.editor.copy("A/B/lambda", 1, "A/B/omega",
 
578
                           Revision.SVN_INVALID_REVNUM);
 
579
            cc.editor.complete();
 
580
        } finally {
 
581
            cc.editor.dispose();
 
582
        }
 
583
 
 
584
        assertEquals(2, cc.getRevision());
 
585
        assertEquals(2, session.getLatestRevision());
 
586
        assertEquals(NodeKind.file,
 
587
                     session.checkPath("A/B/lambda",
 
588
                                       Revision.SVN_INVALID_REVNUM));
 
589
        assertEquals(NodeKind.file,
 
590
                     session.checkPath("A/B/omega",
 
591
                                       Revision.SVN_INVALID_REVNUM));
 
592
    }
 
593
 
 
594
    public void testEditorCopy() throws Exception
 
595
    {
 
596
        testEditorCopy(null);
 
597
    }
 
598
 
 
599
    public void testEditorCopy_WithCallbacks() throws Exception
 
600
    {
 
601
        testEditorCopy(new EditorCallbacks(thisTest.getWCPath(), 1L,
 
602
                                           new HashMap<String, byte[]>(),
 
603
                                           NodeKind.file));
 
604
    }
 
605
 
 
606
    public void testEditorMove() throws Exception
 
607
    {
 
608
        ISVNRemote session = getSession();
 
609
        CommitContext cc =
 
610
            new CommitContext(session, "Move A/B/lambda -> A/B/omega");
 
611
 
 
612
        try {
 
613
            // FIXME: alter dir A/B first
 
614
            cc.editor.move("A/B/lambda", 1, "A/B/omega",
 
615
                           Revision.SVN_INVALID_REVNUM);
 
616
            cc.editor.complete();
 
617
        } finally {
 
618
            cc.editor.dispose();
 
619
        }
 
620
 
 
621
        assertEquals(2, cc.getRevision());
 
622
        assertEquals(2, session.getLatestRevision());
 
623
        assertEquals(NodeKind.none,
 
624
                     session.checkPath("A/B/lambda",
 
625
                                       Revision.SVN_INVALID_REVNUM));
 
626
        assertEquals(NodeKind.file,
 
627
                     session.checkPath("A/B/omega",
 
628
                                       Revision.SVN_INVALID_REVNUM));
 
629
    }
 
630
 
 
631
    public void testEditorDelete() throws Exception
 
632
    {
 
633
        ISVNRemote session = getSession();
 
634
        CommitContext cc =
 
635
            new CommitContext(session, "Delete all greek files");
 
636
 
 
637
        String[] filePaths = { "iota",
 
638
                               "A/mu",
 
639
                               "A/B/lambda",
 
640
                               "A/B/E/alpha",
 
641
                               "A/B/E/beta",
 
642
                               "A/D/gamma",
 
643
                               "A/D/G/pi",
 
644
                               "A/D/G/rho",
 
645
                               "A/D/G/tau",
 
646
                               "A/D/H/chi",
 
647
                               "A/D/H/omega",
 
648
                               "A/D/H/psi" };
 
649
 
 
650
        try {
 
651
            // FIXME: alter a bunch of dirs first
 
652
            for (String path : filePaths)
 
653
                cc.editor.delete(path, 1);
 
654
            cc.editor.complete();
 
655
        } finally {
 
656
            cc.editor.dispose();
 
657
        }
 
658
 
 
659
        assertEquals(2, cc.getRevision());
 
660
        assertEquals(2, session.getLatestRevision());
 
661
        for (String path : filePaths)
 
662
            assertEquals(NodeKind.none,
 
663
                         session.checkPath(path, Revision.SVN_INVALID_REVNUM));
 
664
    }
 
665
 
 
666
    public void testEditorMkdir() throws Exception
 
667
    {
 
668
        ISVNRemote session = getSession();
 
669
        CommitContext cc = new CommitContext(session, "Make hebrew dir");
 
670
 
 
671
        try {
 
672
            // FIXME: alter dir . first
 
673
            cc.editor.addDirectory("ALEPH",
 
674
                                   new ArrayList<String>(),
 
675
                                   new HashMap<String, byte[]>(),
 
676
                                   Revision.SVN_INVALID_REVNUM);
 
677
            cc.editor.complete();
 
678
        } finally {
 
679
            cc.editor.dispose();
 
680
        }
 
681
 
 
682
        assertEquals(2, cc.getRevision());
 
683
        assertEquals(2, session.getLatestRevision());
 
684
        assertEquals(NodeKind.dir,
 
685
                     session.checkPath("ALEPH",
 
686
                                       Revision.SVN_INVALID_REVNUM));
 
687
    }
 
688
 
 
689
    private void testEditorSetDirProps(EditorCallbacks cb) throws Exception
 
690
    {
 
691
        Charset UTF8 = Charset.forName("UTF-8");
 
692
        ISVNRemote session = getSession();
 
693
 
 
694
        byte[] ignoreval = "*.pyc\n.gitignore\n".getBytes(UTF8);
 
695
        byte[] binaryval = new byte[]{(byte)0, (byte)13, (byte)255, (byte)8,
 
696
                                      (byte)127, (byte)128, (byte)129};
 
697
        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
 
698
        props.put("svn:ignore", ignoreval);
 
699
        props.put("binaryprop", binaryval);
 
700
 
 
701
        CommitContext cc =
 
702
            (cb != null
 
703
             ? new CommitContext(session, "Add svn:ignore and binaryprop",
 
704
                                 cb.getBase, cb.getProps, cb.getKind)
 
705
             : new CommitContext(session, "Add svn:ignore and binaryprop"));
 
706
        try {
 
707
            cc.editor.alterDirectory("", 1, null, props);
 
708
            cc.editor.complete();
 
709
        } finally {
 
710
            cc.editor.dispose();
 
711
        }
 
712
 
 
713
        assertEquals(2, cc.getRevision());
 
714
        assertEquals(2, session.getLatestRevision());
 
715
        assertTrue(Arrays.equals(ignoreval,
 
716
                                 client.propertyGet(session.getSessionUrl(),
 
717
                                                    "svn:ignore",
 
718
                                                    Revision.HEAD,
 
719
                                                    Revision.HEAD)));
 
720
        assertTrue(Arrays.equals(binaryval,
 
721
                                 client.propertyGet(session.getSessionUrl(),
 
722
                                                    "binaryprop",
 
723
                                                    Revision.HEAD,
 
724
                                                    Revision.HEAD)));
 
725
    }
 
726
 
 
727
    public void testEditorSetDirProps() throws Exception
 
728
    {
 
729
        testEditorSetDirProps(null);
 
730
    }
 
731
 
 
732
    public void testEditorSetDirProps_WithCallbacks() throws Exception
 
733
    {
 
734
        testEditorSetDirProps(new EditorCallbacks(thisTest.getWCPath(), 1L,
 
735
                                                  new HashMap<String, byte[]>(),
 
736
                                                  NodeKind.dir));
 
737
    }
 
738
 
 
739
    private static byte[] SHA1(byte[] text) throws NoSuchAlgorithmException
 
740
    {
 
741
        MessageDigest md = MessageDigest.getInstance("SHA-1");
 
742
        return md.digest(text);
 
743
    }
 
744
 
 
745
    public void testEditorAddFile() throws Exception
 
746
    {
 
747
        Charset UTF8 = Charset.forName("UTF-8");
 
748
        ISVNRemote session = getSession();
 
749
 
 
750
        byte[] eolstyle = "native".getBytes(UTF8);
 
751
        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
 
752
        props.put("svn:eol-style", eolstyle);
 
753
 
 
754
        byte[] contents = "This is file 'xi'.".getBytes(UTF8);
 
755
        Checksum hash = new Checksum(SHA1(contents), Checksum.Kind.SHA1);
 
756
        ByteArrayInputStream stream = new ByteArrayInputStream(contents);
 
757
 
 
758
        CommitContext cc = new CommitContext(session, "Add A/xi");
 
759
        try {
 
760
            // FIXME: alter dir A first
 
761
            cc.editor.addFile("A/xi", hash, stream, props,
 
762
                              Revision.SVN_INVALID_REVNUM);
 
763
            cc.editor.complete();
 
764
        } finally {
 
765
            cc.editor.dispose();
 
766
        }
 
767
 
 
768
        assertEquals(2, cc.getRevision());
 
769
        assertEquals(2, session.getLatestRevision());
 
770
        assertEquals(NodeKind.file,
 
771
                     session.checkPath("A/xi",
 
772
                                       Revision.SVN_INVALID_REVNUM));
 
773
 
 
774
        byte[] propval = client.propertyGet(session.getSessionUrl() + "/A/xi",
 
775
                                            "svn:eol-style",
 
776
                                            Revision.HEAD,
 
777
                                            Revision.HEAD);
 
778
        assertTrue(Arrays.equals(eolstyle, propval));
 
779
    }
 
780
 
 
781
    public void testEditorDeleteFileProps() throws Exception
 
782
    {
 
783
        Charset UTF8 = Charset.forName("UTF-8");
 
784
        client.propertySetRemote(
 
785
             thisTest.getUrl() + "/iota", 1L,
 
786
             "name", "value".getBytes(UTF8),
 
787
             new CommitMessageCallback() {
 
788
                 public String getLogMessage(Set<CommitItem> elements) {
 
789
                     return "Set property 'name' to 'value'";
 
790
                 }
 
791
             }, false, null, null);
 
792
 
 
793
        ISVNRemote session = getSession();
 
794
        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
 
795
        assertEquals(2L, session.getFile(Revision.SVN_INVALID_REVNUM, "iota",
 
796
                                         null, props));
 
797
 
 
798
        int propcount = 0;
 
799
        for (Map.Entry<String, byte[]> e : props.entrySet()) {
 
800
            final String key = e.getKey();
 
801
            if (key.startsWith("svn:entry:") || key.startsWith("svn:wc:"))
 
802
                continue;
 
803
            ++propcount;
 
804
        }
 
805
        assertEquals(1, propcount);
 
806
 
 
807
        CommitContext cc = new CommitContext(session, "Remove all props");
 
808
        try {
 
809
            props.clear();
 
810
            cc.editor.alterFile("iota", 2L, null, null, props);
 
811
            cc.editor.complete();
 
812
        } finally {
 
813
            cc.editor.dispose();
 
814
        }
 
815
 
 
816
        assertEquals(3L, session.getFile(Revision.SVN_INVALID_REVNUM, "iota",
 
817
                                         null, props));
 
818
        propcount = 0;
 
819
        for (Map.Entry<String, byte[]> e : props.entrySet()) {
 
820
            final String key = e.getKey();
 
821
            if (key.startsWith("svn:entry:") || key.startsWith("svn:wc:"))
 
822
                continue;
 
823
            ++propcount;
 
824
        }
 
825
        assertEquals(0, propcount);
 
826
    }
 
827
 
 
828
    private void testEditorSetFileContents(EditorCallbacks cb) throws Exception
 
829
    {
 
830
        Charset UTF8 = Charset.forName("UTF-8");
 
831
        ISVNRemote session = getSession();
 
832
 
 
833
        byte[] contents = "This is modified file 'alpha'.".getBytes(UTF8);
 
834
        Checksum hash = new Checksum(SHA1(contents), Checksum.Kind.SHA1);
 
835
        ByteArrayInputStream stream = new ByteArrayInputStream(contents);
 
836
 
 
837
        CommitContext cc =
 
838
            (cb != null
 
839
             ? new CommitContext(session, "Change contents of A/B/E/alpha",
 
840
                                 cb.getBase, cb.getProps, cb.getKind)
 
841
             : new CommitContext(session, "Change contents of A/B/E/alpha"));
 
842
        try {
 
843
            cc.editor.alterFile("A/B/E/alpha", 1, hash, stream, null);
 
844
            cc.editor.complete();
 
845
        } finally {
 
846
            cc.editor.dispose();
 
847
        }
 
848
 
 
849
        assertEquals(2, cc.getRevision());
 
850
        assertEquals(2, session.getLatestRevision());
 
851
        ByteArrayOutputStream checkcontents = new ByteArrayOutputStream();
 
852
        client.streamFileContent(session.getSessionUrl() + "/A/B/E/alpha",
 
853
                                 Revision.HEAD, Revision.HEAD, checkcontents);
 
854
        assertTrue(Arrays.equals(contents, checkcontents.toByteArray()));
 
855
    }
 
856
 
 
857
    public void testEditorSetFileContents() throws Exception
 
858
    {
 
859
        testEditorSetFileContents(null);
 
860
    }
 
861
 
 
862
    public void testEditorSetFileContents_WithCallbacks() throws Exception
 
863
    {
 
864
        testEditorSetFileContents(new EditorCallbacks(thisTest.getWCPath(), 1L,
 
865
                                                      new HashMap<String, byte[]>(),
 
866
                                                      NodeKind.file));
 
867
    }
 
868
 
 
869
    // Sanity check so that we don't forget about unimplemented methods.
 
870
    public void testEditorNotImplemented() throws Exception
 
871
    {
 
872
        ISVNRemote session = getSession();
 
873
 
 
874
        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
 
875
        // ArrayList<ISVNEditor.RotatePair> rotation =
 
876
        //     new ArrayList<ISVNEditor.RotatePair>();
 
877
 
 
878
        CommitContext cc = new CommitContext(session, "not implemented");
 
879
        try {
 
880
            String exmsg;
 
881
 
 
882
            try {
 
883
                exmsg = "";
 
884
                cc.editor.addSymlink("", "", props, 1);
 
885
            } catch (RuntimeException ex) {
 
886
                exmsg = ex.getMessage();
 
887
            }
 
888
            assertEquals("Not implemented: CommitEditor.addSymlink", exmsg);
 
889
 
 
890
            try {
 
891
                exmsg = "";
 
892
                cc.editor.alterSymlink("", 1, "", null);
 
893
            } catch (RuntimeException ex) {
 
894
                exmsg = ex.getMessage();
 
895
            }
 
896
            assertEquals("Not implemented: CommitEditor.alterSymlink", exmsg);
 
897
 
 
898
        } finally {
 
899
            cc.editor.dispose();
 
900
        }
 
901
    }
 
902
 
 
903
    private static final class LogMsg
 
904
    {
 
905
        public Set<ChangePath> changedPaths;
 
906
        public long revision;
 
907
        public Map<String, byte[]> revprops;
 
908
        public boolean hasChildren;
 
909
    }
 
910
 
 
911
    private static final class LogReceiver implements LogMessageCallback
 
912
    {
 
913
        public final ArrayList<LogMsg> logs = new ArrayList<LogMsg>();
 
914
 
 
915
        public void singleMessage(Set<ChangePath> changedPaths,
 
916
                                  long revision,
 
917
                                  Map<String, byte[]> revprops,
 
918
                                  boolean hasChildren)
 
919
        {
 
920
            LogMsg msg = new LogMsg();
 
921
            msg.changedPaths = changedPaths;
 
922
            msg.revision = revision;
 
923
            msg.revprops = revprops;
 
924
            msg.hasChildren = hasChildren;
 
925
            logs.add(msg);
 
926
        }
 
927
    }
 
928
 
 
929
    public void testGetLog() throws Exception
 
930
    {
 
931
        ISVNRemote session = getSession();
 
932
        LogReceiver receiver = new LogReceiver();
 
933
 
 
934
        session.getLog(null,
 
935
                       Revision.SVN_INVALID_REVNUM,
 
936
                       Revision.SVN_INVALID_REVNUM,
 
937
                       0, false, false, false, null,
 
938
                       receiver);
 
939
        assertEquals(1, receiver.logs.size());
 
940
        assertTrue(receiver.logs.get(0).revprops.size() > 0);
 
941
 
 
942
        receiver.logs.clear();
 
943
        session.reparent(getTestRepoUrl() + "/A");
 
944
        session.getLog(null,
 
945
                       Revision.SVN_INVALID_REVNUM,
 
946
                       0, 0, false, false, false, null,
 
947
                       receiver);
 
948
        assertEquals(2, receiver.logs.size());
 
949
        assertTrue(receiver.logs.get(0).revprops.size() > 0);
 
950
    }
 
951
 
 
952
    public void testGetLogMissing() throws Exception
 
953
    {
 
954
        ISVNRemote session = getSession();
 
955
        LogReceiver receiver = new LogReceiver();
 
956
 
 
957
        ArrayList<String> paths = new ArrayList<String>(1);
 
958
        paths.add("X");
 
959
 
 
960
        boolean exception = false;
 
961
        try {
 
962
            session.getLog(paths,
 
963
                           Revision.SVN_INVALID_REVNUM,
 
964
                           Revision.SVN_INVALID_REVNUM,
 
965
                           0, false, false, false, null,
 
966
                           receiver);
 
967
        } catch (ClientException ex) {
 
968
            assertEquals("Filesystem has no item",
 
969
                         ex.getAllMessages().get(0).getMessage());
 
970
            exception = true;
 
971
        }
 
972
 
 
973
        assertEquals(0, receiver.logs.size());
 
974
        assertTrue(exception);
 
975
    }
 
976
 
 
977
    public void testConfigHandler() throws Exception
 
978
    {
 
979
        ConfigEvent handler = new ConfigEvent()
 
980
            {
 
981
                public void onLoad(ISVNConfig cfg)
 
982
                {
 
983
                    //System.out.println("config:");
 
984
                    onecat(cfg.config());
 
985
                    //System.out.println("servers:");
 
986
                    onecat(cfg.servers());
 
987
                }
 
988
 
 
989
                private void onecat(ISVNConfig.Category cat)
 
990
                {
 
991
                    for (String sec : cat.sections()) {
 
992
                        //System.out.println("  [" + sec + "]");
 
993
                        ISVNConfig.Enumerator en = new ISVNConfig.Enumerator()
 
994
                            {
 
995
                                public void option(String name, String value)
 
996
                                {
 
997
                                    //System.out.println("    " + name
 
998
                                    //                   + " = " + value);
 
999
                                }
 
1000
                            };
 
1001
                        cat.enumerate(sec, en);
 
1002
                    }
 
1003
                }
 
1004
 
 
1005
            };
 
1006
 
 
1007
        ISVNRemote session;
 
1008
        try
 
1009
        {
 
1010
            if (DefaultAuthn.useDeprecated())
 
1011
                session = new RemoteFactory(
 
1012
                    super.conf.getAbsolutePath(),
 
1013
                    USERNAME, null, // Do not set default password.
 
1014
                    DefaultAuthn.getDeprecated(),
 
1015
                    null, handler, null)
 
1016
                    .openRemoteSession(getTestRepoUrl());
 
1017
            else
 
1018
                session = new RemoteFactory(
 
1019
                    super.conf.getAbsolutePath(),
 
1020
                    USERNAME, null, // Do not set default password.
 
1021
                    DefaultAuthn.getDefault(),
 
1022
                    null, handler, null)
 
1023
                    .openRemoteSession(getTestRepoUrl());
 
1024
        }
 
1025
        catch (ClientException ex)
 
1026
        {
 
1027
            throw new RuntimeException(ex);
 
1028
        }
 
1029
        session.getLatestRevision();
 
1030
    }
 
1031
 
 
1032
    private static class RemoteStatusReceiver implements RemoteStatus
 
1033
    {
 
1034
        static class StatInfo implements Comparable<StatInfo>
 
1035
        {
 
1036
            public String relpath = null;
 
1037
            public char kind = ' '; // F, D, L
 
1038
            public boolean textChanged = false;
 
1039
            public boolean propsChanged = false;
 
1040
            public boolean deleted = false;
 
1041
            public Entry info = null;
 
1042
 
 
1043
            StatInfo(String relpath, char kind, boolean added)
 
1044
            {
 
1045
                this.relpath = relpath;
 
1046
                this.kind = kind;
 
1047
                this.deleted = !added;
 
1048
            }
 
1049
 
 
1050
            StatInfo(String relpath, char kind,
 
1051
                     boolean textChanged, boolean propsChanged,
 
1052
                     Entry info)
 
1053
            {
 
1054
                this.relpath = relpath;
 
1055
                this.kind = kind;
 
1056
                this.textChanged = textChanged;
 
1057
                this.propsChanged = propsChanged;
 
1058
                this.info = info;
 
1059
            }
 
1060
 
 
1061
            @Override
 
1062
            public boolean equals(Object statinfo)
 
1063
            {
 
1064
                final StatInfo that = (StatInfo)statinfo;
 
1065
                return this.relpath.equals(that.relpath);
 
1066
            }
 
1067
 
 
1068
            @Override
 
1069
            public int hashCode()
 
1070
            {
 
1071
                return this.relpath.hashCode();
 
1072
            }
 
1073
 
 
1074
            @Override
 
1075
            public int compareTo(StatInfo that)
 
1076
            {
 
1077
                return this.relpath.compareTo(that.relpath);
 
1078
            }
 
1079
        }
 
1080
 
 
1081
        private boolean debug;
 
1082
 
 
1083
        public RemoteStatusReceiver()
 
1084
        {
 
1085
            this.debug = false;
 
1086
        }
 
1087
 
 
1088
        public RemoteStatusReceiver(boolean debug)
 
1089
        {
 
1090
            this.debug = debug;
 
1091
        }
 
1092
 
 
1093
        public ArrayList<StatInfo> status = new ArrayList<StatInfo>();
 
1094
 
 
1095
        public void addedDirectory(String relativePath)
 
1096
        {
 
1097
            if (debug)
 
1098
                System.err.println("RemoteStatus:  A   (dir)  " +
 
1099
                                   relativePath);
 
1100
            status.add(new StatInfo(relativePath, 'D', true));
 
1101
        }
 
1102
 
 
1103
        public void addedFile(String relativePath)
 
1104
        {
 
1105
            if (debug)
 
1106
                System.err.println("RemoteStatus:  A   (file) "
 
1107
                                   + relativePath);
 
1108
            status.add(new StatInfo(relativePath, 'F', true));
 
1109
        }
 
1110
 
 
1111
        public void addedSymlink(String relativePath)
 
1112
        {
 
1113
            if (debug)
 
1114
                System.err.println("RemoteStatus:  A   (link) "
 
1115
                                   + relativePath);
 
1116
            status.add(new StatInfo(relativePath, 'L', true));
 
1117
        }
 
1118
 
 
1119
        public void modifiedDirectory(String relativePath,
 
1120
                                      boolean childrenModified,
 
1121
                                      boolean propsModified,
 
1122
                                      Entry nodeInfo)
 
1123
        {
 
1124
            if (debug)
 
1125
                System.err.println("RemoteStatus:  " +
 
1126
                                   (childrenModified ? 'M' : '_') +
 
1127
                                   (propsModified ? 'M' : '_') +
 
1128
                                   "  (dir)  " + relativePath);
 
1129
            status.add(new StatInfo(relativePath, 'D',
 
1130
                                    childrenModified, propsModified,
 
1131
                                    nodeInfo));
 
1132
        }
 
1133
 
 
1134
        public void modifiedFile(String relativePath,
 
1135
                                 boolean textModified,
 
1136
                                 boolean propsModified,
 
1137
                                 Entry nodeInfo)
 
1138
        {
 
1139
            if (debug)
 
1140
                System.err.println("RemoteStatus:  " +
 
1141
                                   (textModified ? 'M' : '_') +
 
1142
                                   (propsModified ? 'M' : '_') +
 
1143
                                   "  (file) " + relativePath);
 
1144
            status.add(new StatInfo(relativePath, 'F',
 
1145
                                    textModified, propsModified,
 
1146
                                    nodeInfo));
 
1147
        }
 
1148
 
 
1149
        public void modifiedSymlink(String relativePath,
 
1150
                                    boolean targetModified,
 
1151
                                    boolean propsModified,
 
1152
                                    Entry nodeInfo)
 
1153
        {
 
1154
            if (debug)
 
1155
                System.err.println("RemoteStatus:  " +
 
1156
                                   (targetModified ? 'M' : '_') +
 
1157
                                   (propsModified ? 'M' : '_') +
 
1158
                                   "  (link) " + relativePath);
 
1159
            status.add(new StatInfo(relativePath, 'L',
 
1160
                                    targetModified, propsModified,
 
1161
                                    nodeInfo));
 
1162
 
 
1163
        }
 
1164
 
 
1165
        public void deleted(String relativePath)
 
1166
        {
 
1167
            if (debug)
 
1168
                System.err.println("RemoteStatus:  D          "
 
1169
                                   + relativePath);
 
1170
            status.add(new StatInfo(relativePath, ' ', false));
 
1171
        }
 
1172
    }
 
1173
 
 
1174
    public void testSimpleStatus() throws Exception
 
1175
    {
 
1176
        ISVNRemote session = getSession();
 
1177
 
 
1178
        RemoteStatusReceiver receiver = new RemoteStatusReceiver();
 
1179
        ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
 
1180
                                         Depth.infinity, receiver);
 
1181
        try {
 
1182
            rp.setPath("", 0, Depth.infinity, true, null);
 
1183
            assertEquals(1, rp.finishReport());
 
1184
        } finally {
 
1185
            rp.dispose();
 
1186
        }
 
1187
        assertEquals(21, receiver.status.size());
 
1188
        session.checkPath("", Revision.SVN_INVALID_REVNUM);
 
1189
    }
 
1190
 
 
1191
    public void testTextchangeStatus() throws Exception
 
1192
    {
 
1193
        ISVNRemote session = getSession();
 
1194
 
 
1195
        CommitMessageCallback cmcb = new CommitMessageCallback() {
 
1196
                public String getLogMessage(Set<CommitItem> x) {
 
1197
                    return "Content change on A/B/E/alpha";
 
1198
                }
 
1199
            };
 
1200
 
 
1201
        File alpha = new File(thisTest.getWorkingCopy(), "A/B/E/alpha");
 
1202
        FileOutputStream writer = new FileOutputStream(alpha);
 
1203
        writer.write("changed alpha text".getBytes());
 
1204
        writer.close();
 
1205
        client.commit(thisTest.getWCPathSet(), Depth.infinity, false, false,
 
1206
                      null, null, cmcb, null);
 
1207
 
 
1208
        RemoteStatusReceiver receiver = new RemoteStatusReceiver();
 
1209
        ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
 
1210
                                         Depth.infinity, receiver);
 
1211
        try {
 
1212
            rp.setPath("", 1, Depth.infinity, false, null);
 
1213
            assertEquals(2, rp.finishReport());
 
1214
        } finally {
 
1215
            rp.dispose();
 
1216
        }
 
1217
 
 
1218
        assertEquals(5, receiver.status.size());
 
1219
 
 
1220
        // ra_serf returns the entries in inverted order compared to ra_local.
 
1221
        Collections.sort(receiver.status);
 
1222
        RemoteStatusReceiver.StatInfo mod = receiver.status.get(4);
 
1223
        assertEquals("A/B/E/alpha", mod.relpath);
 
1224
        assertEquals('F', mod.kind);
 
1225
        assertEquals("Text Changed", true, mod.textChanged);
 
1226
        assertEquals("Props Changed", false, mod.propsChanged);
 
1227
        assertEquals("Node Deleted", false, mod.deleted);
 
1228
        assertEquals(2, mod.info.getCommittedRevision());
 
1229
    }
 
1230
 
 
1231
    public void testPropchangeStatus() throws Exception
 
1232
    {
 
1233
        ISVNRemote session = getSession();
 
1234
 
 
1235
        CommitMessageCallback cmcb = new CommitMessageCallback() {
 
1236
                public String getLogMessage(Set<CommitItem> x) {
 
1237
                    return "Property change on A/D/gamma";
 
1238
                }
 
1239
            };
 
1240
        client.propertySetRemote(getTestRepoUrl() + "/A/D/gamma",
 
1241
                                 1L, "foo", "bar".getBytes(), cmcb,
 
1242
                                 false, null, null);
 
1243
 
 
1244
        RemoteStatusReceiver receiver = new RemoteStatusReceiver();
 
1245
        ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
 
1246
                                         Depth.infinity, receiver);
 
1247
        try {
 
1248
            rp.setPath("", 1, Depth.infinity, false, null);
 
1249
            assertEquals(2, rp.finishReport());
 
1250
        } finally {
 
1251
            rp.dispose();
 
1252
        }
 
1253
 
 
1254
        assertEquals(4, receiver.status.size());
 
1255
 
 
1256
        // ra_serf returns the entries in inverted order compared to ra_local.
 
1257
        Collections.sort(receiver.status);
 
1258
        RemoteStatusReceiver.StatInfo mod = receiver.status.get(3);
 
1259
        assertEquals("A/D/gamma", mod.relpath);
 
1260
        assertEquals('F', mod.kind);
 
1261
        assertEquals("TextChanged", false, mod.textChanged);
 
1262
        assertEquals("Props Changed", true, mod.propsChanged);
 
1263
        assertEquals("Node Deleted", false, mod.deleted);
 
1264
        assertEquals(2, mod.info.getCommittedRevision());
 
1265
    }
 
1266
 
 
1267
    public void testDeletedStatus() throws Exception
 
1268
    {
 
1269
        ISVNRemote session = getSession();
 
1270
 
 
1271
        CommitMessageCallback cmcb = new CommitMessageCallback() {
 
1272
                public String getLogMessage(Set<CommitItem> x) {
 
1273
                    return "Delete A/mu";
 
1274
                }
 
1275
            };
 
1276
        HashSet<String> paths = new HashSet<String>(1);
 
1277
        paths.add(getTestRepoUrl() + "/A/mu");
 
1278
        client.remove(paths, false, false, null, cmcb, null);
 
1279
 
 
1280
        RemoteStatusReceiver receiver = new RemoteStatusReceiver();
 
1281
        ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
 
1282
                                         Depth.infinity, receiver);
 
1283
        try {
 
1284
            rp.setPath("", 1, Depth.infinity, false, null);
 
1285
            assertEquals(2, rp.finishReport());
 
1286
        } finally {
 
1287
            rp.dispose();
 
1288
        }
 
1289
        assertEquals(3, receiver.status.size());
 
1290
 
 
1291
        // ra_serf returns the entries in inverted order compared to ra_local.
 
1292
        Collections.sort(receiver.status);
 
1293
        RemoteStatusReceiver.StatInfo mod = receiver.status.get(2);
 
1294
        assertEquals("A/mu", mod.relpath);
 
1295
        assertEquals(' ', mod.kind);
 
1296
        assertEquals(false, mod.textChanged);
 
1297
        assertEquals(false, mod.propsChanged);
 
1298
        assertEquals(true, mod.deleted);
 
1299
    }
 
1300
 
 
1301
    public void testTrivialMergeinfo() throws Exception
 
1302
    {
 
1303
        ISVNRemote session = getSession();
 
1304
        ArrayList<String> paths = new ArrayList<String>(1);
 
1305
        paths.add("");
 
1306
 
 
1307
        Map<String, Mergeinfo> catalog =
 
1308
            session.getMergeinfo(paths, 1L, Mergeinfo.Inheritance.explicit,
 
1309
                                 false);
 
1310
        assertEquals(null, catalog);
 
1311
    }
 
1312
 
 
1313
    public void testBranchMergeinfo() throws Exception
 
1314
    {
 
1315
        CommitMessageCallback cmcb = new CommitMessageCallback() {
 
1316
                public String getLogMessage(Set<CommitItem> x) {
 
1317
                    return "testBranchMergeinfo";
 
1318
                }
 
1319
            };
 
1320
 
 
1321
        ISVNRemote session = getSession();
 
1322
 
 
1323
        // Create a branch
 
1324
        ArrayList<CopySource> dirA = new ArrayList<CopySource>(1);
 
1325
        dirA.add(new CopySource(getTestRepoUrl() + "/A",
 
1326
                                Revision.HEAD, Revision.HEAD));
 
1327
        client.copy(dirA, getTestRepoUrl() + "/Abranch",
 
1328
                    false, false, true, null, cmcb, null);
 
1329
 
 
1330
        // Check mergeinfo on new branch
 
1331
        ArrayList<String> paths = new ArrayList<String>(1);
 
1332
        paths.add("Abranch");
 
1333
        Map<String, Mergeinfo> catalog =
 
1334
            session.getMergeinfo(paths, 2L, Mergeinfo.Inheritance.explicit,
 
1335
                                 false);
 
1336
        assertEquals(null, catalog);
 
1337
 
 
1338
        // Modify source and merge to branch
 
1339
        client.propertySetRemote(getTestRepoUrl() + "/A/D/gamma",
 
1340
                                 2L, "foo", "bar".getBytes(), cmcb,
 
1341
                                 false, null, null);
 
1342
        client.update(thisTest.getWCPathSet(), Revision.HEAD, Depth.infinity,
 
1343
                      false, false, true, false);
 
1344
        client.merge(getTestRepoUrl() + "/A", Revision.HEAD, null,
 
1345
                     thisTest.getWCPath() + "/Abranch", false, Depth.infinity,
 
1346
                     false, false, false, false);
 
1347
        client.commit(thisTest.getWCPathSet(), Depth.infinity, false, false,
 
1348
                      null, null, cmcb, null);
 
1349
 
 
1350
        // Check inherited mergeinfo on updated branch
 
1351
        paths.set(0, "Abranch/mu");
 
1352
        catalog = session.getMergeinfo(paths, 4L,
 
1353
                                       Mergeinfo.Inheritance.nearest_ancestor,
 
1354
                                       false);
 
1355
        assertEquals(1, catalog.size());
 
1356
        List<RevisionRange> ranges =
 
1357
            catalog.get("Abranch/mu").getRevisions("/A/mu");
 
1358
        assertEquals(1, ranges.size());
 
1359
        assertEquals("1-3", ranges.get(0).toString());
 
1360
    }
 
1361
 
 
1362
    public void testGetLocations() throws Exception
 
1363
    {
 
1364
        ISVNRemote session = getSession();
 
1365
 
 
1366
        Long expected = new Long(1L);
 
1367
        ArrayList<Long> revs = new ArrayList<Long>(3);
 
1368
        revs.add(new Long(0L));
 
1369
        revs.add(expected);
 
1370
 
 
1371
        Map<Long, String> locs = session.getLocations("A", 1, revs);
 
1372
 
 
1373
        assertEquals(1, locs.size());
 
1374
        assertTrue(locs.containsKey(expected));
 
1375
        assertEquals("/A", locs.get(expected));
 
1376
    }
 
1377
 
 
1378
    public void testGetLocationSegments() throws Exception
 
1379
    {
 
1380
        ISVNRemote session = getSession();
 
1381
 
 
1382
        List<ISVNRemote.LocationSegment> result =
 
1383
            session.getLocationSegments("A", 1,
 
1384
                                        Revision.SVN_INVALID_REVNUM,
 
1385
                                        Revision.SVN_INVALID_REVNUM);
 
1386
        assertEquals(1, result.size());
 
1387
        ISVNRemote.LocationSegment seg = result.get(0);
 
1388
        assertEquals("A", seg.getPath());
 
1389
        assertEquals(1, seg.getStartRevision());
 
1390
        assertEquals(1, seg.getEndRevision());
 
1391
    }
 
1392
 
 
1393
    public void testGetFileRevisions() throws Exception
 
1394
    {
 
1395
        ISVNRemote session = getSession();
 
1396
 
 
1397
        List<ISVNRemote.FileRevision> result =
 
1398
            session.getFileRevisions("iota", 0, 1, true);
 
1399
        assertEquals(1, result.size());
 
1400
        ISVNRemote.FileRevision rev = result.get(0);
 
1401
        assertEquals("/iota", rev.getPath());
 
1402
        assertFalse(rev.isResultOfMerge());
 
1403
        assertTrue(rev.hasTextDelta());
 
1404
    }
 
1405
 
 
1406
    // This test is a result of a threading bug that was identified in
 
1407
    // serf-1.3.2 and earlier. The net result was that opening two RA
 
1408
    // sessions to an https:// URL in two parallel threads would cause
 
1409
    // a crash in serf, due to the OpenSSL library not being
 
1410
    // initialized in a single-threaded context.
 
1411
    //
 
1412
    // The problem does not appear to exist with other RA methods, but
 
1413
    // the test is here just in case someone is actually pedantic
 
1414
    // enough to test JavaHL with an HTTPS setup.
 
1415
    public void testParallelOpen() throws Exception
 
1416
    {
 
1417
        final Runnable runnable = new Runnable() {
 
1418
                @Override
 
1419
                public void run() {
 
1420
                    ISVNRemote session = null;
 
1421
                    try {
 
1422
                        session = getSession();
 
1423
                        assertEquals(1, session.getLatestRevision());
 
1424
                    }
 
1425
                    catch (ClientException ex) {
 
1426
                        throw new RuntimeException(ex);
 
1427
                    }
 
1428
                    finally {
 
1429
                        if (session != null)
 
1430
                            session.dispose();
 
1431
                    }
 
1432
                }
 
1433
            };
 
1434
 
 
1435
        Thread thread1 = new Thread(runnable);
 
1436
        Thread thread2 = new Thread(runnable);
 
1437
 
 
1438
        thread1.start();
 
1439
        thread2.start();
 
1440
 
 
1441
        thread1.join();
 
1442
        thread2.join();
 
1443
    }
 
1444
}