001package org.apache.maven.wagon;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.log4j.Logger;
023import org.apache.maven.wagon.authentication.AuthenticationException;
024import org.apache.maven.wagon.authentication.AuthenticationInfo;
025import org.apache.maven.wagon.authorization.AuthorizationException;
026import org.apache.maven.wagon.events.TransferEvent;
027import org.apache.maven.wagon.events.TransferListener;
028import org.apache.maven.wagon.observers.ChecksumObserver;
029import org.apache.maven.wagon.observers.Debug;
030import org.apache.maven.wagon.repository.Repository;
031import org.apache.maven.wagon.repository.RepositoryPermissions;
032import org.apache.maven.wagon.resource.Resource;
033import org.codehaus.plexus.PlexusTestCase;
034import org.codehaus.plexus.util.FileUtils;
035import org.easymock.AbstractMatcher;
036import org.easymock.MockControl;
037
038import java.io.File;
039import java.io.IOException;
040import java.security.NoSuchAlgorithmException;
041import java.text.SimpleDateFormat;
042import java.util.ArrayList;
043import java.util.Iterator;
044import java.util.List;
045
046/**
047 * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
048 */
049public abstract class WagonTestCase
050    extends PlexusTestCase
051{
052    protected static Logger logger = Logger.getLogger( WagonTestCase.class );
053
054
055    static final class ProgressArgumentMatcher
056        extends AbstractMatcher
057    {
058        private int size;
059
060        protected boolean argumentMatches( Object expected, Object actual )
061        {
062            if ( actual instanceof byte[] )
063            {
064                return true;
065            }
066            if ( actual instanceof Integer )
067            {
068                size += ( (Integer) actual ).intValue();
069                return true;
070            }
071            return super.argumentMatches( expected, actual );
072        }
073
074        public int getSize()
075        {
076            return size;
077        }
078    }
079
080    protected static String POM = "pom.xml";
081
082    protected Repository localRepository;
083
084    protected Repository testRepository;
085
086    protected String localRepositoryPath;
087
088    protected File sourceFile;
089
090    protected File destFile;
091
092    protected String resource;
093
094    protected File artifactSourceFile;
095
096    protected File artifactDestFile;
097
098    protected ChecksumObserver checksumObserver;
099
100    protected TransferListener mockTransferListener;
101
102    protected MockControl mockTransferListenerControl;
103
104    // ----------------------------------------------------------------------
105    // Constructors
106    // ----------------------------------------------------------------------
107
108    protected void setUp()
109        throws Exception
110    {
111        checksumObserver = new ChecksumObserver();
112
113        mockTransferListenerControl = MockControl.createControl( TransferListener.class );
114        mockTransferListener = (TransferListener) mockTransferListenerControl.getMock();
115
116        super.setUp();
117    }
118
119    // ----------------------------------------------------------------------
120    // Methods that should be provided by subclasses for proper testing
121    // ----------------------------------------------------------------------
122
123    /**
124     * URL of the repository. For a complete test it should point to a non existing folder so we also check for the
125     * creation of new folders in the remote site. <p/> return the URL of the repository as specified by Wagon syntax
126     */
127    protected abstract String getTestRepositoryUrl()
128        throws IOException;
129
130    /**
131     * Protocol id of the Wagon to use, eg. <code>scp</code>, <code>ftp</code>
132     *
133     * @return the protocol id
134     */
135    protected abstract String getProtocol();
136
137    /**
138     * The number of the port which should get used to start the test server
139     *
140     * @return the port number for the test server
141     */
142    protected abstract int getTestRepositoryPort();
143
144    // ----------------------------------------------------------------------
145    // 1. Create a local file repository which mimic a users local file
146    // Repository.
147    //
148    // 2. Create a test repository for the type of wagon we are testing. So,
149    // for example, for testing the file wagon we might have a test
150    // repository url of file://${basedir}/target/file-repository.
151    // ----------------------------------------------------------------------
152
153    protected void setupRepositories()
154        throws Exception
155    {
156        resource = "test-resource";
157
158        // ----------------------------------------------------------------------
159        // Create the test repository for the wagon we are testing.
160        // ----------------------------------------------------------------------
161
162        testRepository = new Repository();
163
164        testRepository.setUrl( getTestRepositoryUrl() );
165
166        testRepository.setPermissions( getPermissions() );
167
168        // ----------------------------------------------------------------------
169        // Create a test local repository.
170        // ----------------------------------------------------------------------
171
172        localRepositoryPath = FileTestUtils.createDir( "local-repository" ).getPath();
173
174        localRepository = createFileRepository( "file://" + localRepositoryPath );
175
176        message( "Local repository: " + localRepository );
177
178        File f = new File( localRepositoryPath );
179
180        if ( !f.exists() )
181        {
182            f.mkdirs();
183        }
184    }
185
186    protected void customizeContext()
187        throws Exception
188    {
189        getContainer().addContextValue( "test.repository", localRepositoryPath );
190    }
191
192    protected void setupWagonTestingFixtures()
193        throws Exception
194    {
195    }
196
197    protected void tearDownWagonTestingFixtures()
198        throws Exception
199    {
200    }
201
202    // ----------------------------------------------------------------------
203    //
204    // ----------------------------------------------------------------------
205
206    protected AuthenticationInfo getAuthInfo()
207    {
208        return new AuthenticationInfo();
209    }
210
211    protected RepositoryPermissions getPermissions()
212    {
213        return new RepositoryPermissions();
214    }
215
216    protected Wagon getWagon()
217        throws Exception
218    {
219        Wagon wagon = (Wagon) lookup( Wagon.ROLE, getProtocol() );
220
221        Debug debug = new Debug();
222
223        wagon.addSessionListener( debug );
224
225        wagon.addTransferListener( debug );
226
227        return wagon;
228    }
229
230    protected void message( String message )
231    {
232        logger.info( message );
233    }
234
235    // ----------------------------------------------------------------------
236    //
237    // ----------------------------------------------------------------------
238
239    public void testWagon()
240        throws Exception
241    {
242        if ( supportsGetIfNewer() )
243        {
244            setupRepositories();
245
246            setupWagonTestingFixtures();
247
248            fileRoundTripTesting();
249
250            tearDownWagonTestingFixtures();
251        }
252    }
253
254    public void testWagonGetIfNewerIsNewer()
255        throws Exception
256    {
257        if ( supportsGetIfNewer() )
258        {
259            setupRepositories();
260            setupWagonTestingFixtures();
261            int expectedSize = putFile();
262            getIfNewer( getExpectedLastModifiedOnGet( testRepository, new Resource( resource ) ) + 30000, false,
263                        expectedSize );
264        }
265    }
266
267    protected boolean supportsGetIfNewer()
268    {
269        return true;
270    }
271
272
273    public void testWagonGetIfNewerIsSame()
274        throws Exception
275    {
276        if ( supportsGetIfNewer() )
277        {
278            setupRepositories();
279            setupWagonTestingFixtures();
280            int expectedSize = putFile();
281            getIfNewer( getExpectedLastModifiedOnGet( testRepository, new Resource( resource ) ), false, expectedSize );
282        }
283    }
284
285    public void testWagonGetIfNewerIsOlder()
286        throws Exception
287    {
288        if ( supportsGetIfNewer() )
289        {
290            setupRepositories();
291            setupWagonTestingFixtures();
292            int expectedSize = putFile();
293            getIfNewer( new SimpleDateFormat( "yyyy-MM-dd" ).parse( "2006-01-01" ).getTime(), true, expectedSize );
294        }
295    }
296
297    private void getIfNewer( long timestamp, boolean expectedResult, int expectedSize )
298        throws Exception
299    {
300        Wagon wagon = getWagon();
301
302        ProgressArgumentMatcher progressArgumentMatcher = setupGetIfNewerTest( wagon, expectedResult, expectedSize );
303
304        connectWagon( wagon );
305
306        boolean result = wagon.getIfNewer( this.resource, destFile, timestamp );
307        assertEquals( expectedResult, result );
308
309        disconnectWagon( wagon );
310
311        assertGetIfNewerTest( progressArgumentMatcher, expectedResult, expectedSize );
312
313        tearDownWagonTestingFixtures();
314    }
315
316    protected ProgressArgumentMatcher setupGetIfNewerTest( Wagon wagon, boolean expectedResult, int expectedSize )
317        throws NoSuchAlgorithmException, IOException
318    {
319        checksumObserver = new ChecksumObserver();
320
321        destFile = FileTestUtils.createUniqueFile( getName(), getName() );
322        destFile.delete();
323        assertFalse( destFile.exists() );
324        destFile.deleteOnExit();
325
326        ProgressArgumentMatcher progressArgumentMatcher = null;
327        if ( expectedResult )
328        {
329            progressArgumentMatcher = replaceMockForGet( wagon, expectedSize );
330        }
331        else
332        {
333            replaceMockForSkippedGetIfNewer( wagon, expectedSize );
334        }
335        return progressArgumentMatcher;
336    }
337
338    protected void assertGetIfNewerTest( ProgressArgumentMatcher progressArgumentMatcher, boolean expectedResult,
339                                         int expectedSize )
340        throws IOException
341    {
342        if ( expectedResult )
343        {
344            verifyMock( progressArgumentMatcher, expectedSize );
345
346            assertNotNull( "check checksum is not null", checksumObserver.getActualChecksum() );
347
348            assertEquals( "compare checksums", "6b144b7285ffd6b0bc8300da162120b9",
349                          checksumObserver.getActualChecksum() );
350
351            // Now compare the contents of the artifact that was placed in
352            // the repository with the contents of the artifact that was
353            // retrieved from the repository.
354
355            String sourceContent = FileUtils.fileRead( sourceFile );
356            String destContent = FileUtils.fileRead( destFile );
357            assertEquals( sourceContent, destContent );
358        }
359        else
360        {
361            mockTransferListenerControl.verify();
362
363            mockTransferListenerControl.reset();
364
365            assertNull( "check checksum is null", checksumObserver.getActualChecksum() );
366
367            assertFalse( destFile.exists() );
368        }
369    }
370
371
372    private void replaceMockForSkippedGetIfNewer( Wagon wagon, int expectedSize )
373    {
374        Resource resource = new Resource( this.resource );
375        mockTransferListener.transferInitiated(
376            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_INITIATED, TransferEvent.REQUEST_GET,
377                                 destFile ) );
378        resource = new Resource( this.resource );
379        resource.setContentLength( getExpectedContentLengthOnGet( expectedSize ) );
380        resource.setLastModified( getExpectedLastModifiedOnGet( testRepository, resource ) );
381        // TODO: transfer skipped event?
382        // mockTransferListener.transferSkipped( createTransferEvent( wagon, resource, TransferEvent.TRANSFER_STARTED,
383        // TransferEvent.REQUEST_GET, destFile ) );
384
385        mockTransferListener.debug( null );
386        mockTransferListenerControl.setMatcher( MockControl.ALWAYS_MATCHER );
387        mockTransferListenerControl.setVoidCallable( MockControl.ZERO_OR_MORE );
388
389        mockTransferListenerControl.replay();
390    }
391
392    public void testWagonPutDirectory()
393        throws Exception
394    {
395        setupRepositories();
396
397        setupWagonTestingFixtures();
398
399        Wagon wagon = getWagon();
400
401        if ( wagon.supportsDirectoryCopy() )
402        {
403            sourceFile = new File( FileTestUtils.getTestOutputDir(), "directory-copy" );
404
405            FileUtils.deleteDirectory( sourceFile );
406
407            writeTestFile( "test-resource-1.txt" );
408            writeTestFile( "a/test-resource-2.txt" );
409            writeTestFile( "a/b/test-resource-3.txt" );
410            writeTestFile( "c/test-resource-4.txt" );
411            writeTestFile( "d/e/f/test-resource-5.txt" );
412
413            wagon.connect( testRepository, getAuthInfo() );
414
415            wagon.putDirectory( sourceFile, "directory-copy" );
416
417            destFile = FileTestUtils.createUniqueFile( getName(), getName() );
418
419            destFile.deleteOnExit();
420
421            wagon.get( "directory-copy/test-resource-1.txt", destFile );
422            wagon.get( "directory-copy/a/test-resource-2.txt", destFile );
423            wagon.get( "directory-copy/a/b/test-resource-3.txt", destFile );
424            wagon.get( "directory-copy/c/test-resource-4.txt", destFile );
425            wagon.get( "directory-copy/d/e/f/test-resource-5.txt", destFile );
426
427            wagon.disconnect();
428        }
429
430        tearDownWagonTestingFixtures();
431    }
432
433    /**
434     * Test for putting a directory with a destination that multiple directories deep, all of which haven't been
435     * created.
436     *
437     * @throws Exception
438     * @since 1.0-beta-2
439     */
440    public void testWagonPutDirectoryDeepDestination()
441        throws Exception
442    {
443        setupRepositories();
444
445        setupWagonTestingFixtures();
446
447        Wagon wagon = getWagon();
448
449        if ( wagon.supportsDirectoryCopy() )
450        {
451            sourceFile = new File( FileTestUtils.getTestOutputDir(), "deep0/deep1/deep2" );
452
453            FileUtils.deleteDirectory( sourceFile );
454
455            writeTestFile( "test-resource-1.txt" );
456            writeTestFile( "a/test-resource-2.txt" );
457            writeTestFile( "a/b/test-resource-3.txt" );
458            writeTestFile( "c/test-resource-4.txt" );
459            writeTestFile( "d/e/f/test-resource-5.txt" );
460
461            wagon.connect( testRepository, getAuthInfo() );
462
463            wagon.putDirectory( sourceFile, "deep0/deep1/deep2" );
464
465            destFile = FileTestUtils.createUniqueFile( getName(), getName() );
466
467            destFile.deleteOnExit();
468
469            wagon.get( "deep0/deep1/deep2/test-resource-1.txt", destFile );
470            wagon.get( "deep0/deep1/deep2/a/test-resource-2.txt", destFile );
471            wagon.get( "deep0/deep1/deep2/a/b/test-resource-3.txt", destFile );
472            wagon.get( "deep0/deep1/deep2/c/test-resource-4.txt", destFile );
473            wagon.get( "deep0/deep1/deep2/d/e/f/test-resource-5.txt", destFile );
474
475            wagon.disconnect();
476        }
477
478        tearDownWagonTestingFixtures();
479    }
480
481    /**
482     * Test that when putting a directory that already exists new files get also copied
483     *
484     * @throws Exception
485     * @since 1.0-beta-1
486     */
487    public void testWagonPutDirectoryWhenDirectoryAlreadyExists()
488        throws Exception
489    {
490
491        final String dirName = "directory-copy-existing";
492
493        final String resourceToCreate = "test-resource-1.txt";
494
495        final String[] resources = { "a/test-resource-2.txt", "a/b/test-resource-3.txt", "c/test-resource-4.txt" };
496
497        setupRepositories();
498
499        setupWagonTestingFixtures();
500
501        Wagon wagon = getWagon();
502
503        if ( wagon.supportsDirectoryCopy() )
504        {
505            sourceFile = new File( FileTestUtils.getTestOutputDir(), dirName );
506
507            FileUtils.deleteDirectory( sourceFile );
508
509            createDirectory( wagon, resourceToCreate, dirName );
510
511            for ( int i = 0; i < resources.length; i++ )
512            {
513                writeTestFile( resources[i] );
514            }
515
516            wagon.connect( testRepository, getAuthInfo() );
517
518            wagon.putDirectory( sourceFile, dirName );
519
520            List resourceNames = new ArrayList( resources.length + 1 );
521
522            resourceNames.add( dirName + "/" + resourceToCreate );
523            for ( int i = 0; i < resources.length; i++ )
524            {
525                resourceNames.add( dirName + "/" + resources[i] );
526            }
527
528            assertResourcesAreInRemoteSide( wagon, resourceNames );
529
530            wagon.disconnect();
531        }
532
533        tearDownWagonTestingFixtures();
534    }
535
536    /**
537     * Test that when putting a directory that already exists new files get also copied and destination is "."
538     *
539     * @throws Exception
540     * @since 1.0-beta-1
541     */
542    public void testWagonPutDirectoryForDot()
543        throws Exception
544    {
545        final String resourceToCreate = "test-resource-1.txt";
546
547        final String[] resources = { "a/test-resource-2.txt", "a/b/test-resource-3.txt", "c/test-resource-4.txt" };
548
549        setupRepositories();
550
551        setupWagonTestingFixtures();
552
553        Wagon wagon = getWagon();
554
555        if ( wagon.supportsDirectoryCopy() )
556        {
557            sourceFile = new File( FileTestUtils.getTestOutputDir(), "dot-repo" );
558
559            FileUtils.deleteDirectory( sourceFile );
560
561            createDirectory( wagon, resourceToCreate, "." );
562
563            for ( int i = 0; i < resources.length; i++ )
564            {
565                writeTestFile( resources[i] );
566            }
567
568            wagon.connect( testRepository, getAuthInfo() );
569
570            wagon.putDirectory( sourceFile, "." );
571
572            List<String> resourceNames = new ArrayList<String>( resources.length + 1 );
573
574            resourceNames.add( resourceToCreate );
575            for ( int i = 0; i < resources.length; i++ )
576            {
577                resourceNames.add( resources[i] );
578            }
579
580            assertResourcesAreInRemoteSide( wagon, resourceNames );
581
582            wagon.disconnect();
583        }
584
585        tearDownWagonTestingFixtures();
586    }
587
588    /**
589     * Create a directory with a resource and check that the other ones don't exist
590     *
591     * @param wagon
592     * @param resourceToCreate name of the resource to be created
593     * @param dirName          directory name to create
594     * @throws Exception
595     */
596    protected void createDirectory( Wagon wagon, String resourceToCreate, String dirName )
597        throws Exception
598    {
599        writeTestFile( resourceToCreate );
600    }
601
602    protected void assertResourcesAreInRemoteSide( Wagon wagon, List<String> resourceNames )
603        throws IOException, TransferFailedException, ResourceDoesNotExistException, AuthorizationException
604    {
605        Iterator<String> iter = resourceNames.iterator();
606        while ( iter.hasNext() )
607        {
608            String resourceName = iter.next();
609
610            File destFile = FileTestUtils.createUniqueFile( getName(), resourceName );
611
612            destFile.deleteOnExit();
613
614            wagon.get( resourceName, destFile );
615        }
616    }
617
618    /**
619     * Assert that a resource does not exist in the remote wagon system
620     *
621     * @param wagon        wagon to get the resource from
622     * @param resourceName name of the resource
623     * @throws IOException             if a temp file can't be created
624     * @throws AuthorizationException
625     * @throws TransferFailedException
626     * @since 1.0-beta-1
627     */
628    protected void assertNotExists( Wagon wagon, String resourceName )
629        throws IOException, TransferFailedException, AuthorizationException
630    {
631        File tmpFile = File.createTempFile( "wagon", null );
632        try
633        {
634            wagon.get( resourceName, tmpFile );
635            fail( "Resource exists: " + resourceName );
636        }
637        catch ( ResourceDoesNotExistException e )
638        {
639            // ok
640        }
641        finally
642        {
643            tmpFile.delete();
644        }
645    }
646
647    private void writeTestFile( String child )
648        throws IOException
649    {
650        File dir = new File( sourceFile, child );
651        dir.getParentFile().mkdirs();
652        FileUtils.fileWrite( dir.getAbsolutePath(), child );
653    }
654
655    public void testFailedGet()
656        throws Exception
657    {
658        setupRepositories();
659
660        setupWagonTestingFixtures();
661
662        message( "Getting test artifact from test repository " + testRepository );
663
664        Wagon wagon = getWagon();
665
666        wagon.addTransferListener( checksumObserver );
667
668        wagon.connect( testRepository, getAuthInfo() );
669
670        destFile = FileTestUtils.createUniqueFile( getName(), getName() );
671
672        destFile.deleteOnExit();
673
674        try
675        {
676            wagon.get( "fubar.txt", destFile );
677            fail( "File was found when it shouldn't have been" );
678        }
679        catch ( ResourceDoesNotExistException e )
680        {
681            // expected
682            assertTrue( true );
683        }
684        finally
685        {
686            wagon.removeTransferListener( checksumObserver );
687
688            wagon.disconnect();
689
690            tearDownWagonTestingFixtures();
691        }
692    }
693
694    public void testFailedGetIfNewer()
695        throws Exception
696    {
697        if ( supportsGetIfNewer() )
698        {
699            setupRepositories();
700            setupWagonTestingFixtures();
701            message( "Getting test artifact from test repository " + testRepository );
702            Wagon wagon = getWagon();
703            wagon.addTransferListener( checksumObserver );
704            wagon.connect( testRepository, getAuthInfo() );
705            destFile = FileTestUtils.createUniqueFile( getName(), getName() );
706            destFile.deleteOnExit();
707            try
708            {
709                wagon.getIfNewer( "fubar.txt", destFile, 0 );
710                fail( "File was found when it shouldn't have been" );
711            }
712            catch ( ResourceDoesNotExistException e )
713            {
714                // expected
715                assertTrue( true );
716            }
717            finally
718            {
719                wagon.removeTransferListener( checksumObserver );
720
721                wagon.disconnect();
722
723                tearDownWagonTestingFixtures();
724            }
725        }
726    }
727
728    /**
729     * Test {@link Wagon#getFileList(String)}.
730     *
731     * @throws Exception
732     * @since 1.0-beta-2
733     */
734    public void testWagonGetFileList()
735        throws Exception
736    {
737        setupRepositories();
738
739        setupWagonTestingFixtures();
740
741        String dirName = "file-list";
742
743        String filenames[] =
744            new String[]{ "test-resource.txt", "test-resource.pom", "test-resource b.txt", "more-resources.dat",
745                ".index.txt" };
746
747        for ( int i = 0; i < filenames.length; i++ )
748        {
749            putFile( dirName + "/" + filenames[i], dirName + "/" + filenames[i], filenames[i] + "\n" );
750        }
751
752        Wagon wagon = getWagon();
753
754        wagon.connect( testRepository, getAuthInfo() );
755
756        List<String> list = wagon.getFileList( dirName );
757        assertNotNull( "file list should not be null.", list );
758        assertTrue( "file list should contain more items (actually contains '" + list + "').",
759                    list.size() >= filenames.length );
760
761        for ( int i = 0; i < filenames.length; i++ )
762        {
763            assertTrue( "Filename '" + filenames[i] + "' should be in list.", list.contains( filenames[i] ) );
764        }
765
766        // WAGON-250
767        list = wagon.getFileList( "" );
768        assertNotNull( "file list should not be null.", list );
769        assertTrue( "file list should contain items (actually contains '" + list + "').", !list.isEmpty() );
770        assertTrue( list.contains( "file-list/" ) );
771        assertFalse( list.contains( "file-list" ) );
772        assertFalse( list.contains( "." ) );
773        assertFalse( list.contains( ".." ) );
774        assertFalse( list.contains( "./" ) );
775        assertFalse( list.contains( "../" ) );
776
777        wagon.disconnect();
778
779        tearDownWagonTestingFixtures();
780    }
781
782    /**
783     * Test {@link Wagon#getFileList(String)} when the directory does not exist.
784     *
785     * @throws Exception
786     * @since 1.0-beta-2
787     */
788    public void testWagonGetFileListWhenDirectoryDoesNotExist()
789        throws Exception
790    {
791        setupRepositories();
792
793        setupWagonTestingFixtures();
794
795        String dirName = "file-list-unexisting";
796
797        Wagon wagon = getWagon();
798
799        wagon.connect( testRepository, getAuthInfo() );
800
801        try
802        {
803            wagon.getFileList( dirName );
804            fail( "getFileList on unexisting directory must throw ResourceDoesNotExistException" );
805        }
806        catch ( ResourceDoesNotExistException e )
807        {
808            // expected
809        }
810        finally
811        {
812            wagon.disconnect();
813
814            tearDownWagonTestingFixtures();
815        }
816    }
817
818    /**
819     * Test for an existing resource.
820     *
821     * @throws Exception
822     * @since 1.0-beta-2
823     */
824    public void testWagonResourceExists()
825        throws Exception
826    {
827        setupRepositories();
828
829        setupWagonTestingFixtures();
830
831        Wagon wagon = getWagon();
832
833        putFile();
834
835        wagon.connect( testRepository, getAuthInfo() );
836
837        assertTrue( sourceFile.getName() + " does not exist", wagon.resourceExists( sourceFile.getName() ) );
838
839        wagon.disconnect();
840
841        tearDownWagonTestingFixtures();
842    }
843
844    /**
845     * Test for an invalid resource.
846     *
847     * @throws Exception
848     * @since 1.0-beta-2
849     */
850    public void testWagonResourceNotExists()
851        throws Exception
852    {
853        setupRepositories();
854
855        setupWagonTestingFixtures();
856
857        Wagon wagon = getWagon();
858
859        wagon.connect( testRepository, getAuthInfo() );
860
861        assertFalse( wagon.resourceExists( "a/bad/resource/name/that/should/not/exist.txt" ) );
862
863        wagon.disconnect();
864
865        tearDownWagonTestingFixtures();
866    }
867
868    // ----------------------------------------------------------------------
869    // File <--> File round trip testing
870    // ----------------------------------------------------------------------
871    // We are testing taking a file, our sourcefile, and placing it into the
872    // test repository that we have setup.
873    // ----------------------------------------------------------------------
874
875    protected void putFile( String resourceName, String testFileName, String content )
876        throws Exception
877    {
878        sourceFile = new File( FileTestUtils.getTestOutputDir(), testFileName );
879        sourceFile.getParentFile().mkdirs();
880        FileUtils.fileWrite( sourceFile.getAbsolutePath(), content );
881
882        Wagon wagon = getWagon();
883
884        ProgressArgumentMatcher progressArgumentMatcher = replayMockForPut( resourceName, content, wagon );
885
886        message( "Putting test artifact: " + resourceName + " into test repository " + testRepository );
887
888        connectWagon( wagon );
889
890        wagon.put( sourceFile, resourceName );
891
892        disconnectWagon( wagon );
893
894        verifyMock( progressArgumentMatcher, content.length() );
895    }
896
897    protected ProgressArgumentMatcher replayMockForPut( String resourceName, String content, Wagon wagon )
898    {
899        Resource resource = new Resource( resourceName );
900        mockTransferListener.transferInitiated(
901            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_INITIATED, TransferEvent.REQUEST_PUT,
902                                 sourceFile ) );
903        resource = new Resource( resourceName );
904        resource.setContentLength( content.length() );
905        resource.setLastModified( sourceFile.lastModified() );
906        mockTransferListener.transferStarted(
907            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_STARTED, TransferEvent.REQUEST_PUT,
908                                 sourceFile ) );
909        mockTransferListener.transferProgress(
910            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT,
911                                 sourceFile ), new byte[]{ }, 0 );
912        ProgressArgumentMatcher progressArgumentMatcher = new ProgressArgumentMatcher();
913        mockTransferListenerControl.setMatcher( progressArgumentMatcher );
914
915        mockTransferListener.debug( null );
916        mockTransferListenerControl.setMatcher( MockControl.ALWAYS_MATCHER );
917        mockTransferListenerControl.setVoidCallable( MockControl.ZERO_OR_MORE );
918
919        mockTransferListener.transferCompleted(
920            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_COMPLETED, TransferEvent.REQUEST_PUT,
921                                 sourceFile ) );
922
923        mockTransferListenerControl.replay();
924        return progressArgumentMatcher;
925    }
926
927    protected TransferEvent createTransferEvent( Wagon wagon, Resource resource, int eventType, int requestType,
928                                                 File file )
929    {
930        TransferEvent transferEvent = new TransferEvent( wagon, resource, eventType, requestType );
931        transferEvent.setLocalFile( file );
932        return transferEvent;
933    }
934
935    protected int putFile()
936        throws Exception
937    {
938        String content = "test-resource.txt\n";
939        putFile( resource, "test-resource", content );
940        return content.length();
941    }
942
943    protected void getFile( int expectedSize )
944        throws Exception
945    {
946        destFile = FileTestUtils.createUniqueFile( getName(), getName() );
947        destFile.deleteOnExit();
948
949        Wagon wagon = getWagon();
950
951        ProgressArgumentMatcher progressArgumentMatcher = replaceMockForGet( wagon, expectedSize );
952
953        message( "Getting test artifact from test repository " + testRepository );
954
955        connectWagon( wagon );
956
957        wagon.get( this.resource, destFile );
958
959        disconnectWagon( wagon );
960
961        verifyMock( progressArgumentMatcher, expectedSize );
962    }
963
964
965    protected void verifyMock( ProgressArgumentMatcher progressArgumentMatcher, int length )
966    {
967        mockTransferListenerControl.verify();
968
969        assertEquals( length, progressArgumentMatcher.getSize() );
970
971        mockTransferListenerControl.reset();
972    }
973
974    protected void disconnectWagon( Wagon wagon )
975        throws ConnectionException
976    {
977        wagon.removeTransferListener( mockTransferListener );
978
979        wagon.removeTransferListener( checksumObserver );
980
981        wagon.disconnect();
982    }
983
984    protected void connectWagon( Wagon wagon )
985        throws ConnectionException, AuthenticationException
986    {
987        wagon.addTransferListener( checksumObserver );
988
989        wagon.addTransferListener( mockTransferListener );
990
991        wagon.connect( testRepository, getAuthInfo() );
992    }
993
994    /**
995     *
996     * some test (mock on transfertprogress call) relies on the fact that InputStream #read(byte[] b, int off, int len)
997     * read all bytes. But javadoc says: ""
998     */
999    protected boolean assertOnTransferProgress()
1000    {
1001        return false;
1002    }
1003
1004    protected ProgressArgumentMatcher replaceMockForGet( Wagon wagon, int expectedSize )
1005    {
1006        Resource resource = new Resource( this.resource );
1007        mockTransferListener.transferInitiated(
1008            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_INITIATED, TransferEvent.REQUEST_GET,
1009                                 destFile ) );
1010        resource = new Resource( this.resource );
1011        resource.setContentLength( getExpectedContentLengthOnGet( expectedSize ) );
1012        resource.setLastModified( getExpectedLastModifiedOnGet( testRepository, resource ) );
1013        TransferEvent te =
1014            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_STARTED, TransferEvent.REQUEST_GET, null );
1015        mockTransferListener.transferStarted( te );
1016        mockTransferListener.transferProgress(
1017            new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_GET ),
1018            new byte[]{ }, 0 );
1019
1020        ProgressArgumentMatcher progressArgumentMatcher = new ProgressArgumentMatcher();
1021
1022        if ( assertOnTransferProgress() )
1023        {
1024            mockTransferListenerControl.setMatcher( progressArgumentMatcher );
1025        }
1026        else
1027        {
1028            mockTransferListenerControl.setMatcher( progressArgumentMatcher );
1029            mockTransferListenerControl.setVoidCallable( MockControl.ZERO_OR_MORE );
1030        }
1031        mockTransferListener.debug( null );
1032        mockTransferListenerControl.setMatcher( MockControl.ALWAYS_MATCHER );
1033        mockTransferListenerControl.setVoidCallable( MockControl.ZERO_OR_MORE );
1034
1035        mockTransferListener.transferCompleted(
1036            createTransferEvent( wagon, resource, TransferEvent.TRANSFER_COMPLETED, TransferEvent.REQUEST_GET,
1037                                 destFile ) );
1038
1039        mockTransferListenerControl.replay();
1040        return progressArgumentMatcher;
1041    }
1042
1043    protected int getExpectedContentLengthOnGet( int expectedSize )
1044    {
1045        return expectedSize;
1046    }
1047
1048    protected long getExpectedLastModifiedOnGet( Repository repository, Resource resource )
1049    {
1050        // default implementation - prone to failing if the time between test file creation and completion of putFile()
1051        // cross the "second" boundary, causing the "remote" and local files to have different times.
1052
1053        return sourceFile.lastModified();
1054    }
1055
1056    protected void fileRoundTripTesting()
1057        throws Exception
1058    {
1059        message( "File round trip testing ..." );
1060
1061        int expectedSize = putFile();
1062
1063        assertNotNull( "check checksum is not null", checksumObserver.getActualChecksum() );
1064
1065        assertEquals( "compare checksums", "6b144b7285ffd6b0bc8300da162120b9", checksumObserver.getActualChecksum() );
1066
1067        checksumObserver = new ChecksumObserver();
1068
1069        getFile( expectedSize );
1070
1071        assertNotNull( "check checksum is not null", checksumObserver.getActualChecksum() );
1072
1073        assertEquals( "compare checksums", "6b144b7285ffd6b0bc8300da162120b9", checksumObserver.getActualChecksum() );
1074
1075        // Now compare the conents of the artifact that was placed in
1076        // the repository with the contents of the artifact that was
1077        // retrieved from the repository.
1078
1079        String sourceContent = FileUtils.fileRead( sourceFile );
1080
1081        String destContent = FileUtils.fileRead( destFile );
1082
1083        assertEquals( sourceContent, destContent );
1084    }
1085
1086    // ----------------------------------------------------------------------
1087    //
1088    // ----------------------------------------------------------------------
1089
1090    protected Repository createFileRepository( String url )
1091    {
1092        File path = new File( url.substring( 7 ) );
1093
1094        path.mkdirs();
1095
1096        Repository repository = new Repository();
1097
1098        repository.setUrl( url );
1099
1100        return repository;
1101    }
1102
1103}