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