1414package org .eclipse .pde .internal .launching ;
1515
1616import java .util .ArrayList ;
17+ import java .util .Arrays ;
1718import java .util .Collection ;
19+ import java .util .IdentityHashMap ;
1820import java .util .LinkedHashSet ;
1921import java .util .List ;
2022import java .util .Map ;
23+ import java .util .Objects ;
2124import java .util .Optional ;
2225import java .util .Set ;
2326import java .util .function .Function ;
27+ import java .util .stream .Collectors ;
2428import java .util .stream .Stream ;
2529
2630import org .eclipse .core .runtime .CoreException ;
2731import org .eclipse .core .runtime .Status ;
2832import org .eclipse .debug .core .ILaunchConfiguration ;
2933import org .eclipse .osgi .service .resolver .BundleDescription ;
34+ import org .eclipse .osgi .service .resolver .ResolverError ;
35+ import org .eclipse .osgi .service .resolver .State ;
36+ import org .eclipse .osgi .service .resolver .StateObjectFactory ;
3037import org .eclipse .osgi .util .NLS ;
3138import org .eclipse .pde .core .plugin .IPluginModelBase ;
3239import org .eclipse .pde .core .plugin .PluginRegistry ;
40+ import org .eclipse .pde .internal .build .BundleHelper ;
3341import org .eclipse .pde .internal .core .DependencyManager ;
3442import org .eclipse .pde .internal .core .PDECore ;
3543import org .eclipse .pde .internal .launching .launcher .BundleLauncherHelper ;
44+ import org .osgi .framework .hooks .resolver .ResolverHook ;
45+ import org .osgi .framework .hooks .resolver .ResolverHookFactory ;
46+ import org .osgi .framework .wiring .BundleCapability ;
47+ import org .osgi .framework .wiring .BundleRequirement ;
3648import org .osgi .framework .wiring .BundleRevision ;
3749
3850public class JUnitLaunchRequirements {
3951
52+ private static final StateObjectFactory FACTORY = BundleHelper .getPlatformAdmin ().getFactory ();
4053 private static final String PDE_JUNIT_RUNTIME = "org.eclipse.pde.junit.runtime" ; //$NON-NLS-1$
4154 private static final String JUNIT4_JDT_RUNTIME_PLUGIN = "org.eclipse.jdt.junit4.runtime" ; //$NON-NLS-1$
4255 private static final String JUNIT5_JDT_RUNTIME_PLUGIN = "org.eclipse.jdt.junit5.runtime" ; //$NON-NLS-1$
4356
4457 public static void addRequiredJunitRuntimePlugins (ILaunchConfiguration configuration , Map <String , List <IPluginModelBase >> collectedModels , Map <IPluginModelBase , String > startLevelMap ) throws CoreException {
45- Collection <String > runtimePlugins = getRequiredJunitRuntimeEclipsePlugins (configuration );
46- Set <BundleDescription > addedRuntimeBundles = addAbsentRequirements (runtimePlugins , collectedModels , startLevelMap );
47- Set <BundleDescription > runtimeRequirements = DependencyManager .findRequirementsClosure (addedRuntimeBundles );
58+ Collection <IPluginModelBase > runtimeBundles = getEclipseJunitRuntimePlugins (configuration , collectedModels , startLevelMap );
59+ List <BundleDescription > roots = runtimeBundles .stream ().map (p -> p .getBundleDescription ()).filter (Objects ::nonNull ).toList ();
60+ Set <BundleDescription > closure = DependencyManager .findRequirementsClosure (roots );
61+ Collection <BundleDescription > runtimeRequirements = filterRequirementsByState (closure , runtimeBundles , configuration );
4862 addAbsentRequirements (runtimeRequirements , collectedModels , startLevelMap );
4963 }
5064
65+ private static Collection <BundleDescription > filterRequirementsByState (Collection <BundleDescription > bundles , Collection <IPluginModelBase > rootBundles , ILaunchConfiguration configuration ) throws CoreException {
66+ //lookup that maps a copy to the original description from the bundles parameter
67+ Map <BundleRevision , BundleDescription > descriptionnMap = new IdentityHashMap <>();
68+ Set <BundleRevision > rootSet = rootBundles .stream ().map (p -> p .getBundleDescription ()).filter (Objects ::nonNull ).collect (Collectors .toSet ());
69+ State state = FACTORY .createState (true );
70+ State targetState = PDECore .getDefault ().getModelManager ().getState ().getState ();
71+ List <BundleDescription > resolveRoots = new ArrayList <>();
72+ long id = 1 ;
73+ for (BundleDescription bundle : bundles ) {
74+ BundleDescription copy = FACTORY .createBundleDescription (id ++, bundle );
75+ descriptionnMap .put (copy , bundle );
76+ state .addBundle (copy );
77+ if (rootSet .contains (bundle )) {
78+ resolveRoots .add (copy );
79+ }
80+ }
81+ state .setPlatformProperties (targetState .getPlatformProperties ());
82+ state .setResolverHookFactory (new ResolverHookFactory () {
83+
84+ @ Override
85+ public ResolverHook begin (Collection <BundleRevision > triggers ) {
86+ return new ResolverHook () {
87+
88+ @ Override
89+ public void filterSingletonCollisions (BundleCapability singleton , Collection <BundleCapability > collisionCandidates ) {
90+ }
91+
92+ @ Override
93+ public void filterResolvable (Collection <BundleRevision > candidates ) {
94+ }
95+
96+ @ Override
97+ public void filterMatches (BundleRequirement requirement , Collection <BundleCapability > candidates ) {
98+ List <BundleCapability > list = candidates .stream ().filter (cp -> isFromDifferentState (cp )).toList ();
99+ if (list .isEmpty ()) {
100+ //nothing to do here...
101+ return ;
102+ }
103+ //iterate in reverse order so we remove lower ranked candidates first ...
104+ for (int i = list .size () - 1 ; i >= 0 && candidates .size () > 1 ; i --) {
105+ BundleCapability capability = list .get (i );
106+ candidates .remove (capability );
107+ }
108+ }
109+
110+ private boolean isFromDifferentState (BundleCapability capability ) {
111+ BundleRevision resource = capability .getResource ();
112+ BundleDescription original = descriptionnMap .get (resource );
113+ if (original != null ) {
114+ return original .getContainingState () != targetState ;
115+ }
116+ return false ;
117+ }
118+
119+ @ Override
120+ public void end () {
121+ }
122+ };
123+ }
124+ });
125+ state .resolve (false );
126+ for (BundleDescription rootBundle : resolveRoots ) {
127+ ResolverError [] errors = state .getResolverErrors (rootBundle );
128+ if (errors .length > 0 ) {
129+ throw new CoreException (Status .error (String .format ("%s can not be resolved: %s" , rootBundle , Arrays .toString (errors )))); //$NON-NLS-1$
130+ }
131+ }
132+ Collection <BundleDescription > closure = DependencyManager .findRequirementsClosure (resolveRoots );
133+ // map back to the originals!
134+ return closure .stream ().map (bd -> descriptionnMap .get (bd )).filter (Objects ::nonNull ).toList ();
135+ }
136+
51137 @ SuppressWarnings ("restriction" )
52138 public static Collection <String > getRequiredJunitRuntimeEclipsePlugins (ILaunchConfiguration configuration ) {
53139 org .eclipse .jdt .internal .junit .launcher .ITestKind testKind = org .eclipse .jdt .internal .junit .launcher .JUnitLaunchConfigurationConstants .getTestRunnerKind (configuration );
@@ -59,7 +145,7 @@ public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchCo
59145 return List .of (PDE_JUNIT_RUNTIME );
60146 } // Nothing to add for JUnit-3
61147 case org .eclipse .jdt .internal .junit .launcher .TestKindRegistry .JUNIT4_TEST_KIND_ID -> {
62- return List .of (PDE_JUNIT_RUNTIME ,JUNIT4_JDT_RUNTIME_PLUGIN );
148+ return List .of (PDE_JUNIT_RUNTIME , JUNIT4_JDT_RUNTIME_PLUGIN );
63149 }
64150 case org .eclipse .jdt .internal .junit .launcher .TestKindRegistry .JUNIT5_TEST_KIND_ID -> {
65151 return List .of (PDE_JUNIT_RUNTIME , JUNIT5_JDT_RUNTIME_PLUGIN );
@@ -68,21 +154,27 @@ public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchCo
68154 }
69155 }
70156
71- private static Set <BundleDescription > addAbsentRequirements (Collection <String > requirements , Map <String , List <IPluginModelBase >> collectedModels , Map <IPluginModelBase , String > startLevelMap ) throws CoreException {
72- Set <BundleDescription > addedRequirements = new LinkedHashSet <>();
73- for (String id : requirements ) {
74- List <IPluginModelBase > models = collectedModels .computeIfAbsent (id , k -> new ArrayList <>());
75- if (models .stream ().noneMatch (p -> p .getBundleDescription ().isResolved ())) {
76- IPluginModelBase model = findRequiredPluginInTargetOrHost (PluginRegistry .findModel (id ), plugins -> plugins .max (PDECore .VERSION ), id );
77- models .add (model );
78- BundleLauncherHelper .addDefaultStartingBundle (startLevelMap , model );
79- addedRequirements .add (model .getBundleDescription ());
80- }
157+ private static Collection <IPluginModelBase > getEclipseJunitRuntimePlugins (ILaunchConfiguration configuration , Map <String , List <IPluginModelBase >> collectedModels , Map <IPluginModelBase , String > startLevelMap ) throws CoreException {
158+ Set <IPluginModelBase > descriptions = new LinkedHashSet <>();
159+ for (String id : getRequiredJunitRuntimeEclipsePlugins (configuration )) {
160+ addIfAbsent (id , collectedModels , startLevelMap ).ifPresent (descriptions ::add );
81161 }
82- return addedRequirements ;
162+ return descriptions ;
163+ }
164+
165+ private static Optional <IPluginModelBase > addIfAbsent (String id , Map <String , List <IPluginModelBase >> collectedModels , Map <IPluginModelBase , String > startLevelMap ) throws CoreException {
166+ List <IPluginModelBase > models = collectedModels .computeIfAbsent (id , k -> new ArrayList <>());
167+ if (models .stream ().noneMatch (m -> m .getBundleDescription ().isResolved ())) {
168+ IPluginModelBase model = findRequiredPluginInTargetOrHost (PluginRegistry .findModel (id ), plugins -> plugins .max (PDECore .VERSION ), id );
169+ models .add (model );
170+ BundleLauncherHelper .addDefaultStartingBundle (startLevelMap , model );
171+ return Optional .of (model );
172+ }
173+
174+ return models .stream ().filter (m -> m .getBundleDescription ().isResolved ()).findFirst ();
83175 }
84176
85- private static void addAbsentRequirements (Set <BundleDescription > requirements , Map <String , List <IPluginModelBase >> collectedModels , Map <IPluginModelBase , String > startLevelMap ) throws CoreException {
177+ private static void addAbsentRequirements (Collection <BundleDescription > requirements , Map <String , List <IPluginModelBase >> collectedModels , Map <IPluginModelBase , String > startLevelMap ) throws CoreException {
86178 for (BundleRevision bundle : requirements ) {
87179 String id = bundle .getSymbolicName ();
88180 List <IPluginModelBase > models = collectedModels .computeIfAbsent (id , k -> new ArrayList <>());
0 commit comments