001package org.eclipse.aether.internal.impl.collect.bf;
002/*
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *  http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 */
020
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.List;
024import java.util.Map;
025import java.util.stream.Collectors;
026
027import org.eclipse.aether.artifact.DefaultArtifact;
028import org.eclipse.aether.graph.DefaultDependencyNode;
029import org.eclipse.aether.graph.Dependency;
030import org.eclipse.aether.graph.DependencyNode;
031import org.eclipse.aether.internal.test.util.TestVersion;
032import org.eclipse.aether.internal.test.util.TestVersionConstraint;
033import org.junit.Test;
034
035import static org.junit.Assert.assertEquals;
036import static org.junit.Assert.assertFalse;
037import static org.junit.Assert.assertTrue;
038
039
040public class DependencyResolutionSkipperTest
041{
042    private static DependencyNode makeDependencyNode( String groupId, String artifactId, String version )
043    {
044        return makeDependencyNode( groupId, artifactId, version, "compile" );
045    }
046
047    private static List<DependencyNode> mutableList( DependencyNode... nodes )
048    {
049        return new ArrayList<>( Arrays.asList( nodes ) );
050    }
051
052    private static DependencyNode makeDependencyNode( String groupId, String artifactId, String version, String scope )
053    {
054        DefaultDependencyNode node = new DefaultDependencyNode(
055                new Dependency( new DefaultArtifact( groupId + ':' + artifactId + ':' + version ), scope )
056        );
057        node.setVersion( new TestVersion( version ) );
058        node.setVersionConstraint( new TestVersionConstraint( node.getVersion() ) );
059        return node;
060    }
061
062    @Test
063    public void testSkipVersionConflict()
064    {
065        // A -> B -> C 3.0 -> D   => C3.0 SHOULD BE SKIPPED
066        // | -> E -> F -> G
067        // | -> C 2.0 -> H  => C2.0 is the winner
068        DependencyNode aNode = makeDependencyNode( "some-group", "A", "1.0" );
069        DependencyNode bNode = makeDependencyNode( "some-group", "B", "1.0" );
070        DependencyNode c3Node = makeDependencyNode( "some-group", "C", "3.0" );
071        DependencyNode dNode = makeDependencyNode( "some-group", "D", "1.0" );
072        DependencyNode eNode = makeDependencyNode( "some-group", "E", "1.0" );
073        DependencyNode fNode = makeDependencyNode( "some-group", "F", "1.0" );
074        DependencyNode c2Node = makeDependencyNode( "some-group", "C", "2.0" );
075        DependencyNode gNode = makeDependencyNode( "some-group", "G", "1.0" );
076        DependencyNode hNode = makeDependencyNode( "some-group", "H", "1.0" );
077
078        aNode.setChildren( mutableList( bNode, eNode, c2Node ) );
079        bNode.setChildren( mutableList( c3Node ) );
080        c3Node.setChildren( mutableList( dNode ) );
081        eNode.setChildren( mutableList( fNode ) );
082        fNode.setChildren( mutableList( gNode ) );
083        c2Node.setChildren( mutableList( hNode ) );
084
085        //follow the BFS resolve sequence
086        DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper = DependencyResolutionSkipper.defaultSkipper();
087        assertFalse( skipper.skipResolution( aNode, new ArrayList<>() ) );
088        skipper.cache( aNode, new ArrayList<>() );
089        assertFalse( skipper.skipResolution( bNode, mutableList( aNode ) ) );
090        skipper.cache( bNode, mutableList( aNode ) );
091        assertFalse( skipper.skipResolution( eNode, mutableList( aNode ) ) );
092        skipper.cache( eNode, mutableList( aNode ) );
093        assertFalse( skipper.skipResolution( c2Node, mutableList( aNode ) ) );
094        skipper.cache( c2Node, mutableList( aNode ) );
095        assertTrue( skipper.skipResolution( c3Node, mutableList( aNode, bNode ) ) );//version conflict
096        assertFalse( skipper.skipResolution( fNode, mutableList( aNode, eNode ) ) );
097        skipper.cache( fNode, mutableList( aNode, eNode ) );
098        assertFalse( skipper.skipResolution( gNode, mutableList( aNode, eNode, fNode ) ) );
099        skipper.cache( gNode, mutableList( aNode, eNode, fNode ) );
100
101        Map<DependencyNode, DependencyResolutionSkipper.DependencyResolutionResult> results = skipper.getResults();
102        assertEquals( results.size(), 7 );
103
104        List<DependencyResolutionSkipper.DependencyResolutionResult> skipped =
105                results.values().stream()
106                        .filter( dependencyResolutionResult -> dependencyResolutionResult.skippedAsVersionConflict )
107                        .collect( Collectors.toList() );
108        assertEquals( skipped.size(), 1 );
109        assertTrue( skipped.get( 0 ).current == c3Node );
110    }
111
112    @Test
113    public void testSkipDeeperDuplicateNode()
114    {
115        // A -> B
116        // |--> C -> B  => B here will be skipped
117        // |--> D -> C  => C here will be skipped
118        DependencyNode aNode = makeDependencyNode( "some-group", "A", "1.0" );
119        DependencyNode bNode = makeDependencyNode( "some-group", "B", "1.0" );
120        DependencyNode cNode = makeDependencyNode( "some-group", "C", "1.0" );
121        DependencyNode dNode = makeDependencyNode( "some-group", "D", "1.0" );
122        DependencyNode b1Node = new DefaultDependencyNode( bNode );
123        DependencyNode c1Node = new DefaultDependencyNode( cNode );
124
125        aNode.setChildren( mutableList( bNode, cNode, dNode ) );
126        bNode.setChildren( new ArrayList<>() );
127        cNode.setChildren( mutableList( b1Node ) );
128        dNode.setChildren( mutableList( c1Node ) );
129
130        //follow the BFS resolve sequence
131        DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper = DependencyResolutionSkipper.defaultSkipper();
132        assertFalse( skipper.skipResolution( aNode, new ArrayList<>() ) );
133        skipper.cache( aNode, new ArrayList<>() );
134        assertFalse( skipper.skipResolution( bNode, mutableList( aNode ) ) );
135        skipper.cache( bNode, mutableList( aNode ) );
136        assertFalse( skipper.skipResolution( cNode, mutableList( aNode ) ) );
137        skipper.cache( cNode, mutableList( aNode ) );
138        assertFalse( skipper.skipResolution( dNode, mutableList( aNode ) ) );
139        skipper.cache( dNode, mutableList( aNode ) );
140
141        assertTrue( skipper.skipResolution( b1Node, mutableList( aNode, cNode ) ) );
142        skipper.cache( b1Node, mutableList( aNode, cNode ) );
143
144        assertTrue( skipper.skipResolution( c1Node, mutableList( aNode, dNode ) ) );
145        skipper.cache( c1Node, mutableList( aNode, dNode ) );
146
147        Map<DependencyNode, DependencyResolutionSkipper.DependencyResolutionResult> results = skipper.getResults();
148        assertEquals( results.size(), 6 );
149
150        List<DependencyResolutionSkipper.DefaultDependencyResolutionSkipper.DependencyResolutionResult> skipped =
151                results.values().stream()
152                        .filter( dependencyResolutionResult -> dependencyResolutionResult.skippedAsDuplicate )
153                        .collect( Collectors.toList() );
154        assertEquals( skipped.size(), 2 );
155        assertTrue( skipped.get( 0 ).current == b1Node );
156        assertTrue( skipped.get( 1 ).current == c1Node );
157    }
158
159
160    @Test
161    public void testForceResolution()
162    {
163        // A -> B -> C -> D => 3rd D here will be force-resolved
164        // |--> C -> D => 2nd D will be force-resolved
165        // |--> D => 1st D to resolve
166        DependencyNode aNode = makeDependencyNode( "some-group", "A", "1.0" );
167        DependencyNode bNode = makeDependencyNode( "some-group", "B", "1.0" );
168        DependencyNode cNode = makeDependencyNode( "some-group", "C", "1.0" );
169        DependencyNode dNode = makeDependencyNode( "some-group", "D", "1.0" );
170        DependencyNode c1Node = new DefaultDependencyNode( cNode );
171        DependencyNode d1Node = new DefaultDependencyNode( dNode );
172        DependencyNode d2Node = new DefaultDependencyNode( dNode );
173
174        aNode.setChildren( mutableList( bNode, cNode, dNode ) );
175        bNode.setChildren( mutableList( c1Node ) );
176        c1Node.setChildren( mutableList( d2Node ) );
177        cNode.setChildren( mutableList( d1Node ) );
178        dNode.setChildren( new ArrayList<>() );
179
180        //follow the BFS resolve sequence
181        DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper = DependencyResolutionSkipper.defaultSkipper();
182        assertFalse( skipper.skipResolution( aNode, new ArrayList<>() ) );
183        skipper.cache( aNode, new ArrayList<>() );
184        assertFalse( skipper.skipResolution( bNode, mutableList( aNode ) ) );
185        skipper.cache( bNode, mutableList( aNode ) );
186        assertFalse( skipper.skipResolution( cNode, mutableList( aNode ) ) );
187        skipper.cache( cNode, mutableList( aNode ) );
188        assertFalse( skipper.skipResolution( dNode, mutableList( aNode ) ) );
189        skipper.cache( dNode, mutableList( aNode ) );
190
191        assertFalse( skipper.skipResolution( c1Node, mutableList( aNode, bNode ) ) );
192        skipper.cache( c1Node, mutableList( aNode, bNode ) );
193
194        assertFalse( skipper.skipResolution( d1Node, mutableList( aNode, cNode ) ) );
195        skipper.cache( d1Node, mutableList( aNode, cNode ) );
196
197        assertFalse( skipper.skipResolution( d2Node, mutableList( aNode, bNode, c1Node ) ) );
198        skipper.cache( d2Node, mutableList( aNode, bNode, c1Node ) );
199
200        Map<DependencyNode, DependencyResolutionSkipper.DependencyResolutionResult> results = skipper.getResults();
201        assertEquals( results.size(), 7 );
202
203        List<DependencyResolutionSkipper.DefaultDependencyResolutionSkipper.DependencyResolutionResult> forceResolved =
204                results.values().stream()
205                        .filter( dependencyResolutionResult -> dependencyResolutionResult.forceResolution )
206                        .collect( Collectors.toList() );
207        assertEquals( forceResolved.size(), 3 );
208        assertTrue( forceResolved.get( 0 ).current == c1Node );
209        assertTrue( forceResolved.get( 1 ).current == d1Node );
210        assertTrue( forceResolved.get( 2 ).current == d2Node );
211    }
212
213}