001package org.eclipse.aether.transport.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 static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertTrue;
024import static org.junit.Assert.fail;
025
026import java.io.File;
027import java.net.URI;
028import java.nio.charset.StandardCharsets;
029import java.util.Map;
030import java.util.UUID;
031
032import org.apache.maven.wagon.ResourceDoesNotExistException;
033import org.apache.maven.wagon.TransferFailedException;
034import org.apache.maven.wagon.Wagon;
035import org.eclipse.aether.ConfigurationProperties;
036import org.eclipse.aether.DefaultRepositorySystemSession;
037import org.eclipse.aether.internal.test.util.TestFileUtils;
038import org.eclipse.aether.internal.test.util.TestLoggerFactory;
039import org.eclipse.aether.internal.test.util.TestUtils;
040import org.eclipse.aether.repository.Authentication;
041import org.eclipse.aether.repository.Proxy;
042import org.eclipse.aether.repository.RemoteRepository;
043import org.eclipse.aether.spi.connector.transport.GetTask;
044import org.eclipse.aether.spi.connector.transport.PeekTask;
045import org.eclipse.aether.spi.connector.transport.PutTask;
046import org.eclipse.aether.spi.connector.transport.Transporter;
047import org.eclipse.aether.spi.connector.transport.TransporterFactory;
048import org.eclipse.aether.transfer.NoTransporterException;
049import org.eclipse.aether.transfer.TransferCancelledException;
050import org.eclipse.aether.util.repository.AuthenticationBuilder;
051import org.junit.After;
052import org.junit.Before;
053import org.junit.Test;
054
055/**
056 */
057public abstract class AbstractWagonTransporterTest
058{
059
060    private DefaultRepositorySystemSession session;
061
062    private TransporterFactory factory;
063
064    private Transporter transporter;
065
066    private String id;
067
068    private Map<String, String> fs;
069
070    protected abstract Wagon newWagon();
071
072    private RemoteRepository newRepo( String url )
073    {
074        return new RemoteRepository.Builder( "test", "default", url ).build();
075    }
076
077    private void newTransporter( String url )
078        throws Exception
079    {
080        newTransporter( newRepo( url ) );
081    }
082
083    private void newTransporter( RemoteRepository repo )
084        throws Exception
085    {
086        if ( transporter != null )
087        {
088            transporter.close();
089            transporter = null;
090        }
091        transporter = factory.newInstance( session, repo );
092    }
093
094    @Before
095    public void setUp()
096        throws Exception
097    {
098        session = TestUtils.newSession();
099        factory = new WagonTransporterFactory( new WagonProvider()
100        {
101            public Wagon lookup( String roleHint )
102                throws Exception
103            {
104                if ( "mem".equalsIgnoreCase( roleHint ) )
105                {
106                    return newWagon();
107                }
108                throw new IllegalArgumentException( "unknown wagon role: " + roleHint );
109            }
110
111            public void release( Wagon wagon )
112            {
113            }
114        }, new WagonConfigurator()
115        {
116            public void configure( Wagon wagon, Object configuration )
117                throws Exception
118            {
119                ( (Configurable) wagon ).setConfiguration( configuration );
120            }
121        }, new TestLoggerFactory() );
122        id = UUID.randomUUID().toString().replace( "-", "" );
123        fs = MemWagonUtils.getFilesystem( id );
124        fs.put( "file.txt", "test" );
125        fs.put( "empty.txt", "" );
126        fs.put( "some space.txt", "space" );
127        newTransporter( "mem://" + id );
128    }
129
130    @After
131    public void tearDown()
132    {
133        if ( transporter != null )
134        {
135            transporter.close();
136            transporter = null;
137        }
138        factory = null;
139        session = null;
140    }
141
142    @Test
143    public void testClassify()
144    {
145        assertEquals( Transporter.ERROR_OTHER, transporter.classify( new TransferFailedException( "test" ) ) );
146        assertEquals( Transporter.ERROR_NOT_FOUND, transporter.classify( new ResourceDoesNotExistException( "test" ) ) );
147    }
148
149    @Test
150    public void testPeek()
151        throws Exception
152    {
153        transporter.peek( new PeekTask( URI.create( "file.txt" ) ) );
154    }
155
156    @Test
157    public void testPeek_NotFound()
158        throws Exception
159    {
160        try
161        {
162            transporter.peek( new PeekTask( URI.create( "missing.txt" ) ) );
163            fail( "Expected error" );
164        }
165        catch ( ResourceDoesNotExistException e )
166        {
167            assertEquals( Transporter.ERROR_NOT_FOUND, transporter.classify( e ) );
168        }
169    }
170
171    @Test
172    public void testPeek_Closed()
173        throws Exception
174    {
175        transporter.close();
176        try
177        {
178            transporter.peek( new PeekTask( URI.create( "missing.txt" ) ) );
179            fail( "Expected error" );
180        }
181        catch ( IllegalStateException e )
182        {
183            assertEquals( Transporter.ERROR_OTHER, transporter.classify( e ) );
184        }
185    }
186
187    @Test
188    public void testGet_ToMemory()
189        throws Exception
190    {
191        RecordingTransportListener listener = new RecordingTransportListener();
192        GetTask task = new GetTask( URI.create( "file.txt" ) ).setListener( listener );
193        transporter.get( task );
194        assertEquals( "test", task.getDataString() );
195        assertEquals( 0L, listener.dataOffset );
196        assertEquals( 4L, listener.dataLength );
197        assertEquals( 1, listener.startedCount );
198        assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
199        assertEquals( task.getDataString(), new String( listener.baos.toByteArray(), StandardCharsets.UTF_8 ) );
200    }
201
202    @Test
203    public void testGet_ToFile()
204        throws Exception
205    {
206        File file = TestFileUtils.createTempFile( "failure" );
207        RecordingTransportListener listener = new RecordingTransportListener();
208        GetTask task = new GetTask( URI.create( "file.txt" ) ).setDataFile( file ).setListener( listener );
209        transporter.get( task );
210        assertEquals( "test", TestFileUtils.readString( file ) );
211        assertEquals( 0L, listener.dataOffset );
212        assertEquals( 4L, listener.dataLength );
213        assertEquals( 1, listener.startedCount );
214        assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
215        assertEquals( "test", new String( listener.baos.toByteArray(), StandardCharsets.UTF_8 ) );
216    }
217
218    @Test
219    public void testGet_EmptyResource()
220        throws Exception
221    {
222        File file = TestFileUtils.createTempFile( "failure" );
223        assertTrue( file.delete() && !file.exists() );
224        RecordingTransportListener listener = new RecordingTransportListener();
225        GetTask task = new GetTask( URI.create( "empty.txt" ) ).setDataFile( file ).setListener( listener );
226        transporter.get( task );
227        assertEquals( "", TestFileUtils.readString( file ) );
228        assertEquals( 0L, listener.dataOffset );
229        assertEquals( 0L, listener.dataLength );
230        assertEquals( 1, listener.startedCount );
231        assertEquals( 0, listener.progressedCount );
232        assertEquals( "", new String( listener.baos.toByteArray(), StandardCharsets.UTF_8 ) );
233    }
234
235    @Test
236    public void testGet_EncodedResourcePath()
237        throws Exception
238    {
239        GetTask task = new GetTask( URI.create( "some%20space.txt" ) );
240        transporter.get( task );
241        assertEquals( "space", task.getDataString() );
242    }
243
244    @Test
245    public void testGet_FileHandleLeak()
246        throws Exception
247    {
248        for ( int i = 0; i < 100; i++ )
249        {
250            File file = TestFileUtils.createTempFile( "failure" );
251            transporter.get( new GetTask( URI.create( "file.txt" ) ).setDataFile( file ) );
252            assertTrue( i + ", " + file.getAbsolutePath(), file.delete() );
253        }
254    }
255
256    @Test
257    public void testGet_NotFound()
258        throws Exception
259    {
260        try
261        {
262            transporter.get( new GetTask( URI.create( "missing.txt" ) ) );
263            fail( "Expected error" );
264        }
265        catch ( ResourceDoesNotExistException e )
266        {
267            assertEquals( Transporter.ERROR_NOT_FOUND, transporter.classify( e ) );
268        }
269    }
270
271    @Test
272    public void testGet_Closed()
273        throws Exception
274    {
275        transporter.close();
276        try
277        {
278            transporter.get( new GetTask( URI.create( "file.txt" ) ) );
279            fail( "Expected error" );
280        }
281        catch ( IllegalStateException e )
282        {
283            assertEquals( Transporter.ERROR_OTHER, transporter.classify( e ) );
284        }
285    }
286
287    @Test
288    public void testGet_StartCancelled()
289        throws Exception
290    {
291        RecordingTransportListener listener = new RecordingTransportListener();
292        listener.cancelStart = true;
293        GetTask task = new GetTask( URI.create( "file.txt" ) ).setListener( listener );
294        transporter.get( task );
295        assertEquals( 1, listener.startedCount );
296    }
297
298    @Test
299    public void testGet_ProgressCancelled()
300        throws Exception
301    {
302        RecordingTransportListener listener = new RecordingTransportListener();
303        listener.cancelProgress = true;
304        GetTask task = new GetTask( URI.create( "file.txt" ) ).setListener( listener );
305        try
306        {
307            transporter.get( task );
308            fail( "Expected error" );
309        }
310        catch ( TransferCancelledException e )
311        {
312            assertEquals( Transporter.ERROR_OTHER, transporter.classify( e ) );
313        }
314        assertEquals( 0L, listener.dataOffset );
315        assertEquals( 4L, listener.dataLength );
316        assertEquals( 1, listener.startedCount );
317        assertEquals( 1, listener.progressedCount );
318    }
319
320    @Test
321    public void testPut_FromMemory()
322        throws Exception
323    {
324        RecordingTransportListener listener = new RecordingTransportListener();
325        PutTask task = new PutTask( URI.create( "file.txt" ) ).setListener( listener ).setDataString( "upload" );
326        transporter.put( task );
327        assertEquals( 0L, listener.dataOffset );
328        assertEquals( 6L, listener.dataLength );
329        assertEquals( 1, listener.startedCount );
330        assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
331        assertEquals( "upload", fs.get( "file.txt" ) );
332    }
333
334    @Test
335    public void testPut_FromFile()
336        throws Exception
337    {
338        File file = TestFileUtils.createTempFile( "upload" );
339        RecordingTransportListener listener = new RecordingTransportListener();
340        PutTask task = new PutTask( URI.create( "file.txt" ) ).setListener( listener ).setDataFile( file );
341        transporter.put( task );
342        assertEquals( 0L, listener.dataOffset );
343        assertEquals( 6L, listener.dataLength );
344        assertEquals( 1, listener.startedCount );
345        assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
346        assertEquals( "upload", fs.get( "file.txt" ) );
347    }
348
349    @Test
350    public void testPut_EmptyResource()
351        throws Exception
352    {
353        RecordingTransportListener listener = new RecordingTransportListener();
354        PutTask task = new PutTask( URI.create( "file.txt" ) ).setListener( listener );
355        transporter.put( task );
356        assertEquals( 0L, listener.dataOffset );
357        assertEquals( 0L, listener.dataLength );
358        assertEquals( 1, listener.startedCount );
359        assertEquals( 0, listener.progressedCount );
360        assertEquals( "", fs.get( "file.txt" ) );
361    }
362
363    @Test
364    public void testPut_NonExistentParentDir()
365        throws Exception
366    {
367        RecordingTransportListener listener = new RecordingTransportListener();
368        PutTask task =
369            new PutTask( URI.create( "dir/sub/dir/file.txt" ) ).setListener( listener ).setDataString( "upload" );
370        transporter.put( task );
371        assertEquals( 0L, listener.dataOffset );
372        assertEquals( 6L, listener.dataLength );
373        assertEquals( 1, listener.startedCount );
374        assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
375        assertEquals( "upload", fs.get( "dir/sub/dir/file.txt" ) );
376    }
377
378    @Test
379    public void testPut_EncodedResourcePath()
380        throws Exception
381    {
382        RecordingTransportListener listener = new RecordingTransportListener();
383        PutTask task = new PutTask( URI.create( "some%20space.txt" ) ).setListener( listener ).setDataString( "OK" );
384        transporter.put( task );
385        assertEquals( 0L, listener.dataOffset );
386        assertEquals( 2L, listener.dataLength );
387        assertEquals( 1, listener.startedCount );
388        assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
389        assertEquals( "OK", fs.get( "some space.txt" ) );
390    }
391
392    @Test
393    public void testPut_FileHandleLeak()
394        throws Exception
395    {
396        for ( int i = 0; i < 100; i++ )
397        {
398            File src = TestFileUtils.createTempFile( "upload" );
399            transporter.put( new PutTask( URI.create( "file.txt" ) ).setDataFile( src ) );
400            assertTrue( i + ", " + src.getAbsolutePath(), src.delete() );
401        }
402    }
403
404    @Test
405    public void testPut_Closed()
406        throws Exception
407    {
408        transporter.close();
409        try
410        {
411            transporter.put( new PutTask( URI.create( "missing.txt" ) ) );
412            fail( "Expected error" );
413        }
414        catch ( IllegalStateException e )
415        {
416            assertEquals( Transporter.ERROR_OTHER, transporter.classify( e ) );
417        }
418    }
419
420    @Test
421    public void testPut_StartCancelled()
422        throws Exception
423    {
424        RecordingTransportListener listener = new RecordingTransportListener();
425        listener.cancelStart = true;
426        PutTask task = new PutTask( URI.create( "file.txt" ) ).setListener( listener ).setDataString( "upload" );
427        transporter.put( task );
428        assertEquals( 1, listener.startedCount );
429    }
430
431    @Test
432    public void testPut_ProgressCancelled()
433        throws Exception
434    {
435        RecordingTransportListener listener = new RecordingTransportListener();
436        listener.cancelProgress = true;
437        PutTask task = new PutTask( URI.create( "file.txt" ) ).setListener( listener ).setDataString( "upload" );
438        try
439        {
440            transporter.put( task );
441            fail( "Expected error" );
442        }
443        catch ( TransferCancelledException e )
444        {
445            assertEquals( Transporter.ERROR_OTHER, transporter.classify( e ) );
446        }
447        assertEquals( 0L, listener.dataOffset );
448        assertEquals( 6L, listener.dataLength );
449        assertEquals( 1, listener.startedCount );
450        assertEquals( 1, listener.progressedCount );
451    }
452
453    @Test( expected = NoTransporterException.class )
454    public void testInit_BadProtocol()
455        throws Exception
456    {
457        newTransporter( "bad:/void" );
458    }
459
460    @Test
461    public void testInit_CaseInsensitiveProtocol()
462        throws Exception
463    {
464        newTransporter( "mem:/void" );
465        newTransporter( "MEM:/void" );
466        newTransporter( "mEm:/void" );
467    }
468
469    @Test
470    public void testInit_Configuration()
471        throws Exception
472    {
473        session.setConfigProperty( "aether.connector.wagon.config.test", "passed" );
474        newTransporter( "mem://" + id + "?config=passed" );
475        transporter.peek( new PeekTask( URI.create( "file.txt" ) ) );
476    }
477
478    @Test
479    public void testInit_UserAgent()
480        throws Exception
481    {
482        session.setConfigProperty( ConfigurationProperties.USER_AGENT, "Test/1.0" );
483        newTransporter( "mem://" + id + "?userAgent=Test/1.0" );
484        transporter.peek( new PeekTask( URI.create( "file.txt" ) ) );
485    }
486
487    @Test
488    public void testInit_Timeout()
489        throws Exception
490    {
491        session.setConfigProperty( ConfigurationProperties.REQUEST_TIMEOUT, "12345678" );
492        newTransporter( "mem://" + id + "?requestTimeout=12345678" );
493        transporter.peek( new PeekTask( URI.create( "file.txt" ) ) );
494    }
495
496    @Test
497    public void testInit_ServerAuth()
498        throws Exception
499    {
500        String url =
501            "mem://" + id + "?serverUsername=testuser&serverPassword=testpass"
502                + "&serverPrivateKey=testkey&serverPassphrase=testphrase";
503        Authentication auth =
504            new AuthenticationBuilder().addUsername( "testuser" ).addPassword( "testpass" ).addPrivateKey( "testkey",
505                                                                                                           "testphrase" ).build();
506        RemoteRepository repo =
507            new RemoteRepository.Builder( "test", "default", url ).setAuthentication( auth ).build();
508        newTransporter( repo );
509        transporter.peek( new PeekTask( URI.create( "file.txt" ) ) );
510    }
511
512    @Test
513    public void testInit_Proxy()
514        throws Exception
515    {
516        String url = "mem://" + id + "?proxyHost=testhost&proxyPort=8888";
517        RemoteRepository repo =
518            new RemoteRepository.Builder( "test", "default", url ).setProxy( new Proxy( "http", "testhost", 8888 ) ).build();
519        newTransporter( repo );
520        transporter.peek( new PeekTask( URI.create( "file.txt" ) ) );
521    }
522
523    @Test
524    public void testInit_ProxyAuth()
525        throws Exception
526    {
527        String url = "mem://" + id + "?proxyUsername=testuser&proxyPassword=testpass";
528        Authentication auth = new AuthenticationBuilder().addUsername( "testuser" ).addPassword( "testpass" ).build();
529        RemoteRepository repo =
530            new RemoteRepository.Builder( "test", "default", url ).setProxy( new Proxy( "http", "testhost", 8888, auth ) ).build();
531        newTransporter( repo );
532        transporter.peek( new PeekTask( URI.create( "file.txt" ) ) );
533    }
534
535}