From 1eedc6c25354a0eeb32b8f76694845646bbc7227 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 6 Apr 2016 12:15:39 +0200 Subject: [PATCH] [platform] IDE update strategy reworked - the concept of a 'new channel' dropped; no confusing "new channel" dialog anymore (IDEA-144121) - updates are not restricted by a major version (IDEA-153386) - ability added to target updates for specific builds on a server side - UI tweaks (stable channel decription changed; ignored builds editing added) --- .../UpdateStrategyCustomization.java | 11 +- .../impl/AbstractUpdateDialog.java | 22 -- .../updateSettings/impl/ChannelStatus.java | 6 +- .../impl/CheckForUpdateResult.java | 64 ++--- .../updateSettings/impl/NewChannelDialog.java | 101 -------- .../updateSettings/impl/NewChannelForm.form | 40 --- .../updateSettings/impl/NoUpdatesDialog.java | 6 +- .../impl/PluginUpdateInfoDialog.java | 2 +- .../updateSettings/impl/UpdateChecker.kt | 71 +---- .../openapi/updateSettings/impl/UpdateInfo.kt | 86 ++---- .../updateSettings/impl/UpdateInfoDialog.java | 54 ++-- .../updateSettings/impl/UpdateSettings.java | 18 +- .../impl/UpdateSettingsConfigurable.java | 26 +- .../updateSettings/impl/UpdateStrategy.java | 125 --------- .../updateSettings/impl/UpdateStrategy.kt | 77 ++++++ .../impl/UpdatesSettingsPanel.form | 142 +++++----- .../impl/UserUpdateSettings.java | 15 +- .../src/messages/IdeBundle.properties | 9 +- .../com/intellij/ide/updates/InfoReader.java | 40 --- .../ide/updates/UpdateInfoParsingTest.kt | 110 ++++++++ .../ide/updates/UpdateStrategyTest.java | 244 ------------------ .../ide/updates/UpdateStrategyTest.kt | 167 ++++++++++++ .../ide/updates/UpdatesInfoParserTest.java | 100 ------- .../com/intellij/ide/updates/current.xml | 188 -------------- .../intellij/ide/updates/emptyChannels.xml | 6 - .../com/intellij/ide/updates/idea-123280.xml | 32 --- .../com/intellij/ide/updates/idea-2eap.xml | 20 -- .../com/intellij/ide/updates/idea-new9eap.xml | 36 --- .../ide/updates/idea-newChannel-release.xml | 39 --- .../intellij/ide/updates/idea-newChannel.xml | 39 --- .../intellij/ide/updates/idea-newbuild.xml | 34 --- .../ide/updates/idea-patchAvailable.xml | 37 --- .../com/intellij/ide/updates/idea-same.xml | 36 --- .../intellij/ide/updates/oneProductOnly.xml | 36 --- 34 files changed, 582 insertions(+), 1457 deletions(-) delete mode 100644 platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelDialog.java delete mode 100644 platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelForm.form delete mode 100644 platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/InfoReader.java create mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateInfoParsingTest.kt delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.java create mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/UpdatesInfoParserTest.java delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/current.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/emptyChannels.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-123280.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-2eap.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-new9eap.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-newChannel-release.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-newChannel.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-newbuild.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-patchAvailable.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/idea-same.xml delete mode 100644 platform/platform-tests/testSrc/com/intellij/ide/updates/oneProductOnly.xml diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/UpdateStrategyCustomization.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/UpdateStrategyCustomization.java index f54ad660e3b1..f30334bfe74b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/UpdateStrategyCustomization.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/UpdateStrategyCustomization.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,11 +28,4 @@ public class UpdateStrategyCustomization { public boolean forceEapUpdateChannelForEapBuilds() { return true; } - - /** - * Whether the updater will allow patch updates to cross major version boundaries. - */ - public boolean allowMajorVersionUpdate() { - return false; - } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java index fe27df9d859c..a719954e0920 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java @@ -23,8 +23,6 @@ import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.ui.ColorUtil; import com.intellij.ui.IdeBorderFactory; -import com.intellij.ui.LicensingFacade; -import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,7 +31,6 @@ import javax.swing.*; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import java.awt.*; -import java.util.Date; /** * @author anna @@ -76,25 +73,6 @@ public abstract class AbstractUpdateDialog extends DialogWrapper { app.invokeLater(() -> app.restart(true)); } - protected void initLicensingInfo(@NotNull UpdateChannel channel, @SuppressWarnings("UnusedParameters") @NotNull BuildInfo build) { - LicensingFacade facade = LicensingFacade.getInstance(); - if (facade != null) { - if (channel.getLicensing().equals(UpdateChannel.LICENSING_EAP)) { - myLicenseInfo = IdeBundle.message("updates.channel.bundled.key"); - } - else { - Date buildDate = build.getReleaseDate(); - Date expiration = facade.getLicenseExpirationDate(); - if (buildDate != null && facade.isPerpetualForProduct(buildDate)) { - myLicenseInfo = IdeBundle.message("updates.fallback.build"); - } - else if (expiration != null && expiration.after(new Date())) { - myLicenseInfo = IdeBundle.message("updates.subscription.active.till", DateFormatUtil.formatAboutDialogDate(expiration)); - } - } - } - } - protected void configureMessageArea(@NotNull JEditorPane area) { String messageBody = myEnableLink ? IdeBundle.message("updates.configure.label", ShowSettingsUtil.getSettingsMenuName()) : ""; configureMessageArea(area, messageBody, null, null); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/ChannelStatus.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/ChannelStatus.java index 08ba80e188f2..4948412116c6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/ChannelStatus.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/ChannelStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ public enum ChannelStatus { EAP("eap", "Early Access Program"), MILESTONE("milestone", "Milestone Releases"), BETA("beta", "Beta Releases or Public Previews"), - RELEASE("release", "New Major Version Releases"); + RELEASE("release", "Stable Releases"); private final String myCode; private final String myDisplayName; @@ -53,4 +53,4 @@ public enum ChannelStatus { public String toString() { return myDisplayName; } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateResult.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateResult.java index 844fb6f6b6e5..369657b85c93 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateResult.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.intellij.openapi.updateSettings.impl; +import com.intellij.openapi.util.BuildNumber; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,61 +23,48 @@ import java.util.Collections; import java.util.List; public class CheckForUpdateResult { - private final BuildInfo myNewBuildInSelectedChannel; - private final UpdateChannel myUpdatedChannel; - private final UpdateChannel myChannelToPropose; - private final List myAllChannelIds; private final UpdateStrategy.State myState; + private final BuildInfo myNewBuild; + private final UpdateChannel myUpdatedChannel; private final Exception myError; - public CheckForUpdateResult(@Nullable BuildInfo newBuildInSelectedChannel, - @Nullable UpdateChannel updated, - @Nullable UpdateChannel channelToPropose, - @NotNull List allChannelsIds) { - myNewBuildInSelectedChannel = newBuildInSelectedChannel; - myUpdatedChannel = updated; - myChannelToPropose = channelToPropose; - myAllChannelIds = allChannelsIds; + public CheckForUpdateResult(@Nullable BuildInfo newBuild, @Nullable UpdateChannel updatedChannel) { myState = UpdateStrategy.State.LOADED; + myNewBuild = newBuild; + myUpdatedChannel = updatedChannel; myError = null; } public CheckForUpdateResult(@NotNull UpdateStrategy.State state, @Nullable Exception e) { - myNewBuildInSelectedChannel = null; - myUpdatedChannel = null; - myChannelToPropose = null; - myAllChannelIds = Collections.emptyList(); myState = state; + myNewBuild = null; + myUpdatedChannel = null; myError = e; } - @Nullable - public BuildInfo getNewBuildInSelectedChannel() { - return myNewBuildInSelectedChannel; - } - - @Nullable - public UpdateChannel getUpdatedChannel() { - return myUpdatedChannel; - } - - @Nullable - public UpdateChannel getChannelToPropose() { - return myChannelToPropose; - } - - @NotNull - public List getAllChannelsIds() { - return myAllChannelIds; - } - @NotNull public UpdateStrategy.State getState() { return myState; } + @Nullable + public BuildInfo getNewBuild() { + return myNewBuild; + } + + @Nullable + public PatchInfo findPatchForBuild(@NotNull BuildNumber build) { + List patches = myNewBuild != null ? myNewBuild.getPatches() : Collections.emptyList(); + return patches.stream().filter(p -> p.isAvailable() && p.getFromBuild().compareTo(build) == 0).findFirst().orElse(null); + } + + @Nullable + public UpdateChannel getUpdatedChannel() { + return myUpdatedChannel; + } + @Nullable public Exception getError() { return myError; } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelDialog.java deleted file mode 100644 index d97a02f78c88..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelDialog.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.updateSettings.impl; - -import com.intellij.ide.BrowserUtil; -import com.intellij.ide.IdeBundle; -import com.intellij.ui.BrowserHyperlinkListener; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.util.Objects; - -/** - * @author yole - */ -class NewChannelDialog extends AbstractUpdateDialog { - private final UpdateChannel myChannel; - private final BuildInfo myLatestBuild; - - public NewChannelDialog(@NotNull UpdateChannel channel) { - super(false); - myChannel = channel; - myLatestBuild = channel.getLatestBuild(); - assert myLatestBuild != null; - - initLicensingInfo(myChannel, myLatestBuild); - - init(); - } - - @Override - protected JComponent createCenterPanel() { - return new NewChannelPanel().myPanel; - } - - @NotNull - @Override - protected Action[] createActions() { - return new Action[]{ - getOKAction(), - - new AbstractAction(IdeBundle.message("updates.remind.later.button")) { - @Override - public void actionPerformed(ActionEvent e) { - UpdateSettings.getInstance().forgetChannelId(myChannel.getId()); - doCancelAction(); - } - }, - - getCancelAction() - }; - } - - @Override - protected String getOkButtonText() { - return IdeBundle.message("updates.more.info.button"); - } - - @Override - protected String getCancelButtonText() { - return IdeBundle.message("updates.ignore.update.button"); - } - - @Override - protected void doOKAction() { - BrowserUtil.browse(Objects.requireNonNull(myChannel.getHomePageUrl(), "missing URL for channel " + myChannel.getId())); - super.doOKAction(); - } - - private class NewChannelPanel { - private JPanel myPanel; - private JEditorPane myMessageArea; - private JEditorPane myLicenseArea; - - private NewChannelPanel() { - String message = IdeBundle.message("updates.channel.name.message", myChannel.getName(), myLatestBuild.getMessage()); - configureMessageArea(myMessageArea, message, null, BrowserHyperlinkListener.INSTANCE); - - if (myLicenseInfo != null) { - configureMessageArea(myLicenseArea, myLicenseInfo, null, BrowserHyperlinkListener.INSTANCE); - } - else { - myLicenseArea.setVisible(false); - } - } - } -} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelForm.form b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelForm.form deleted file mode 100644 index ed73b2a578bc..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NewChannelForm.form +++ /dev/null @@ -1,40 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesDialog.java index fb7f8110f5c4..90c7b315267f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import javax.swing.*; * @author pti */ class NoUpdatesDialog extends AbstractUpdateDialog { - protected NoUpdatesDialog(boolean enableLink) { + NoUpdatesDialog(boolean enableLink) { super(enableLink); init(); } @@ -57,4 +57,4 @@ class NoUpdatesDialog extends AbstractUpdateDialog { configureMessageArea(myMessageArea); } } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoDialog.java index 8f3ee84ef9ce..b89e341d2603 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoDialog.java @@ -38,7 +38,7 @@ class PluginUpdateInfoDialog extends AbstractUpdateDialog { private final Collection myUploadedPlugins; private final boolean myPlatformUpdate; - public PluginUpdateInfoDialog(Collection uploadedPlugins, boolean enableLink) { + PluginUpdateInfoDialog(Collection uploadedPlugins, boolean enableLink) { super(enableLink); myUploadedPlugins = uploadedPlugins; myPlatformUpdate = false; diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt index 972b7a71cda7..5f9847f74923 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt @@ -37,9 +37,7 @@ import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectManager -import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.ui.Messages -import com.intellij.openapi.updateSettings.UpdateStrategyCustomization import com.intellij.openapi.util.ActionCallback import com.intellij.openapi.util.BuildNumber import com.intellij.openapi.util.SystemInfo @@ -56,7 +54,6 @@ import com.intellij.util.ui.UIUtil import com.intellij.xml.util.XmlStringUtil import org.apache.http.client.utils.URIBuilder import org.jdom.JDOMException -import org.jetbrains.annotations.Contract import java.io.File import java.io.IOException import java.net.URISyntaxException @@ -133,9 +130,7 @@ object UpdateChecker { val result = checkPlatformUpdate(updateSettings) if (manualCheck && result.state == UpdateStrategy.State.LOADED) { - val settings = UpdateSettings.getInstance() - settings.saveLastCheckedInfo() - settings.setKnownChannelIds(result.allChannelsIds) + UpdateSettings.getInstance().saveLastCheckedInfo() } else if (result.state == UpdateStrategy.State.CONNECTION_ERROR) { val e = result.error @@ -149,24 +144,16 @@ object UpdateChecker { indicator?.text = IdeBundle.message("updates.checking.plugins") + val buildNumber: BuildNumber? = result.newBuild?.apiVersion + val incompatiblePlugins: MutableCollection? = if (buildNumber != null) HashSet() else null + val updatedPlugins: Collection? - val incompatiblePlugins: MutableCollection? - - if (newChannelReady(result.channelToPropose)) { - updatedPlugins = null - incompatiblePlugins = null + try { + updatedPlugins = checkPluginsUpdate(updateSettings, indicator, incompatiblePlugins, buildNumber) } - else { - val buildNumber: BuildNumber? = result.newBuildInSelectedChannel?.apiVersion - - incompatiblePlugins = if (buildNumber != null) HashSet() else null - try { - updatedPlugins = checkPluginsUpdate(updateSettings, indicator, incompatiblePlugins, buildNumber) - } - catch (e: IOException) { - showErrorMessage(manualCheck, IdeBundle.message("updates.error.connection.failed", e.message)) - return - } + catch (e: IOException) { + showErrorMessage(manualCheck, IdeBundle.message("updates.error.connection.failed", e.message)) + return } // show result @@ -215,10 +202,7 @@ object UpdateChecker { return CheckForUpdateResult(UpdateStrategy.State.NOTHING_LOADED, null) } - val appInfo = ApplicationInfo.getInstance() - val majorVersion = Integer.parseInt(appInfo.majorVersion) - val customization = UpdateStrategyCustomization.getInstance() - val strategy = UpdateStrategy(majorVersion, appInfo.build, updateInfo, settings, customization) + val strategy = UpdateStrategy(ApplicationInfo.getInstance().build, updateInfo, settings) return strategy.checkForUpdates() } @@ -362,11 +346,6 @@ object UpdateChecker { } } - @Contract("null -> false") - private fun newChannelReady(channelToPropose: UpdateChannel?): Boolean { - return channelToPropose?.getLatestBuild() != null - } - private fun showUpdateResult(project: Project?, checkForUpdateResult: CheckForUpdateResult, updateSettings: UpdateSettings, @@ -374,14 +353,14 @@ object UpdateChecker { incompatiblePlugins: Collection?, enableLink: Boolean, alwaysShowResults: Boolean) { - val channelToPropose = checkForUpdateResult.channelToPropose val updatedChannel = checkForUpdateResult.updatedChannel - val latestBuild = checkForUpdateResult.newBuildInSelectedChannel + val newBuild = checkForUpdateResult.newBuild - if (updatedChannel != null && latestBuild != null) { + if (updatedChannel != null && newBuild != null) { val runnable = { + val patch = checkForUpdateResult.findPatchForBuild(ApplicationInfo.getInstance().build) val forceHttps = updateSettings.canUseSecureConnection() - UpdateInfoDialog(updatedChannel, latestBuild, enableLink, forceHttps, updatedPlugins, incompatiblePlugins).show() + UpdateInfoDialog(updatedChannel, newBuild, patch, enableLink, forceHttps, updatedPlugins, incompatiblePlugins).show() } if (alwaysShowResults) { @@ -392,28 +371,6 @@ object UpdateChecker { showNotification(project, message, runnable, NotificationUniqueType.UPDATE_IN_CHANNEL) } } - else if (newChannelReady(channelToPropose)) { - val runnable = { - val dialog = NewChannelDialog(channelToPropose!!) - dialog.show() - // once we informed that new product is available (when new channel was detected), remember the fact - if (dialog.exitCode == DialogWrapper.CANCEL_EXIT_CODE && - checkForUpdateResult.state == UpdateStrategy.State.LOADED && - !updateSettings.knownChannelsIds.contains(channelToPropose.id)) { - val newIds = ArrayList(updateSettings.knownChannelsIds) - newIds.add(channelToPropose.id) - updateSettings.setKnownChannelIds(newIds) - } - } - - if (alwaysShowResults) { - runnable.invoke() - } - else { - val message = IdeBundle.message("updates.new.version.available", ApplicationNamesInfo.getInstance().fullProductName) - showNotification(project, message, runnable, NotificationUniqueType.NEW_CHANNEL) - } - } else if (updatedPlugins != null && !updatedPlugins.isEmpty()) { val runnable = { PluginUpdateInfoDialog(updatedPlugins, enableLink).show() } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfo.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfo.kt index f782fc00376b..2ddef8c41e12 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfo.kt +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfo.kt @@ -15,9 +15,9 @@ */ package com.intellij.openapi.updateSettings.impl -import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.util.BuildNumber +import com.intellij.openapi.util.BuildRange import com.intellij.openapi.util.SystemInfo import org.jdom.Element import org.jdom.JDOMException @@ -27,23 +27,15 @@ import java.util.* class UpdatesInfo(node: Element) { private val products = node.getChildren("product").map { Product(it) } - - val productsCount: Int - get() = products.size - - fun getProduct(code: String): Product? = products.find { it.hasCode(code) } + operator fun get(code: String): Product? = products.find { code in it.codes } } class Product(node: Element) { - val name: String = node.getAttributeValue("name") ?: throw JDOMException("product.name missing") + val name: String = node.getAttributeValue("name") ?: throw JDOMException("product@name missing") + val codes: Set = node.getChildren("code").map { it.value.trim() }.toSet() val channels: List = node.getChildren("channel").map { UpdateChannel(it) } - private val codes = node.getChildren("code").map { it.value.trim() }.toSet() - fun hasCode(code: String): Boolean = codes.contains(code) - - fun findUpdateChannelById(id: String): UpdateChannel? = channels.find { it.id == id } - - fun getAllChannelIds(): List = channels.map { it.id } + override fun toString() = codes.firstOrNull() ?: "-" } class UpdateChannel(node: Element) { @@ -52,74 +44,50 @@ class UpdateChannel(node: Element) { const val LICENSING_PRODUCTION = "production" } - val id: String = node.getAttributeValue("id") ?: throw JDOMException("channel.id missing") - val name: String = node.getAttributeValue("name") ?: throw JDOMException("channel.name missing") + val id: String = node.getAttributeValue("id") ?: throw JDOMException("channel@id missing") val status: ChannelStatus = ChannelStatus.fromCode(node.getAttributeValue("status")) val licensing: String = node.getAttributeValue("licensing", LICENSING_PRODUCTION) - val majorVersion: Int = node.getAttributeValue("majorVersion")?.toInt() ?: -1 val homePageUrl: String? = node.getAttributeValue("url") - val feedbackUrl: String? = node.getAttributeValue("feedback") - val evalDays: Int = node.getAttributeValue("evalDays")?.toInt() ?: 30 - private val builds = node.getChildren("build").map { BuildInfo(it) } + val builds: List = node.getChildren("build").map { BuildInfo(it) } - fun getLatestBuild(): BuildInfo? = latestBuild(builds) - fun getLatestBuild(baseline: Int): BuildInfo? = latestBuild(builds.filter { it.number.baselineVersion == baseline }) - - private fun latestBuild(builds: List) = - builds.fold(null as BuildInfo?) { best, candidate -> if (best == null || best.compareTo(candidate) < 0) candidate else best } + override fun toString() = id } -class BuildInfo(node: Element) : Comparable { - val number: BuildNumber = BuildNumber.fromString(node.getAttributeValue("number") ?: throw JDOMException("build.number missing")) - val apiVersion: BuildNumber = node.getAttributeValue("apiVersion")?.let { BuildNumber.fromString(it, number.productCode) } ?: number +class BuildInfo(node: Element) { + val number: BuildNumber = BuildNumber.fromString(node.getAttributeValue("number") ?: throw JDOMException("build@number missing")) + val apiVersion: BuildNumber = BuildNumber.fromString(node.getAttributeValue("apiVersion"), number.productCode) ?: number val version: String = node.getAttributeValue("version") ?: "" val message: String = node.getChild("message")?.value ?: "" - val releaseDate: Date? = node.getAttributeValue("releaseDate")?.let { - try { SimpleDateFormat("yyyyMMdd", Locale.US).parse(it) } // same as the 'majorReleaseDate' in ApplicationInfo.xml + val releaseDate: Date? = parseDate(node.getAttributeValue("releaseDate")) + val target: BuildRange? = BuildRange.fromStrings(node.getAttributeValue("targetSince"), node.getAttributeValue("targetUntil")) + val buttons: List = node.getChildren("button").map { ButtonInfo(it) } + val patches: List = node.getChildren("patch").map { PatchInfo(it) } + + private fun parseDate(value: String?): Date? = value?.let { + try { + SimpleDateFormat("yyyyMMdd", Locale.US).parse(it) // same as the 'majorReleaseDate' in ApplicationInfo.xml + } catch (e: ParseException) { Logger.getInstance(BuildInfo::class.java).info("Failed to parse build release date " + it) null } } - val buttons: List = node.getChildren("button").map { ButtonInfo(it) } - private val patches = node.getChildren("patch").map { PatchInfo(it) } - /** - * Returns -1 if version information is missing or does not match to expected format "majorVer.minorVer" - */ - val majorVersion: Int - get() { - val dotIndex = version.indexOf('.') - if (dotIndex > 0) { - try { - return version.substring(0, dotIndex).toInt() - } - catch (ignored: NumberFormatException) { } - } - - return -1 - } - - fun findPatchForCurrentBuild(): PatchInfo? = findPatchForBuild(ApplicationInfo.getInstance().build) - - fun findPatchForBuild(currentBuild: BuildNumber): PatchInfo? = - patches.find { it.isAvailable && it.fromBuild.asStringWithoutProductCode() == currentBuild.asStringWithoutProductCode() } - - override fun compareTo(other: BuildInfo): Int = number.compareTo(other.number) - - override fun toString(): String = "BuildInfo(number=$number)" + override fun toString() = "${number}/${version}" } class ButtonInfo(node: Element) { - val name: String = node.getAttributeValue("name") ?: throw JDOMException("button.name missing") - val url: String = node.getAttributeValue("url") ?: throw JDOMException("button.url missing") + val name: String = node.getAttributeValue("name") ?: throw JDOMException("button@name missing") + val url: String = node.getAttributeValue("url") ?: throw JDOMException("button@url missing") val isDownload: Boolean = node.getAttributeValue("download") != null // a button marked with this attribute is hidden when a patch is available + + override fun toString() = name } class PatchInfo(node: Element) { - val fromBuild: BuildNumber = BuildNumber.fromString(node.getAttributeValue("from") ?: throw JDOMException("patch.from missing")) + val fromBuild: BuildNumber = BuildNumber.fromString(node.getAttributeValue("from") ?: throw JDOMException("patch@from missing")) val size: String? = node.getAttributeValue("size") - val isAvailable: Boolean = node.getAttributeValue("exclusions")?.split(",")?.none { it.trim() == osSuffix } ?: true + val isAvailable: Boolean = node.getAttributeValue("exclusions")?.splitToSequence(",")?.none { it.trim() == osSuffix } ?: true val osSuffix: String get() = if (SystemInfo.isWindows) "win" else if (SystemInfo.isMac) "mac" else if (SystemInfo.isUnix) "unix" else "unknown" diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java index 9fd19b71de69..d6aabeb88624 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java @@ -29,16 +29,20 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.BrowserHyperlinkListener; import com.intellij.ui.JBColor; +import com.intellij.ui.LicensingFacade; import com.intellij.ui.components.JBLabel; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.DateFormatUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; import java.io.File; import java.io.IOException; import java.util.Collection; +import java.util.Date; import java.util.List; /** @@ -48,25 +52,26 @@ class UpdateInfoDialog extends AbstractUpdateDialog { private final UpdateChannel myUpdatedChannel; private final boolean myForceHttps; private final Collection myUpdatedPlugins; - private final BuildInfo myLatestBuild; + private final BuildInfo myNewBuild; private final PatchInfo myPatch; private final boolean myWriteProtected; - protected UpdateInfoDialog(@NotNull UpdateChannel channel, - @NotNull BuildInfo latestBuild, - boolean enableLink, - boolean forceHttps, - Collection updatedPlugins, - Collection incompatiblePlugins) { + UpdateInfoDialog(@NotNull UpdateChannel channel, + @NotNull BuildInfo newBuild, + @Nullable PatchInfo patch, + boolean enableLink, + boolean forceHttps, + Collection updatedPlugins, + Collection incompatiblePlugins) { super(enableLink); myUpdatedChannel = channel; myForceHttps = forceHttps; myUpdatedPlugins = updatedPlugins; - myLatestBuild = latestBuild; - myPatch = myLatestBuild.findPatchForCurrentBuild(); + myNewBuild = newBuild; + myPatch = patch; myWriteProtected = myPatch != null && !new File(PathManager.getHomePath()).canWrite(); getCancelAction().putValue(DEFAULT_ACTION, Boolean.TRUE); - initLicensingInfo(myUpdatedChannel, myLatestBuild); + initLicensingInfo(myUpdatedChannel, myNewBuild); init(); if (incompatiblePlugins != null && !incompatiblePlugins.isEmpty()) { @@ -80,6 +85,25 @@ class UpdateInfoDialog extends AbstractUpdateDialog { } } + private void initLicensingInfo(@NotNull UpdateChannel channel, @NotNull BuildInfo build) { + LicensingFacade facade = LicensingFacade.getInstance(); + if (facade != null) { + if (channel.getLicensing().equals(UpdateChannel.LICENSING_EAP)) { + myLicenseInfo = IdeBundle.message("updates.channel.bundled.key"); + } + else { + Date buildDate = build.getReleaseDate(); + Date expiration = facade.getLicenseExpirationDate(); + if (buildDate != null && facade.isPerpetualForProduct(buildDate)) { + myLicenseInfo = IdeBundle.message("updates.fallback.build"); + } + else if (expiration != null && expiration.after(new Date())) { + myLicenseInfo = IdeBundle.message("updates.subscription.active.till", DateFormatUtil.formatAboutDialogDate(expiration)); + } + } + } + } + @Override protected JComponent createCenterPanel() { return new UpdateInfoPanel().myPanel; @@ -105,7 +129,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog { }); } - List buttons = myLatestBuild.getButtons(); + List buttons = myNewBuild.getButtons(); if (buttons.isEmpty()) { actions.add(new AbstractAction(IdeBundle.message("updates.more.info.button")) { @Override @@ -125,7 +149,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog { actions.add(new AbstractAction(IdeBundle.message("updates.ignore.update.button")) { @Override public void actionPerformed(ActionEvent e) { - String build = myLatestBuild.getNumber().asStringWithoutProductCode(); + String build = myNewBuild.getNumber().asStringWithoutProductCode(); UpdateSettings.getInstance().getIgnoredBuildNumbers().add(build); doCancelAction(); } @@ -143,7 +167,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog { private void downloadPatchAndRestart() { try { - UpdateChecker.installPlatformUpdate(myPatch, myLatestBuild.getNumber(), myForceHttps); + UpdateChecker.installPlatformUpdate(myPatch, myNewBuild.getNumber(), myForceHttps); if (myUpdatedPlugins != null && !myUpdatedPlugins.isEmpty()) { new PluginUpdateInfoDialog(getContentPanel(), myUpdatedPlugins).show(); @@ -196,7 +220,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog { ApplicationInfo appInfo = ApplicationInfo.getInstance(); ApplicationNamesInfo appNames = ApplicationNamesInfo.getInstance(); - String message = myLatestBuild.getMessage(); + String message = myNewBuild.getMessage(); final String fullProductName = appNames.getFullProductName(); if (StringUtil.isEmpty(message)) { message = IdeBundle.message("updates.new.version.available", fullProductName); @@ -217,7 +241,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog { appInfo.getBuild().asStringWithoutProductCode() ) ); - myNewVersion.setText(formatVersion(myLatestBuild.getVersion(), myLatestBuild.getNumber().asStringWithoutProductCode())); + myNewVersion.setText(formatVersion(myNewBuild.getVersion(), myNewBuild.getNumber().asStringWithoutProductCode())); if (myPatch != null && !StringUtil.isEmptyOrSpaces(myPatch.getSize())) { myPatchInfo.setText(myPatch.getSize() + " MB"); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettings.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettings.java index d0c8ece653db..0d10d26ed25f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettings.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettings.java @@ -38,7 +38,6 @@ import java.util.List; public class UpdateSettings implements PersistentStateComponent, UserUpdateSettings { public static class State { @CollectionBean public final List pluginHosts = new SmartList(); - @CollectionBean public final List knownUpdateChannels = new SmartList(); @CollectionBean public final List ignoredBuildNumbers = new SmartList(); public boolean CHECK_NEEDED = true; @@ -98,21 +97,6 @@ public class UpdateSettings implements PersistentStateComponent getKnownChannelsIds() { - return new ArrayList(myState.knownUpdateChannels); - } - - @Override - public void setKnownChannelIds(@NotNull List ids) { - myState.knownUpdateChannels.clear(); - myState.knownUpdateChannels.addAll(ids); - } - - public void forgetChannelId(String id) { - myState.knownUpdateChannels.remove(id); - } - @Override public List getIgnoredBuildNumbers() { return myState.ignoredBuildNumbers; @@ -150,6 +134,7 @@ public class UpdateSettings implements PersistentStateComponent /** @deprecated use {@link #getSelectedChannelStatus()} (to be removed in IDEA 17) */ @SuppressWarnings("unused") public String getUpdateChannelType() { @@ -161,4 +146,5 @@ public class UpdateSettings implements PersistentStateComponent } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettingsConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettingsConfigurable.java index d4e7da861d61..54b854054c55 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettingsConfigurable.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateSettingsConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package com.intellij.openapi.updateSettings.impl; import com.intellij.ide.DataManager; import com.intellij.ide.IdeBundle; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; @@ -26,11 +28,14 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.updateSettings.UpdateStrategyCustomization; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.CollectionComboBoxModel; import com.intellij.ui.JBColor; import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.labels.ActionLink; import com.intellij.util.net.NetUtils; import com.intellij.util.text.DateFormatUtil; import org.jetbrains.annotations.NotNull; @@ -40,6 +45,7 @@ import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Arrays; +import java.util.List; /** * @author pti @@ -146,13 +152,14 @@ public class UpdateSettingsConfigurable extends BaseConfigurable implements Sear private final UpdateSettings mySettings; private JPanel myPanel; private JCheckBox myCheckForUpdates; - private JComboBox myUpdateChannels; + private JComboBox myUpdateChannels; private JButton myCheckNow; private JBLabel myChannelWarning; private JCheckBox myUseSecureConnection; private JLabel myBuildNumber; private JLabel myVersionNumber; private JLabel myLastCheckedDate; + @SuppressWarnings("unused") private ActionLink myIgnoredBuildsLink; public UpdatesSettingsPanel(boolean checkNowEnabled) { mySettings = UpdateSettings.getInstance(); @@ -207,6 +214,21 @@ public class UpdateSettingsConfigurable extends BaseConfigurable implements Sear myChannelWarning.setForeground(JBColor.RED); } + private void createUIComponents() { + myIgnoredBuildsLink = new ActionLink(IdeBundle.message("updates.settings.ignored"), new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + List buildNumbers = mySettings.getIgnoredBuildNumbers(); + String text = StringUtil.join(buildNumbers, "\n"); + String result = Messages.showMultilineInputDialog(null, null, IdeBundle.message("updates.settings.ignored.title"), text, null, null); + if (result != null) { + buildNumbers.clear(); + buildNumbers.addAll(StringUtil.split(result, "\n")); + } + } + }); + } + private void updateLastCheckedLabel() { long time = mySettings.getLastTimeChecked(); myLastCheckedDate.setText(time == 0 ? IdeBundle.message("updates.last.check.never") : DateFormatUtil.formatPrettyDateTime(time)); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.java deleted file mode 100644 index 85ba0bab8a07..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.updateSettings.impl; - -import com.intellij.openapi.updateSettings.UpdateStrategyCustomization; -import com.intellij.openapi.util.BuildNumber; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.List; - -public class UpdateStrategy { - public enum State {LOADED, CONNECTION_ERROR, NOTHING_LOADED} - - private final int myMajorVersion; - private final BuildNumber myCurrentBuild; - private final UpdatesInfo myUpdatesInfo; - private final UserUpdateSettings myUpdateSettings; - private final ChannelStatus myChannelStatus; - private final UpdateStrategyCustomization myStrategyCustomization; - - /** @deprecated use {@link #UpdateStrategy(int, BuildNumber, UpdatesInfo, UserUpdateSettings, UpdateStrategyCustomization)} */ - @SuppressWarnings("unused") - public UpdateStrategy(int majorVersion, - @NotNull BuildNumber currentBuild, - @NotNull UpdatesInfo updatesInfo, - @NotNull UserUpdateSettings updateSettings) { - this(majorVersion, currentBuild, updatesInfo, updateSettings, UpdateStrategyCustomization.getInstance()); - } - - public UpdateStrategy(int majorVersion, - @NotNull BuildNumber currentBuild, - @NotNull UpdatesInfo updatesInfo, - @NotNull UserUpdateSettings updateSettings, - @NotNull UpdateStrategyCustomization customization) { - myMajorVersion = majorVersion; - myCurrentBuild = currentBuild; - myUpdatesInfo = updatesInfo; - myUpdateSettings = updateSettings; - myChannelStatus = updateSettings.getSelectedChannelStatus(); - myStrategyCustomization = customization; - } - - public final CheckForUpdateResult checkForUpdates() { - Product product = myUpdatesInfo.getProduct(myCurrentBuild.getProductCode()); - - if (product == null || product.getChannels().isEmpty()) { - return new CheckForUpdateResult(State.NOTHING_LOADED, null); - } - - UpdateChannel updatedChannel = null; - BuildInfo newBuild = null; - List activeChannels = getActiveChannels(product); - for (UpdateChannel channel : activeChannels) { - BuildInfo latestBuild = channel.getLatestBuild(myCurrentBuild.getBaselineVersion()); - if (latestBuild == null || latestBuild.getNumber().compareTo(myCurrentBuild) <= 0) { - latestBuild = channel.getLatestBuild(); - } - if (isNewVersion(latestBuild)) { - updatedChannel = channel; - newBuild = latestBuild; - break; - } - } - - UpdateChannel channelToPropose = null; - for (UpdateChannel channel : product.getChannels()) { - if (!myUpdateSettings.getKnownChannelsIds().contains(channel.getId()) && - channel.getMajorVersion() >= myMajorVersion && - channel.getStatus().compareTo(myChannelStatus) >= 0 && - isNewVersion(channel.getLatestBuild()) && - (channelToPropose == null || isBetter(channelToPropose, channel))) { - channelToPropose = channel; - } - } - - return new CheckForUpdateResult(newBuild, updatedChannel, channelToPropose, product.getAllChannelIds()); - } - - private List getActiveChannels(Product product) { - List result = new ArrayList(); - - for (UpdateChannel channel : product.getChannels()) { - // If the update is to a new version and on a stabler channel, choose it. - if ((channel.getMajorVersion() >= myMajorVersion && channel.getStatus().compareTo(myChannelStatus) >= 0) && - (myStrategyCustomization.allowMajorVersionUpdate() || - channel.getMajorVersion() == myMajorVersion || - channel.getStatus() == ChannelStatus.EAP && myChannelStatus == ChannelStatus.EAP)) { - // Prefer channel that has same status as our selected channel status - if (channel.getMajorVersion() == myMajorVersion && channel.getStatus().compareTo(myChannelStatus) == 0) { - result.add(0, channel); - } - else { - result.add(channel); - } - } - } - - return result; - } - - private boolean isNewVersion(BuildInfo latestBuild) { - return latestBuild != null && - !myUpdateSettings.getIgnoredBuildNumbers().contains(latestBuild.getNumber().asStringWithoutProductCode()) && - myCurrentBuild.compareTo(latestBuild.getNumber()) < 0; - } - - private static boolean isBetter(UpdateChannel channelToPropose, UpdateChannel channel) { - return channel.getMajorVersion() > channelToPropose.getMajorVersion() || - channel.getMajorVersion() == channelToPropose.getMajorVersion() && channel.getStatus().compareTo(channelToPropose.getStatus()) > 0; - } -} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt new file mode 100644 index 000000000000..98b5d41ba31a --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.updateSettings.impl + +import com.intellij.openapi.updateSettings.UpdateStrategyCustomization +import com.intellij.openapi.util.BuildNumber +import java.util.* + +class UpdateStrategy(private val currentBuild: BuildNumber, private val updates: UpdatesInfo, private val settings: UserUpdateSettings) { + enum class State { + LOADED, CONNECTION_ERROR, NOTHING_LOADED + } + + fun checkForUpdates(): CheckForUpdateResult { + val product = updates[currentBuild.productCode] + if (product == null || product.channels.isEmpty()) { + return CheckForUpdateResult(State.NOTHING_LOADED, null) + } + + val selectedChannel = settings.selectedChannelStatus + val ignoredBuilds = settings.ignoredBuildNumbers.toSet() + + val result = product.channels.asSequence() + .filter { it.status.compareTo(selectedChannel) >= 0 } + .sortedWith(Comparator { ch1, ch2 -> ch1.status.compareTo(ch2.status) }) // by stability, asc + .map { ch -> ch.builds.asSequence().filter { build -> isApplicable(build, ignoredBuilds) } to ch } + .map { p -> candidateBuild(p.first) to p.second } + .filter { p -> p.first != null } + .maxBy { p -> p.first!!.number } + + return CheckForUpdateResult(result?.first, result?.second) + } + + private fun isApplicable(candidate: BuildInfo, ignoredBuilds: Set) = + candidate.number > currentBuild && + candidate.number.asStringWithoutProductCode() !in ignoredBuilds && + candidate.target?.inRange(currentBuild) ?: true + + private fun candidateBuild(builds: Sequence) = + latestBuild(builds.filter { it.number.baselineVersion == currentBuild.baselineVersion }) ?: latestBuild(builds) + + private fun latestBuild(builds: Sequence) = + builds.fold(null as BuildInfo?) { best, candidate -> if (best == null || candidate.number > best.number) candidate else best } + + // + + @Deprecated("use {@link #UpdateStrategy(BuildNumber, UpdatesInfo, UserUpdateSettings)}") + constructor(@Suppress("UNUSED_PARAMETER") majorVersion: Int, + @Suppress("UNUSED_PARAMETER") currentBuild: BuildNumber, + @Suppress("UNUSED_PARAMETER") updatesInfo: UpdatesInfo, + @Suppress("UNUSED_PARAMETER") updateSettings: UserUpdateSettings) : this(currentBuild, updatesInfo, updateSettings) { + } + + + @Deprecated("use {@link #UpdateStrategy(BuildNumber, UpdatesInfo, UserUpdateSettings)}") + constructor(@Suppress("UNUSED_PARAMETER") majorVersion: Int, + @Suppress("UNUSED_PARAMETER") currentBuild: BuildNumber, + @Suppress("UNUSED_PARAMETER") updatesInfo: UpdatesInfo, + @Suppress("UNUSED_PARAMETER") updateSettings: UserUpdateSettings, + @Suppress("UNUSED_PARAMETER") customization: UpdateStrategyCustomization) : this(currentBuild, updatesInfo, updateSettings) { + } + + // +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesSettingsPanel.form b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesSettingsPanel.form index b8d01a22b157..ca50aebc7d48 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesSettingsPanel.form +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesSettingsPanel.form @@ -1,6 +1,6 @@
- + @@ -8,91 +8,57 @@ - - + - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - + - + + - + - + - + - + - + - + - + @@ -100,20 +66,68 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UserUpdateSettings.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UserUpdateSettings.java index f1c792199b55..7a5ad3a5b582 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UserUpdateSettings.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UserUpdateSettings.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +20,6 @@ import org.jetbrains.annotations.NotNull; import java.util.List; public interface UserUpdateSettings { - @NotNull - List getKnownChannelsIds(); - - List getIgnoredBuildNumbers(); - - void setKnownChannelIds(List ids); - - @NotNull - ChannelStatus getSelectedChannelStatus(); -} + @NotNull ChannelStatus getSelectedChannelStatus(); + @NotNull List getIgnoredBuildNumbers(); +} \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index 301e0ec64802..be1aeb45c111 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -906,7 +906,6 @@ updates.error.connection.failed=Connection failed ({0}). Please check network co updates.error.plugin.description.failed=Failed to load plugin descriptions from: \"{0}\" update.notifications.title=Platform and Plugin Updates update.notifications.group=Platform and Plugin Updates -updates.channel.name.message={0}: {1} updates.channel.bundled.key=The new version has an expiration date and does not require a license key. updates.fallback.build=You have perpetual fallback license for the new version. updates.subscription.active.till=Your subscription is active until {0}. @@ -935,9 +934,11 @@ updates.settings.check.now.button=&Check Now updates.settings.checkbox=Automatically check updates for updates.settings.unstable=Please note that this channel is less stable than your current one. updates.settings.secure=Use secure connection -updates.settings.build.number=Build number: -updates.settings.current.version=Current version: -updates.settings.last.check=Last checked: +updates.settings.build.number=Build number +updates.settings.current.version=Current version +updates.settings.last.check=Last checked +updates.settings.ignored=View/edit Ignored builds +updates.settings.ignored.title=Ignored Builds update.plugin.host.url.message=Repository URL: update.no.update.hosts=No plugin repositories configured update.edit.plugin.host.title=Edit Repository diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/InfoReader.java b/platform/platform-tests/testSrc/com/intellij/ide/updates/InfoReader.java deleted file mode 100644 index 97b59be98774..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/InfoReader.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide.updates; - -import com.intellij.openapi.updateSettings.impl.UpdatesInfo; -import com.intellij.util.JdomKt; -import org.jetbrains.annotations.NotNull; - -import java.io.InputStream; -import java.net.URL; - -public class InfoReader { - @NotNull - public static UpdatesInfo read(@NotNull String fileName) { - return read(InfoReader.class.getResource(fileName)); - } - - @NotNull - public static UpdatesInfo read(@NotNull URL url) { - try (InputStream stream = url.openStream()) { - return new UpdatesInfo(JdomKt.loadElement(stream)); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateInfoParsingTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateInfoParsingTest.kt new file mode 100644 index 000000000000..37a9bec9f233 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateInfoParsingTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.updates + +import com.intellij.openapi.updateSettings.impl.ChannelStatus +import com.intellij.openapi.updateSettings.impl.UpdateChannel +import com.intellij.openapi.updateSettings.impl.UpdatesInfo +import com.intellij.openapi.util.BuildNumber +import com.intellij.util.loadElement +import org.junit.Test +import java.net.URL +import java.text.SimpleDateFormat +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class UpdateInfoParsingTest { + @Test fun liveJetbrainsUpdateFile() { + val info = load(URL("http://www.jetbrains.com/updates/updates.xml").readText()) + assertNotNull(info["IC"]) + } + + @Test fun liveAndroidUpdateFile() { + val info = load(URL("https://dl.google.com/android/studio/patches/updates.xml").readText()) + assertNotNull(info["AI"]) + } + + @Test fun emptyChannels() { + val info = load(""" + + + IU + IC + + """.trimIndent()) + val product = info["IU"]!! + assertEquals("IntelliJ IDEA", product.name) + assertEquals(0, product.channels.size) + assertEquals(product, info["IC"]) + } + + @Test fun oneProductOnly() { + val info = load(""" + + + IU + + + + IntelliJ IDEA 9.0.4 is available. Please visit https://www.jetbrains.com/idea to learn more and download it. + + + + + + + IntelliJ IDEA X RC is available. Please visit hhttp://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more. + + + + + """.trimIndent()) + + val product = info["IU"]!! + assertEquals("IntelliJ IDEA", product.name) + assertEquals(2, product.channels.size) + + val channel = product.channels.find { it.id == "IDEA10EAP" }!! + assertEquals(ChannelStatus.EAP, channel.status) + assertEquals(UpdateChannel.LICENSING_EAP, channel.licensing) + assertNotNull(channel.homePageUrl) + assertEquals(1, channel.builds.size) + + val build = channel.builds[0] + assertEquals(BuildNumber.fromString("98.520"), build.number) + assertEquals("2011-04-03", SimpleDateFormat("yyyy-MM-dd").format(build.releaseDate)) + assertEquals(0, build.patches.size) + + assertEquals(1, product.channels.find { it.id == "idea90" }!!.builds[0].patches.size) + } + + @Test fun targetRanges() { + val info = load(""" + + + IU + + + + + + + """.trimIndent()) + assertEquals(2, info["IU"]!!.channels[0].builds.count { it.target != null }) + } + + private fun load(text: String) = UpdatesInfo(loadElement(text)) +} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.java b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.java deleted file mode 100644 index 3cae33381c18..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide.updates; - -import com.intellij.openapi.updateSettings.UpdateStrategyCustomization; -import com.intellij.openapi.updateSettings.impl.*; -import com.intellij.openapi.util.BuildNumber; -import org.jetbrains.annotations.NotNull; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.Assert.*; - -public class UpdateStrategyTest { - @Test - public void testWithUndefinedSelection() { - // could be if somebody used before previous version of IDEA - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-98.520"), InfoReader.read("idea-same.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - assertNull(result.getNewBuildInSelectedChannel()); - } - - @Test - public void testWithUserSelection() { - // assume user has version 9 eap - and used eap channel - we want to introduce new eap - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.429"), InfoReader.read("idea-new9eap.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - BuildInfo update = result.getNewBuildInSelectedChannel(); - assertNotNull(update); - assertEquals("95.627", update.getNumber().toString()); - } - - @Test - public void testIgnore() { - // assume user has version 9 eap - and used eap channel - we want to introduce new eap - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP, "95.627", "98.620"); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.429"), InfoReader.read("idea-new9eap.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - BuildInfo update = result.getNewBuildInSelectedChannel(); - assertNull(update); - } - - @Test - public void testNewChannelAppears() { - // assume user has version 9 eap subscription (default or selected) - // and new channel appears - eap of version 10 is there - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.RELEASE); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.627"), InfoReader.read("idea-newChannel-release.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - BuildInfo update = result.getNewBuildInSelectedChannel(); - assertNull(update); - - UpdateChannel newChannel = result.getChannelToPropose(); - assertNotNull(newChannel); - assertEquals("IDEA10EAP", newChannel.getId()); - assertEquals("IntelliJ IDEA X EAP", newChannel.getName()); - } - - @Test - public void testNewChannelWithOlderBuild() { - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(10, BuildNumber.fromString("IU-107.80"), InfoReader.read("idea-newChannel.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - BuildInfo update = result.getNewBuildInSelectedChannel(); - assertNull(update); - - UpdateChannel newChannel = result.getChannelToPropose(); - assertNull(newChannel); - } - - @Test - public void testNewChannelAndNewBuildAppear() { - // assume user has version 9 eap subscription (default or selected) - // and new channels appears - eap of version 10 is there - // and new build withing old channel appears also - // we need to show only one dialog - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(9, BuildNumber.fromString("IU-95.429"), InfoReader.read("idea-newChannel.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - BuildInfo update = result.getNewBuildInSelectedChannel(); - assertNotNull(update); - assertEquals("95.627", update.getNumber().toString()); - - UpdateChannel newChannel = result.getChannelToPropose(); - assertNotNull(newChannel); - assertEquals("IDEA10EAP", newChannel.getId()); - assertEquals("IntelliJ IDEA X EAP", newChannel.getName()); - } - - @Test - public void testChannelWithCurrentStatusPreferred() { - BuildNumber currentBuild = BuildNumber.fromString("IU-139.658"); - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(14, currentBuild, InfoReader.read("idea-patchAvailable.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - - UpdateChannel channel = result.getUpdatedChannel(); - assertNotNull(channel); - assertEquals(ChannelStatus.EAP, channel.getStatus()); - - BuildInfo selectedChannel = result.getNewBuildInSelectedChannel(); - assertNotNull(selectedChannel); - assertNotNull(selectedChannel.findPatchForBuild(currentBuild)); - } - - @Test - public void testStableUpdatePreferredByDefault() { - BuildNumber currentBuild = BuildNumber.fromString("IU-143.381"); - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.RELEASE); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-123280.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - - UpdateChannel channel = result.getUpdatedChannel(); - assertNotNull(channel); - assertEquals(ChannelStatus.RELEASE, channel.getStatus()); - - BuildInfo build = result.getNewBuildInSelectedChannel(); - assertNotNull(build); - assertEquals("143.382", build.getNumber().toString()); - assertNotNull(build.findPatchForBuild(currentBuild)); - } - - @Test - public void testStableChannelProposedByDefault() { - BuildNumber currentBuild = BuildNumber.fromString("IU-143.381"); - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.RELEASE); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-123280.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - UpdateChannel channelToPropose = result.getChannelToPropose(); - assertNotNull(channelToPropose); - assertEquals("IDEA_Release", channelToPropose.getId()); - - settings.setKnownChannelIds(Collections.singletonList(channelToPropose.getId())); - result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - assertNull(result.getChannelToPropose()); - } - - @Test - public void testParallelCampaignsInChannel() { - BuildNumber currentBuild = BuildNumber.fromString("IU-143.381"); - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-2eap.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - BuildInfo build = result.getNewBuildInSelectedChannel(); - assertNotNull(build); - assertEquals("143.888", build.getNumber().toString()); - } - - @Test - public void testCrossingParallelCampaignsInChannel() { - BuildNumber currentBuild = BuildNumber.fromString("IU-143.888"); - TestUpdateSettings settings = new TestUpdateSettings(ChannelStatus.EAP); - UpdateStrategyCustomization customization = new UpdateStrategyCustomization(); - UpdateStrategy strategy = new UpdateStrategy(15, currentBuild, InfoReader.read("idea-2eap.xml"), settings, customization); - - CheckForUpdateResult result = strategy.checkForUpdates(); - assertEquals(UpdateStrategy.State.LOADED, result.getState()); - BuildInfo build = result.getNewBuildInSelectedChannel(); - assertNotNull(build); - assertEquals("144.888", build.getNumber().toString()); - } - - private static class TestUpdateSettings implements UserUpdateSettings { - private final ChannelStatus myChannelStatus; - private final List myIgnoredBuildNumbers; - private List myKnownChannelIds = Collections.emptyList(); - - public TestUpdateSettings(ChannelStatus channelStatus, String... ignoredBuilds) { - myChannelStatus = channelStatus; - myIgnoredBuildNumbers = Arrays.asList(ignoredBuilds); - } - - @NotNull - @Override - public List getKnownChannelsIds() { - return myKnownChannelIds; - } - - @Override - public List getIgnoredBuildNumbers() { - return myIgnoredBuildNumbers; - } - - @Override - public void setKnownChannelIds(List ids) { - myKnownChannelIds = ids; - } - - @NotNull - @Override - public ChannelStatus getSelectedChannelStatus() { - return myChannelStatus; - } - } -} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt new file mode 100644 index 000000000000..44bc8b388e2f --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.updates + +import com.intellij.openapi.updateSettings.impl.* +import com.intellij.openapi.util.BuildNumber +import com.intellij.util.loadElement +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class UpdateStrategyTest { + @Test fun `channel contains no builds`() { + val result = check("IU-145.258", ChannelStatus.RELEASE, """""") + assertNull(result.newBuild) + } + + @Test fun `already on the latest build`() { + val result = check("IU-145.258", ChannelStatus.RELEASE, """ + + + + + + """) + assertNull(result.newBuild) + } + + @Test fun `patch exclusions`() { + val result = check("IU-145.258", ChannelStatus.RELEASE, """ + + + + + + """) + assertNotNull(result.findPatchForBuild(BuildNumber.fromString("145.596"))) + assertNull(result.findPatchForBuild(BuildNumber.fromString("145.258"))) + } + + @Test fun `order of builds does not matter`() { + val resultDesc = check("IU-143.2332", ChannelStatus.RELEASE, """ + + + + """) + assertEquals("145.597", resultDesc.newBuild?.number.toString()) + + val resultAsc = check("IU-143.2332", ChannelStatus.RELEASE, """ + + + + """) + assertEquals("145.597", resultAsc.newBuild?.number.toString()) + } + + @Test fun `newer updates are preferred`() { + val result = check("IU-145.258", ChannelStatus.EAP, """ + + + + + + """) + assertEquals("145.597", result.newBuild?.number.toString()) + } + + @Test fun `newer updates are preferred over more stable ones`() { + val result = check("IU-145.257", ChannelStatus.EAP, """ + + + + + + + + + """) + assertEquals("145.596", result.newBuild?.number.toString()) + } + + @Test fun `newer updates from non-allowed channels are ignored`() { + val channels = """ + + + + + + + + + """ + assertEquals("145.258", check("IU-145.256", ChannelStatus.RELEASE, channels).newBuild?.number.toString()) + assertNull(check("IU-145.258", ChannelStatus.RELEASE, channels).newBuild) + } + + @Test fun `ignored updates are excluded`() { + val result = check("IU-145.258", ChannelStatus.EAP, """ + + + + """, listOf("145.596")) + assertEquals("145.595", result.newBuild?.number.toString()) + } + + @Test fun `updates can be targeted for specific builds`() { + val channels = """ + + + + """ + assertEquals("145.595", check("IU-145.258", ChannelStatus.EAP, channels).newBuild?.number.toString()) + assertEquals("145.596", check("IU-145.595", ChannelStatus.EAP, channels).newBuild?.number.toString()) + } + + @Test fun `updates from the same baseline are preferred`() { + val result = check("IU-143.2287", ChannelStatus.RELEASE, """ + + + + """) + assertEquals("143.2332", result.newBuild?.number.toString()) + } + + @Test fun `cross-baseline updates are perfectly legal`() { + val result = check("IU-143.2332", ChannelStatus.EAP, """ + + + + """) + assertEquals("145.597", result.newBuild?.number.toString()) + } + + private fun check(currentBuild: String, + selectedChannel: ChannelStatus, + testData: String, + ignoredBuilds: List = emptyList()): CheckForUpdateResult { + val updates = UpdatesInfo(loadElement(""" + + + IU + ${testData} + + """)) + val settings = object : UserUpdateSettings { + override fun getSelectedChannelStatus() = selectedChannel + override fun getIgnoredBuildNumbers() = ignoredBuilds + } + val result = UpdateStrategy(BuildNumber.fromString(currentBuild), updates, settings).checkForUpdates() + assertEquals(UpdateStrategy.State.LOADED, result.state) + return result + } +} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdatesInfoParserTest.java b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdatesInfoParserTest.java deleted file mode 100644 index d929d5690267..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdatesInfoParserTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide.updates; - -import com.intellij.openapi.updateSettings.impl.*; -import com.intellij.openapi.util.BuildNumber; -import org.junit.Test; - -import java.net.MalformedURLException; -import java.net.URL; -import java.text.SimpleDateFormat; -import java.util.Date; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class UpdatesInfoParserTest { - @Test - public void testLiveJetbrainsUpdateFile() throws MalformedURLException { - UpdatesInfo info = InfoReader.read(new URL("http://www.jetbrains.com/updates/updates.xml")); - assertNotNull(info.getProduct("IC")); - } - - @Test - public void testLiveAndroidUpdateFile() throws MalformedURLException { - UpdatesInfo info = InfoReader.read(new URL("https://dl.google.com/android/studio/patches/updates.xml")); - assertNotNull(info.getProduct("AI")); - } - - @Test - public void testValidXmlParsing() { - UpdatesInfo info = InfoReader.read("current.xml"); - assertEquals(5, info.getProductsCount()); - - Product product = info.getProduct("IU"); - assertNotNull(product); - checkProduct(product, "IntelliJ IDEA", "maiaEAP", "IDEA10EAP", "idea90"); - - UpdateChannel channel = product.findUpdateChannelById("IDEA10EAP"); - assertNotNull(channel); - assertEquals(ChannelStatus.EAP, channel.getStatus()); - assertEquals(UpdateChannel.LICENSING_EAP, channel.getLicensing()); - assertNotNull(channel.getHomePageUrl()); - assertNotNull(channel.getFeedbackUrl()); - - BuildInfo build = channel.getLatestBuild(); - assertNotNull(build); - assertEquals(BuildNumber.fromString("98.520"), build.getNumber()); - Date date = build.getReleaseDate(); - assertNotNull(date); - assertEquals("2011-04-03", new SimpleDateFormat("yyyy-MM-dd").format(date)); - } - - @Test - public void testEmptyChannels() { - UpdatesInfo info = InfoReader.read("emptyChannels.xml"); - - Product product = info.getProduct("IU"); - assertNotNull(product); - assertEquals(0, product.getChannels().size()); - } - - @Test - public void testOneProductOnly() { - UpdatesInfo info = InfoReader.read("oneProductOnly.xml"); - assertNotNull(info); - - Product product = info.getProduct("IU"); - assertNotNull(product); - checkProduct(product, "IntelliJ IDEA", "maiaEAP", "IDEA10EAP", "idea90"); - - UpdateChannel channel = product.findUpdateChannelById("IDEA10EAP"); - assertNotNull(channel); - - BuildInfo build = channel.getLatestBuild(); - assertNotNull(build); - assertEquals(BuildNumber.fromString("98.520"), build.getNumber()); - } - - private static void checkProduct(Product product, String name, String... channels) { - assertEquals(name, product.getName()); - assertEquals(channels.length, product.getChannels().size()); - for (String channel : channels) { - assertNotNull(product.findUpdateChannelById(channel)); - } - } -} diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/current.xml b/platform/platform-tests/testSrc/com/intellij/ide/updates/current.xml deleted file mode 100644 index b9b2994a9b88..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/current.xml +++ /dev/null @@ -1,188 +0,0 @@ - - - IU - IC - - - - IntelliJ IDEA 9.0.4 is available. - Please visit https://www.jetbrains.com/idea to learn more and download it. - - - - - - - - IntelliJ IDEA 9.0.4 is available. - Please visit https://www.jetbrains.com/idea to learn more and download it. - - - - - - - - IntelliJ IDEA X RC is available. - Please visit http://confluence.jetbrains.net/display/IDEADEV/IDEA+X+EAP to learn more and download it. - - - - - - - - RM - - - RubyMine 3.0 is available. - Please visit https://www.jetbrains.com/ruby to learn more and download it. - - - - - - RubyMine 3.0 is available. - Please visit https://www.jetbrains.com/ruby to learn more and download it. - - - - - - RubyMine 3.0 Release Candidate 2 is available. - Please visit https://www.jetbrains.com/ruby to learn more and download it. - - - - - - RubyMine 3.0 is available. - Please visit https://www.jetbrains.com/ruby to learn more and download it. - - - - - - - PY - - - PyCharm 1.0.1 is available. - Please visit www.jetbrains.com/pycharm to download it. - - - - - - - PyCharm 1.0.1 is available. - Please visit www.jetbrains.com/pycharm to download it. - - - - - - - PS - - - PhpStorm 1.0.2 is available. - Please visit https://www.jetbrains.com/phpstorm to download it. - - - - - - - PhpStorm 1.0.2 is available. - Please visit https://www.jetbrains.com/phpstorm to download it. - - - - - - - PhpStorm 2.0 EAP update is available. - Please visit http://confluence.jetbrains.net/display/WI/ to download it. - - - - - - - - PhpStorm 2.0 is available. - Please visit https://www.jetbrains.com/phpstorm to download it. - - - - - - - WS - - - WebStorm 1.0.2 is available. - Please visit https://www.jetbrains.com/webstorm to download it. - - - - - - - WebStorm 1.0.2 is available. - Please visit https://www.jetbrains.com/webstorm to download it. - - - - - - - WebStorm 2.0 EAP update is available. - Please visit http://confluence.jetbrains.net/display/WI/ to download it. - - - - - - - - WebStorm 1.0.2 is available. - Please visit https://www.jetbrains.com/webstorm to download it. - - - - - diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/emptyChannels.xml b/platform/platform-tests/testSrc/com/intellij/ide/updates/emptyChannels.xml deleted file mode 100644 index 53fe2aa9a84a..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/emptyChannels.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - IU - IC - - diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/idea-123280.xml b/platform/platform-tests/testSrc/com/intellij/ide/updates/idea-123280.xml deleted file mode 100644 index 1733e7b88f99..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/idea-123280.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - IU - IC - - - - -