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