Bug 563564 - Skip remediation page in case of EE incompatibility

Do not allow remediation in that case, since remediation has to be done
on user environment; and currently remediation page doesn't report
issues with EE.
In case of EE incompatibility, directly show the error.

Change-Id: I03be37b2d54dd14bb6751484142112f3d786d07b
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF
index 6a22965..8998c04 100644
--- a/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF
@@ -2,14 +2,14 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.equinox.p2.tests.ui
-Bundle-Version: 1.2.400.qualifier
+Bundle-Version: 1.2.500.qualifier
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.4.100",
  org.eclipse.ui;bundle-version="3.6.0",
  org.eclipse.equinox.p2.metadata;bundle-version="1.0.0",
  org.eclipse.equinox.p2.metadata.repository;bundle-version="1.0.100",
- org.eclipse.equinox.p2.ui;bundle-version="1.0.100",
+ org.eclipse.equinox.p2.ui;bundle-version="2.6.0",
  org.eclipse.core.tests.harness;bundle-version="3.4.0",
  org.eclipse.equinox.p2.core;bundle-version="1.0.100",
  org.eclipse.equinox.p2.engine;bundle-version="1.0.100",
diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml b/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml
index 7ac5702..1aeb1fb 100644
--- a/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml
+++ b/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml
@@ -21,7 +21,7 @@
 
 	<groupId>org.eclipse.equinox</groupId>
 	<artifactId>org.eclipse.equinox.p2.tests.ui</artifactId>
-	<version>1.2.400-SNAPSHOT</version>
+	<version>1.2.500-SNAPSHOT</version>
 	<packaging>eclipse-test-plugin</packaging>
 
 	<properties>
diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/AllTests.java b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/AllTests.java
index fc5afec..116f1df 100644
--- a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/AllTests.java
+++ b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/AllTests.java
@@ -22,7 +22,8 @@
 @RunWith(Suite.class)
 @Suite.SuiteClasses({ InstallWizardTest.class, InstalledSoftwarePageTest.class, InstallWithRemediationTest.class,
 		InstallationHistoryPageTest.class, UpdateWizardTest.class, UninstallWizardTest.class,
-		RepositoryManipulationPageTest.class, IUPropertyPagesTest.class, PreferencePagesTest.class })
+		RepositoryManipulationPageTest.class, IUPropertyPagesTest.class, PreferencePagesTest.class,
+		EECompatibilityTest.class })
 public class AllTests {
 	// test suite
 }
diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/EECompatibilityTest.java b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/EECompatibilityTest.java
new file mode 100644
index 0000000..e0e8e44
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/EECompatibilityTest.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ *  Copyright (c) 2020 Red Hat Inc., and others.
+ *
+ *  This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License 2.0
+ *  which accompanies this distribution, and is available at
+ *  https://www.eclipse.org/legal/epl-2.0/
+ *
+ *  SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.equinox.p2.tests.ui.dialogs;
+
+import static org.junit.Assume.assumeTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.equinox.internal.p2.metadata.ProvidedCapability;
+import org.eclipse.equinox.internal.p2.ui.ProvUI;
+import org.eclipse.equinox.internal.p2.ui.dialogs.*;
+import org.eclipse.equinox.p2.engine.IProfile;
+import org.eclipse.equinox.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
+import org.eclipse.equinox.p2.operations.InstallOperation;
+import org.eclipse.swt.widgets.Shell;
+import org.junit.*;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+@RunWith(BlockJUnit4ClassRunner.class)
+public class EECompatibilityTest extends WizardTest {
+
+	private static final int latestKnownJavaVersion = 14;
+
+	private final class ProvisioningWizardDialogExtension extends ProvisioningWizardDialog {
+		private ProvisioningWizardDialogExtension(Shell parent, ProvisioningOperationWizard wizard) {
+			super(parent, wizard);
+		}
+
+		@Override
+		public void nextPressed() {
+			super.nextPressed();
+		}
+	}
+
+	private boolean previewCheckJREValue = false;
+	private IInstallableUnit iuRequiringTooRecentEE;
+
+	@Before
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		addAJREJavaSEUnit(getProfile(TESTPROFILE));
+		previewCheckJREValue = getPolicy().getCheckAgainstCurrentExecutionEnvironment();
+		getPolicy().setCheckAgainstCurrentExecutionEnvironment(true);
+		this.iuRequiringTooRecentEE = iuRequiringTooRecentEE();
+		assumeTrue("Tests are skipped on latest known Java version", Integer
+				.parseInt(System.getProperty("java.version").split("\\.")[0].split("-")[0]) < latestKnownJavaVersion);
+	}
+
+	@After
+	@Override
+	public void tearDown() throws Exception {
+		super.tearDown();
+		getPolicy().setCheckAgainstCurrentExecutionEnvironment(previewCheckJREValue);
+	}
+
+	@Test
+	public void testSingleIUPreventInstallation() {
+		PreselectedIUInstallWizard wizard = new PreselectedIUInstallWizard(getProvisioningUI(), null,
+				Collections.singletonList(iuRequiringTooRecentEE), null);
+		wizard.setBypassLicensePage(true);
+		ProvisioningWizardDialogExtension dialog = new ProvisioningWizardDialogExtension(ProvUI.getDefaultParentShell(),
+				wizard);
+		dialog.setBlockOnOpen(false);
+		dialog.open();
+		dialog.nextPressed();
+		IResolutionErrorReportingPage page = (IResolutionErrorReportingPage) dialog.getCurrentPage();
+		page.getMessage();
+		assertFalse(wizard.canFinish());
+	}
+
+	@Test
+	@Ignore(value = "This test is not relevant as it doesn't build an interesting remediation. Feel free to improve!")
+	public void testEEIssueSkipsRemediation() {
+		IInstallableUnit unsatisfiableUnit = unsatisfiableUnit();
+		IInstallableUnit unsatisfiedFeature = createFeature(unsatisfiableUnit);
+		createTestMetdataRepository(new IInstallableUnit[] { unsatisfiableUnit, unsatisfiedFeature });
+		IInstallableUnit[] units = new IInstallableUnit[] { iuRequiringTooRecentEE, createFeature(unsatisfiedFeature) };
+		PreselectedIUInstallWizard wizard = new PreselectedIUInstallWizard(getProvisioningUI(), null,
+				Arrays.asList(units), null);
+		wizard.setBypassLicensePage(true);
+		ProvisioningWizardDialogExtension dialog = new ProvisioningWizardDialogExtension(ProvUI.getDefaultParentShell(),
+				wizard);
+		dialog.setBlockOnOpen(false);
+		dialog.open();
+		dialog.nextPressed(); // to remediation page
+		assertTrue(dialog.getCurrentPage() instanceof RemediationPage);
+		// Here, we'd like to have remediation pre-selecting "Keep my installation the
+		// same and modify the items being installed to be compatible" and keeping the
+		// iuRequiringTooRecentEE as part of the plan.
+		// but this is not happening with current test scenario, so test it ignored so
+		// far.
+		assertFalse(wizard.canFinish());
+		dialog.nextPressed();
+		assertFalse(wizard.canFinish());
+		dialog.close();
+	}
+
+	private IInstallableUnit unsatisfiableUnit() {
+		InstallableUnitDescription desc = new InstallableUnitDescription();
+		desc.setId("unsatisfiable-unit"); //$NON-NLS-1$
+		desc.setVersion(Version.create("0.0.1"));
+		desc.addProvidedCapabilities(Collections.singletonList(
+				new ProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, desc.getId(), desc.getVersion())));
+		desc.addRequirements(Collections.singletonList(MetadataFactory.createRequirement(
+				IInstallableUnit.NAMESPACE_IU_ID, "blah", VersionRange.create("1.0.0"), null, false, true)));
+		return MetadataFactory.createInstallableUnit(desc);
+	}
+
+	private static IInstallableUnit iuRequiringTooRecentEE() {
+		InstallableUnitDescription desc = new InstallableUnitDescription();
+		desc.setId("dummyIU"); //$NON-NLS-1$
+		desc.setVersion(Version.create("0.0.1"));
+		desc.addProvidedCapabilities(Collections.singletonList(
+				new ProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, desc.getId(), desc.getVersion())));
+		desc.addRequirements(Collections.singletonList(MetadataFactory.createRequirement("osgi.ee", "JavaSE",
+				VersionRange.create(latestKnownJavaVersion + ".0.0"), null, false, true)));
+		return MetadataFactory.createInstallableUnit(desc);
+	}
+
+	private static IInstallableUnit createFeature(IInstallableUnit... included) {
+		InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
+		iu.setId("dummyFeatureIncluding"
+				+ Stream.of(included).map(IInstallableUnit::getId).collect(Collectors.joining()) + ".feature.group");
+		iu.setVersion(Version.create("0.0.1"));
+		iu.addProvidedCapabilities(Collections.singleton(MetadataFactory
+				.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, iu.getId(), iu.getVersion())));
+		iu.addRequirements(Stream.of(included)
+				.map(unit -> MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, unit.getId(),
+						new VersionRange(unit.getVersion(), true, iu.getVersion(), true), null, false, false))
+				.collect(Collectors.toList()));
+		iu.setProperty(InstallableUnitDescription.PROP_TYPE_GROUP, Boolean.TRUE.toString());
+		return MetadataFactory.createInstallableUnit(iu);
+	}
+
+	private void addAJREJavaSEUnit(IProfile profile) {
+		InstallOperation installOperation = new InstallOperation(getSession(),
+				Collections.singletonList(aJREJavaSE(latestKnownJavaVersion)));
+		installOperation.setProfileId(TESTPROFILE);
+		installOperation.resolveModal(new NullProgressMonitor());
+		installOperation.getProvisioningJob(new NullProgressMonitor()).run(new NullProgressMonitor());
+	}
+
+	private IInstallableUnit aJREJavaSE(int javaSEVersion) {
+		InstallableUnitDescription desc = new InstallableUnitDescription();
+		desc.setId("a.jre.javase"); //$NON-NLS-1$
+		desc.setVersion(Version.create(javaSEVersion + ".0.0"));
+		desc.addProvidedCapabilities(Arrays.asList(new ProvidedCapability[] {
+				new ProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, desc.getId(), desc.getVersion()),
+				new ProvidedCapability("osgi.ee", "JavaSE", desc.getVersion()) }));
+		return MetadataFactory.createInstallableUnit(desc);
+	}
+}
diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java
index cc2c367..7147955 100644
--- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java
+++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java
@@ -18,6 +18,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashSet;
+import java.util.stream.Stream;
 import org.eclipse.core.runtime.*;
 import org.eclipse.core.runtime.jobs.*;
 import org.eclipse.equinox.internal.p2.ui.*;
