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