diff --git a/plugins/settings-sync/intellij.settingsSync.iml b/plugins/settings-sync/intellij.settingsSync.iml
index 5e21574e356f..c8ebfa737c40 100644
--- a/plugins/settings-sync/intellij.settingsSync.iml
+++ b/plugins/settings-sync/intellij.settingsSync.iml
@@ -83,5 +83,6 @@
+
\ No newline at end of file
diff --git a/plugins/settings-sync/resources/META-INF/plugin.xml b/plugins/settings-sync/resources/META-INF/plugin.xml
index f97c5e340b5a..f4edd67460b6 100644
--- a/plugins/settings-sync/resources/META-INF/plugin.xml
+++ b/plugins/settings-sync/resources/META-INF/plugin.xml
@@ -21,6 +21,8 @@
+
()
private val json = Json { prettyPrint = true }
@@ -34,6 +36,9 @@ internal object SettingsSnapshotZipSerializer {
fun serializeToZip(snapshot: SettingsSnapshot): Path {
val file = FileUtil.createTempFile(SETTINGS_SYNC_SNAPSHOT_ZIP, null)
serialize(snapshot, Compressor.Zip(file))
+ if (file.length() > MAX_ZIP_SIZE) {
+ throw ZipSizeExceedException(file.length())
+ }
return file.toPath()
}
@@ -183,4 +188,10 @@ internal object SettingsSnapshotZipSerializer {
var configFolder: String = ""
var isDeleted: Boolean = false
}
+
+ class ZipSizeExceedException(private val size: Long): RuntimeException() {
+ override fun toString(): String {
+ return "Zip size $size excesses maximum allowed zip size"
+ }
+ }
}
\ No newline at end of file
diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt
index 31e5b9068d6c..34b1be1ce15f 100644
--- a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt
+++ b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt
@@ -4,14 +4,7 @@ import com.intellij.concurrency.ConcurrentCollectionFactory
import com.intellij.configurationStore.*
import com.intellij.configurationStore.schemeManager.SchemeManagerFactoryBase
import com.intellij.configurationStore.schemeManager.SchemeManagerImpl
-import com.intellij.notification.Notification
-import com.intellij.notification.NotificationAction
-import com.intellij.notification.NotificationGroupManager
-import com.intellij.notification.NotificationType
-import com.intellij.openapi.application.ApplicationManager
-import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.application.PathManager.OPTIONS_DIRECTORY
-import com.intellij.openapi.application.ex.ApplicationEx
import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.StateStorage
@@ -21,11 +14,10 @@ import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.options.SchemeManagerFactory
import com.intellij.openapi.util.registry.Registry
import com.intellij.settingsSync.SettingsSnapshot.MetaInfo
+import com.intellij.settingsSync.notification.NotificationService
import com.intellij.settingsSync.plugins.SettingsSyncPluginManager
import com.intellij.util.io.*
-import org.jdom.Element
import java.io.InputStream
-import java.lang.RuntimeException
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
@@ -35,8 +27,6 @@ import java.time.Instant
import java.util.concurrent.locks.ReadWriteLock
import java.util.concurrent.locks.ReentrantReadWriteLock
import java.util.function.Predicate
-import kotlin.collections.ArrayList
-import kotlin.collections.LinkedHashSet
import kotlin.concurrent.withLock
import kotlin.io.path.exists
import kotlin.io.path.name
@@ -105,43 +95,9 @@ internal class SettingsSyncIdeMediatorImpl(private val componentStore: Component
notifyRestartNeeded()
}
-
private fun notifyRestartNeeded() {
- if (restartRequiredReasons.isEmpty())
- return
- val notification = buildRestartNeededNotification()
- notification.addAction(NotificationAction.create(
- SettingsSyncBundle.message("sync.restart.notification.action", ApplicationNamesInfo.getInstance().fullProductName),
- com.intellij.util.Consumer {
- val app = ApplicationManager.getApplication() as ApplicationEx
- app.restart(true)
- }))
- notification.notify(null)
- }
-
- private fun buildRestartNeededNotification(): Notification {
- fun getMultiReasonRestartMessage(): String {
- assert(restartRequiredReasons.size > 1)
- val message = StringBuilder(SettingsSyncBundle.message("sync.restart.notification.message.subtitle")).append('\n')
-
- val sortedRestartReasons = restartRequiredReasons.sorted()
- for ((counter, reason) in sortedRestartReasons.withIndex()) {
- message.append(reason.getMultiReasonNotificationListEntry(counter + 1))
- }
-
- message.dropLast(0) // we do not need the new line in the end
- return message.toString()
- }
-
- val message = when {
- restartRequiredReasons.isEmpty() -> throw RuntimeException("No restart reasons found")
- restartRequiredReasons.size == 1 -> restartRequiredReasons.first().getSingleReasonNotificationMessage()
- else -> getMultiReasonRestartMessage()
- }
- return NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
- .createNotification(SettingsSyncBundle.message("sync.restart.notification.title"),
- message,
- NotificationType.INFORMATION)
+ if (restartRequiredReasons.isEmpty()) return
+ NotificationService.getInstance().notifyRestartNeeded(restartRequiredReasons)
}
override fun activateStreamProvider() {
diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/notification/NotificationService.kt b/plugins/settings-sync/src/com/intellij/settingsSync/notification/NotificationService.kt
new file mode 100644
index 000000000000..f773e7e1cd0e
--- /dev/null
+++ b/plugins/settings-sync/src/com/intellij/settingsSync/notification/NotificationService.kt
@@ -0,0 +1,16 @@
+package com.intellij.settingsSync.notification
+
+import com.intellij.notification.Notification
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.settingsSync.RestartReason
+
+interface NotificationService {
+ companion object {
+ fun getInstance(): NotificationService = ApplicationManager.getApplication().getService(NotificationService::class.java)
+ }
+
+ fun notifyZipSizeExceed()
+ fun buildZipSizeExceedNotification(): Notification
+ fun notifyRestartNeeded(reasons: Collection)
+ fun buildRestartNeededNotification(reasons: Collection): Notification
+}
\ No newline at end of file
diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/notification/NotificationServiceImpl.kt b/plugins/settings-sync/src/com/intellij/settingsSync/notification/NotificationServiceImpl.kt
new file mode 100644
index 000000000000..747fef4c67d0
--- /dev/null
+++ b/plugins/settings-sync/src/com/intellij/settingsSync/notification/NotificationServiceImpl.kt
@@ -0,0 +1,63 @@
+package com.intellij.settingsSync.notification
+
+import com.intellij.notification.Notification
+import com.intellij.notification.NotificationAction
+import com.intellij.notification.NotificationGroupManager
+import com.intellij.notification.NotificationType
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.application.ApplicationNamesInfo
+import com.intellij.openapi.application.ex.ApplicationEx
+import com.intellij.settingsSync.NOTIFICATION_GROUP
+import com.intellij.settingsSync.RestartReason
+import com.intellij.settingsSync.SettingsSyncBundle
+import java.lang.RuntimeException
+
+internal class NotificationServiceImpl: NotificationService {
+ override fun notifyZipSizeExceed() {
+ val notification = buildZipSizeExceedNotification()
+ notification.notify(null)
+ }
+
+ override fun buildZipSizeExceedNotification(): Notification {
+ return NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
+ .createNotification(SettingsSyncBundle.message("sync.notification.size.exceed.title"),
+ SettingsSyncBundle.message("sync.notification.size.exceed.text"),
+ NotificationType.ERROR)
+ }
+
+ override fun notifyRestartNeeded(reasons: Collection) {
+ val notification = buildRestartNeededNotification(reasons)
+ notification.addAction(NotificationAction.create(
+ SettingsSyncBundle.message("sync.restart.notification.action", ApplicationNamesInfo.getInstance().fullProductName),
+ com.intellij.util.Consumer {
+ val app = ApplicationManager.getApplication() as ApplicationEx
+ app.restart(true)
+ }))
+ notification.notify(null)
+ }
+
+ override fun buildRestartNeededNotification(reasons: Collection): Notification {
+ fun getMultiReasonRestartMessage(): String {
+ assert(reasons.size > 1)
+ val message = StringBuilder(SettingsSyncBundle.message("sync.restart.notification.message.subtitle")).append('\n')
+
+ val sortedRestartReasons = reasons.sorted()
+ for ((counter, reason) in sortedRestartReasons.withIndex()) {
+ message.append(reason.getMultiReasonNotificationListEntry(counter + 1))
+ }
+
+ message.dropLast(0) // we do not need the new line in the end
+ return message.toString()
+ }
+
+ val message = when {
+ reasons.isEmpty() -> throw RuntimeException("No restart reasons provided")
+ reasons.size == 1 -> reasons.first().getSingleReasonNotificationMessage()
+ else -> getMultiReasonRestartMessage()
+ }
+ return NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
+ .createNotification(SettingsSyncBundle.message("sync.restart.notification.title"),
+ message,
+ NotificationType.INFORMATION)
+ }
+}
\ No newline at end of file
diff --git a/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncRealIdeTest.kt b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncRealIdeTest.kt
index 60557cd2d2bc..46cc01ff876f 100644
--- a/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncRealIdeTest.kt
+++ b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncRealIdeTest.kt
@@ -3,24 +3,36 @@ package com.intellij.settingsSync
import com.intellij.configurationStore.getPerOsSettingsStorageFolderName
import com.intellij.ide.GeneralSettings
import com.intellij.ide.ui.UISettings
+import com.intellij.idea.TestFor
import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.*
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
import com.intellij.openapi.keymap.impl.KeymapImpl
import com.intellij.openapi.keymap.impl.KeymapManagerImpl
import com.intellij.openapi.util.Disposer
import com.intellij.settingsSync.SettingsSnapshot.MetaInfo
+import com.intellij.settingsSync.notification.NotificationService
+import com.intellij.settingsSync.notification.NotificationServiceImpl
+import com.intellij.testFramework.replaceService
import com.intellij.util.io.readText
import com.intellij.util.toByteArray
import com.intellij.util.xmlb.annotations.Attribute
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.take
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
import java.nio.charset.Charset
import java.time.Instant
+import java.util.*
import kotlin.io.path.div
import kotlin.io.path.exists
@@ -287,8 +299,8 @@ internal class SettingsSyncRealIdeTest : SettingsSyncRealIdeTestBase() {
//assertTrue("Didn't await for the push request", cdl.await(5, TIMEOUT_UNIT))
}
- // temporarily disabled: the failure needs to be investigated
- //@Test
+ @Test
+ @Ignore // TODO investigate
fun `local and remote changes in different files are both applied`() {
val generalSettings = GeneralSettings.getInstance().init()
initSettingsSync(SettingsSyncBridge.InitMode.JustInit)
@@ -331,6 +343,20 @@ internal class SettingsSyncRealIdeTest : SettingsSyncRealIdeTestBase() {
assertFalse(EditorSettingsExternalizable.getInstance().isShowIntentionBulb)
}
+ @TestFor(issues = ["IDEA-291623"])
+ @Test
+ fun `zip file size limit exceed`() {
+ val notificationServiceSpy = Mockito.spy()
+ ApplicationManager.getApplication().replaceService(NotificationService::class.java, notificationServiceSpy, disposable)
+
+ EditorSettingsExternalizable.getInstance().initModifyAndSave {
+ languageBreadcrumbsMap = (1..100000).associate { UUID.randomUUID().toString() to true } // please FIXME if you now a better way to make a fat file
+ }
+ initSettingsSync(SettingsSyncBridge.InitMode.JustInit)
+
+ verify(notificationServiceSpy).notifyZipSizeExceed()
+ }
+
//@Test
fun `only changed components should be reloaded`() {
TODO()