@@ -69,12 +70,6 @@
 		setNeedsProgressMonitor(true);
 		if (operation != null) {
 			provisioningContext = operation.getProvisioningContext();
-			if (operation.hasResolved() && getPolicy().getCheckAgainstCurrentExecutionEnvironment()) {
-				this.localJRECheckPlan = ProvUI.toCompabilityWithCurrentJREProvisioningPlan(operation, null);
-				if (!localJRECheckPlan.getStatus().isOK()) {
-					couldNotResolveStatus = localJRECheckPlan.getStatus();
-				}
-			}
 		}
 	}
 
@@ -136,6 +131,13 @@
 				getContainer().run(true, true, monitor -> {
 					remediationOperation.setCurrentRemedy(remediationPage.getRemediationGroup().getCurrentRemedy());
 					remediationOperation.resolveModal(monitor);
+					if (getPolicy().getCheckAgainstCurrentExecutionEnvironment()) {
+						this.localJRECheckPlan = ProvUI
+								.toCompabilityWithCurrentJREProvisioningPlan(remediationOperation, monitor);
+						if (!compatibleWithCurrentEE()) {
+							couldNotResolveStatus = localJRECheckPlan.getStatus();
+						}
+					}
 				});
 			} catch (InterruptedException e) {
 				// Nothing to report if thread was interrupted
@@ -147,6 +149,9 @@
 			initializeResolutionModelElements(ElementUtils.requestToElement(
 					((RemediationOperation) operation).getCurrentRemedy(), !(this instanceof UpdateWizard)));
 			planChanged();
+			if (getPolicy().getCheckAgainstCurrentExecutionEnvironment() && !compatibleWithCurrentEE()) {
+				return errorPage;
+			}
 			return resolutionPage;
 		} else if (page == mainPage || page == errorPage) {
 			ISelectableIUsPage currentPage = (ISelectableIUsPage) page;
@@ -160,8 +165,8 @@
 				initializeResolutionModelElements(planSelections);
 			}
 			IStatus status = operation.getResolutionResult();
