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