-			if (status.getSeverity() != IStatus.ERROR && localJRECheckPlan != null
-					&& !localJRECheckPlan.getStatus().isOK()) {
+			if (getPolicy().getCheckAgainstCurrentExecutionEnvironment() && !compatibleWithCurrentEE()) {
+				// skip remediation for EE compatibility issues
 				return errorPage;
 			}
 			if (status == null || status.getSeverity() == IStatus.ERROR) {
@@ -189,6 +194,40 @@
 		return super.getNextPage(page);
 	}
 
+	private boolean compatibleWithCurrentEE() {
+		if (operation == null || !getPolicy().getCheckAgainstCurrentExecutionEnvironment()) {
+			return true;
+		}
+		if (localJRECheckPlan == null) {
+			try {
+				getContainer().run(true, true, monitor -> {
+					if (!operation.hasResolved()) {
+						operation.resolveModal(monitor);
+					}
+					if (operation.hasResolved()) {
+						this.localJRECheckPlan = ProvUI.toCompabilityWithCurrentJREProvisioningPlan(operation, null);
+						if (!compatibleWithCurrentEE()) {
+							couldNotResolveStatus = localJRECheckPlan.getStatus();
+						}
+					}
+				});
+			} catch (InvocationTargetException | InterruptedException e) {
+				return false;
+			}
+		}
+		IStatus currentEEPlanStatus = localJRECheckPlan.getStatus();
+		if (currentEEPlanStatus.getSeverity() != IStatus.ERROR) {
+			return true;
+		}
+		return Stream.of(currentEEPlanStatus).filter(status -> status.getSeverity() == IStatus.ERROR)
+				.flatMap(status -> status.isMultiStatus() ? Stream.of(status.getChildren()) : Stream.of(status))
+				.filter(status -> status.getSeverity() == IStatus.ERROR)
+				.flatMap(status -> status.isMultiStatus() ? Stream.of(status.getChildren()) : Stream.of(status))
+				.filter(status -> status.getSeverity() == IStatus.ERROR)
+				.flatMap(status -> status.isMultiStatus() ? Stream.of(status.getChildren()) : Stream.of(status))
+				.map(IStatus::getMessage).noneMatch(message -> message.contains("osgi.ee")); //$NON-NLS-1$
+	}
+
 	/**
 	 * The selections that drive the provisioning operation have changed. We might
 	 * need to change the completion state of the resolution page.
@@ -300,7 +339,7 @@
 					operation.resolveModal(monitor);
 					if (getPolicy().getCheckAgainstCurrentExecutionEnvironment()) {
 						this.localJRECheckPlan = ProvUI.toCompabilityWithCurrentJREProvisioningPlan(operation, monitor);
-						if (!localJRECheckPlan.getStatus().isOK()) {
+						if (!compatibleWithCurrentEE()) {
 							couldNotResolveStatus = localJRECheckPlan.getStatus();
 						}
 					}
@@ -348,10 +387,9 @@
 		if (statusOverridesOperation())
 			return couldNotResolveStatus;
 		if (operation != null && operation.getResolutionResult() != null) {
-			if (!operation.getResolutionResult().isOK() || localJRECheckPlan == null
-					|| localJRECheckPlan.getStatus().isOK()) {
+			if (!operation.getResolutionResult().isOK() || localJRECheckPlan == null || compatibleWithCurrentEE()) {
 				return operation.getResolutionResult();
-			} else if (localJRECheckPlan != null) {
+			} else if (!compatibleWithCurrentEE()) {
 				return localJRECheckPlan.getStatus();
 			}
 		}
@@ -460,7 +498,7 @@
 	public boolean statusOverridesOperation() {
 		return operation != null && operation.getResolutionResult() != null
 				&& operation.getResolutionResult().getSeverity() < IStatus.ERROR
-				&& (localJRECheckPlan != null && !localJRECheckPlan.getStatus().isOK());
+				&& (localJRECheckPlan != null && !compatibleWithCurrentEE());
 	}
 
 	public void setRelaxedResolution(boolean value) {