diff --git a/lib/annotations/jdk/java/nio/file/annotations.xml b/lib/annotations/jdk/java/nio/file/annotations.xml new file mode 100644 index 000000000000..6706a3e31d7b --- /dev/null +++ b/lib/annotations/jdk/java/nio/file/annotations.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt b/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt index 9fa0e2b953f0..1caac75d4a69 100644 --- a/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt +++ b/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt @@ -20,11 +20,11 @@ import org.jetbrains.io.MessageDecoder import org.jetbrains.io.NettyUtil import org.junit.ClassRule import org.junit.Test -import java.util.UUID +import java.util.* import java.util.concurrent.TimeUnit // we don't handle String in efficient way - because we want to test readContent/readChars also -public class BinaryRequestHandlerTest { +internal class BinaryRequestHandlerTest { companion object { @ClassRule val projectRule = ProjectRule() } @@ -47,13 +47,13 @@ public class BinaryRequestHandlerTest { } }) - val port = BuiltInServerManager.getInstance().waitForStart().getPort() + val port = BuiltInServerManager.getInstance().waitForStart().port val channel = bootstrap.connect(NetUtils.getLoopbackAddress(), port).syncUninterruptibly().channel() val buffer = channel.alloc().buffer() buffer.writeByte('C'.toInt()) buffer.writeByte('H'.toInt()) - buffer.writeLong(MyBinaryRequestHandler.ID.getMostSignificantBits()) - buffer.writeLong(MyBinaryRequestHandler.ID.getLeastSignificantBits()) + buffer.writeLong(MyBinaryRequestHandler.ID.mostSignificantBits) + buffer.writeLong(MyBinaryRequestHandler.ID.leastSignificantBits) val message = Unpooled.copiedBuffer(text, CharsetUtil.UTF_8) buffer.writeShort(message.readableBytes()) @@ -67,7 +67,7 @@ public class BinaryRequestHandlerTest { } }) - if (result.getState() == Promise.State.PENDING) { + if (result.state == Promise.State.PENDING) { val semaphore = Semaphore() semaphore.down() result.processed { semaphore.up() } diff --git a/platform/configuration-store-impl/src/ApplicationStoreImpl.kt b/platform/configuration-store-impl/src/ApplicationStoreImpl.kt index 9e4583d1aa50..afb961526dab 100644 --- a/platform/configuration-store-impl/src/ApplicationStoreImpl.kt +++ b/platform/configuration-store-impl/src/ApplicationStoreImpl.kt @@ -32,9 +32,9 @@ import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VfsUtil import org.jdom.Element -class ApplicationPathMacroManager : BasePathMacroManager(null) +private class ApplicationPathMacroManager : BasePathMacroManager(null) -class ApplicationStoreImpl(private val application: Application, pathMacroManager: PathMacroManager) : ComponentStoreImpl() { +class ApplicationStoreImpl(private val application: Application, pathMacroManager: PathMacroManager? = null) : ComponentStoreImpl() { override val storageManager = ApplicationStorageManager(application, pathMacroManager) // number of app components require some state, so, we load default state in test mode @@ -42,8 +42,9 @@ class ApplicationStoreImpl(private val application: Application, pathMacroManage get() = if (application.isUnitTestMode) StateLoadPolicy.LOAD_ONLY_DEFAULT else StateLoadPolicy.LOAD override fun setPath(path: String) { - storageManager.addMacro(ROOT_CONFIG, path) + // app config must be first, because collapseMacros collapse from fist to last, so, at first we must replace APP_CONFIG because it overlaps ROOT_CONFIG value storageManager.addMacro(StoragePathMacros.APP_CONFIG, "$path/${ApplicationStorageManager.FILE_STORAGE_DIR}") + storageManager.addMacro(ROOT_CONFIG, path) val configDir = LocalFileSystem.getInstance().refreshAndFindFileByPath(path) if (configDir != null) { @@ -110,9 +111,9 @@ class ApplicationStorageManager(private val application: Application, pathMacroM return path } - override fun expandNormalizedPath(path: String): String { + override fun expandMacros(path: String): String { if (path[0] == '$') { - return super.expandNormalizedPath(path) + return super.expandMacros(path) } else { return "${expandMacro(StoragePathMacros.APP_CONFIG)}/$path" diff --git a/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt b/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt index 35d8156911a7..56303e7d4ae6 100644 --- a/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt +++ b/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt @@ -73,8 +73,6 @@ internal class DefaultProjectStoreImpl(override val project: ProjectImpl, privat override fun expandMacros(file: String) = throw UnsupportedOperationException("Method expandMacros not implemented in " + javaClass) - override fun collapseMacros(path: String) = throw UnsupportedOperationException("Method collapseMacros not implemented in " + javaClass) - override fun getOldStorage(component: Any, componentName: String, operation: StateStorageOperation) = storage } diff --git a/platform/configuration-store-impl/src/ProjectStateStorageManager.kt b/platform/configuration-store-impl/src/ProjectStateStorageManager.kt index 8bd2930c25be..ed8ccf7526d1 100644 --- a/platform/configuration-store-impl/src/ProjectStateStorageManager.kt +++ b/platform/configuration-store-impl/src/ProjectStateStorageManager.kt @@ -35,9 +35,9 @@ class ProjectStateStorageManager(macroSubstitutor: TrackingPathMacroSubstitutor, return path } - override fun expandNormalizedPath(path: String): String { + override fun expandMacros(path: String): String { if (path[0] == '$') { - return super.expandNormalizedPath(path) + return super.expandMacros(path) } else { return "${expandMacro(StoragePathMacros.PROJECT_CONFIG_DIR)}/$path" diff --git a/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt b/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt index 174a2de54b4d..f10998ff977c 100644 --- a/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt @@ -29,9 +29,9 @@ import com.intellij.util.containers.ContainerUtil import com.intellij.util.lang.CompoundRuntimeException import java.io.File -val ROOT_CONFIG: String = "\$ROOT_CONFIG$" +val ROOT_CONFIG = "\$ROOT_CONFIG$" -public abstract class SchemeManagerFactoryBase : SchemesManagerFactory(), SettingsSavingComponent { +sealed class SchemeManagerFactoryBase : SchemesManagerFactory(), SettingsSavingComponent { private val managers = ContainerUtil.createLockFreeCopyOnWriteList>() abstract val componentManager: ComponentManager @@ -85,27 +85,27 @@ public abstract class SchemeManagerFactoryBase : SchemesManagerFactory(), Settin CompoundRuntimeException.throwIfNotEmpty(errors) } -} -private class ApplicationSchemeManagerFactory : SchemeManagerFactoryBase() { - override val componentManager: ComponentManager - get() = ApplicationManager.getApplication() + private class ApplicationSchemeManagerFactory : SchemeManagerFactoryBase() { + override val componentManager: ComponentManager + get() = ApplicationManager.getApplication() - override fun checkPath(originalPath: String): String { - var path = super.checkPath(originalPath) - if (path.startsWith(ROOT_CONFIG)) { - path = path.substring(ROOT_CONFIG.length() + 1) - val message = "Path must not contains ROOT_CONFIG macro, corrected: $path" - if (ApplicationManager.getApplication().isUnitTestMode) throw AssertionError(message) else LOG.warn(message) + override fun checkPath(originalPath: String): String { + var path = super.checkPath(originalPath) + if (path.startsWith(ROOT_CONFIG)) { + path = path.substring(ROOT_CONFIG.length() + 1) + val message = "Path must not contains ROOT_CONFIG macro, corrected: $path" + if (ApplicationManager.getApplication().isUnitTestMode) throw AssertionError(message) else LOG.warn(message) + } + return path } - return path + + override fun pathToFile(path: String, storageManager: StateStorageManager) = File(storageManager.expandMacros("$ROOT_CONFIG/$path")) } - override fun pathToFile(path: String, storageManager: StateStorageManager) = File(storageManager.expandMacros("$ROOT_CONFIG/$path")) -} + private class ProjectSchemeManagerFactory(private val project: Project) : SchemeManagerFactoryBase() { + override val componentManager = project -private class ProjectSchemeManagerFactory(private val project: Project) : SchemeManagerFactoryBase() { - override val componentManager = project - - override fun pathToFile(path: String, storageManager: StateStorageManager) = File(project.basePath, if (ProjectUtil.isDirectoryBased(project)) "${Project.DIRECTORY_STORE_FOLDER}/$path" else ".$path") + override fun pathToFile(path: String, storageManager: StateStorageManager) = File(project.basePath, if (ProjectUtil.isDirectoryBased(project)) "${Project.DIRECTORY_STORE_FOLDER}/$path" else ".$path") + } } \ No newline at end of file diff --git a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt index 69c4e03e55a5..446902f7bee0 100644 --- a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt +++ b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt @@ -52,7 +52,7 @@ open class StateStorageManagerImpl(private val rootTagName: String, private val storageLock = ReentrantLock() private val storages = THashMap() - public var streamProvider: StreamProvider? = null + var streamProvider: StreamProvider? = null // access under storageLock private var isUseVfsListener = if (componentManager == null) ThreeState.NO else ThreeState.UNSURE // unsure because depends on stream provider state @@ -138,22 +138,16 @@ open class StateStorageManagerImpl(private val rootTagName: String, return if (path.endsWith('/')) path.substring(0, path.length() - 1) else path } - /** - * @param path Must be normalized (see normalizeFileSpec}) - * @return System-independent path - */ - open fun expandNormalizedPath(path: String): String = expandMacros(path) - - fun getOrCreateStorage(fileSpec: String, + fun getOrCreateStorage(collapsedPath: String, roamingType: RoamingType = RoamingType.DEFAULT, storageClass: Class = StateStorage::class.java, @Suppress("DEPRECATED_SYMBOL_WITH_MESSAGE") @SuppressWarnings("deprecation") stateSplitter: Class = StateSplitterEx::class.java): StateStorage { - val collapsedPath = normalizeFileSpec(fileSpec) - val key = if (storageClass == StateStorage::class.java) collapsedPath else storageClass.name + val normalizedCollapsedPath = normalizeFileSpec(collapsedPath) + val key = if (storageClass == StateStorage::class.java) normalizedCollapsedPath else storageClass.name storageLock.withLock { var storage = storages.get(key) if (storage == null) { - storage = createStateStorage(storageClass, collapsedPath, roamingType, stateSplitter) + storage = createStateStorage(storageClass, normalizedCollapsedPath, roamingType, stateSplitter) storages.put(key, storage) } return storage @@ -197,7 +191,7 @@ open class StateStorageManagerImpl(private val rootTagName: String, isUseVfsListener = ThreeState.fromBoolean(streamProvider == null || !streamProvider!!.enabled) } - val filePath = expandNormalizedPath(collapsedPath) + val filePath = expandMacros(collapsedPath) @Suppress("DEPRECATED_SYMBOL_WITH_MESSAGE") if (stateSplitter != StateSplitter::class.java && stateSplitter != StateSplitterEx::class.java) { val storage = MyDirectoryStorage(this, File(filePath), ReflectionUtil.newInstance(stateSplitter)) @@ -282,7 +276,7 @@ open class StateStorageManagerImpl(private val rootTagName: String, try { if (virtualFileTracker != null) { storages.forEachEntry({ collapsedPath, storage -> - virtualFileTracker.remove(expandNormalizedPath(collapsedPath)) + virtualFileTracker.remove(expandMacros(collapsedPath)) true }) } @@ -295,7 +289,7 @@ open class StateStorageManagerImpl(private val rootTagName: String, protected open fun getMacroSubstitutor(fileSpec: String): TrackingPathMacroSubstitutor? = pathMacroSubstitutor - override final fun expandMacros(path: String): String { + override fun expandMacros(path: String): String { // replacement can contains $ (php tests), so, this check must be performed before expand val matcher = MACRO_PATTERN.matcher(path) matcherLoop@ @@ -326,17 +320,17 @@ open class StateStorageManagerImpl(private val rootTagName: String, throw IllegalArgumentException("Unknown macro $macro") } - override final fun collapseMacros(path: String): String { + fun collapseMacros(path: String): String { var result = path for ((key, value) in macros) { result = StringUtil.replace(result, value, key) } - return result + return normalizeFileSpec(result) } override fun startExternalization() = StateStorageManagerExternalizationSession(this) - class StateStorageManagerExternalizationSession(protected val storageManager: StateStorageManagerImpl) : StateStorageManager.ExternalizationSession { + private class StateStorageManagerExternalizationSession(private val storageManager: StateStorageManagerImpl) : StateStorageManager.ExternalizationSession { private val sessions = LinkedHashMap() override fun setState(storageSpecs: Array, component: Any, componentName: String, state: Any) { diff --git a/platform/configuration-store-impl/testSrc/ApplicationStoreTest.kt b/platform/configuration-store-impl/testSrc/ApplicationStoreTest.kt index 6af9347b4ace..27ac1dbc5e2e 100644 --- a/platform/configuration-store-impl/testSrc/ApplicationStoreTest.kt +++ b/platform/configuration-store-impl/testSrc/ApplicationStoreTest.kt @@ -67,7 +67,7 @@ internal class ApplicationStoreTest { component.foo = "newValue" componentStore.save(SmartList()) - assertThat(streamProvider.data.get(RoamingType.DEFAULT)!!.get("new.xml")).isEqualTo("\n" + " \n" + "") + assertThat(streamProvider.data.get(RoamingType.DEFAULT)!!.get("new.xml")).isEqualTo("\n \n") } @Test fun `load from stream provider`() { @@ -83,7 +83,7 @@ internal class ApplicationStoreTest { componentStore.initComponent(component, false) assertThat(component.foo).isEqualTo("newValue") - assertThat(Paths.get(componentStore.storageManager.expandNormalizedPath(fileSpec))).isRegularFile() + assertThat(Paths.get(componentStore.storageManager.expandMacros(fileSpec))).isRegularFile() } @Test fun `remove deprecated storage on write`() { diff --git a/platform/configuration-store-impl/testSrc/StorageManagerTest.kt b/platform/configuration-store-impl/testSrc/StorageManagerTest.kt index 76d47dac3373..7f5a9ae3bc92 100644 --- a/platform/configuration-store-impl/testSrc/StorageManagerTest.kt +++ b/platform/configuration-store-impl/testSrc/StorageManagerTest.kt @@ -27,7 +27,7 @@ import org.junit.ClassRule import org.junit.Test import kotlin.properties.Delegates -class StorageManagerTest { +internal class StorageManagerTest { companion object { val MACRO = "\$MACRO1$" @@ -36,27 +36,27 @@ class StorageManagerTest { private var storageManager: StateStorageManagerImpl by Delegates.notNull() - public @Before fun setUp() { + @Before fun setUp() { storageManager = StateStorageManagerImpl("foo") storageManager.addMacro(MACRO, "/temp/m1") } - public @Test fun createFileStateStorageMacroSubstituted() { + @Test fun createFileStateStorageMacroSubstituted() { assertThat(storageManager.getOrCreateStorage("$MACRO/test.xml")).isNotNull() } - public @Test fun `collapse macro`() { + @Test fun `collapse macro`() { assertThat(storageManager.collapseMacros("/temp/m1/foo")).isEqualTo("$MACRO/foo") - assertThat(storageManager.collapseMacros("\\temp\\m1\\foo")).isEqualTo("\\temp\\m1\\foo") + assertThat(storageManager.collapseMacros("\\temp\\m1\\foo")).isEqualTo("/temp/m1/foo") } - public @Test fun `add system-dependent macro`() { + @Test fun `add system-dependent macro`() { val key = "\$INVALID$" val expansion = "\\temp" assertThatThrownBy({storageManager.addMacro(key, expansion) }).hasMessage("Macro $key set to system-dependent expansion $expansion") } - public @Test fun `create storage assertion thrown when unknown macro`() { + @Test fun `create storage assertion thrown when unknown macro`() { try { storageManager.getOrCreateStorage("\$UNKNOWN_MACRO$/test.xml") TestCase.fail("Exception expected") @@ -66,7 +66,7 @@ class StorageManagerTest { } } - public @Test fun `create file storage macro substituted when expansion has$`() { + @Test fun `create file storage macro substituted when expansion has$`() { storageManager.addMacro("\$DOLLAR_MACRO$", "/temp/d$") assertThat(storageManager.getOrCreateStorage("\$DOLLAR_MACRO$/test.xml")).isNotNull() } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java index 811ca228a6d2..f657aa4c95af 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java @@ -161,7 +161,14 @@ public class ExportSettingsAction extends AnAction implements DumbAware { } @NotNull - public static MultiMap getExportableComponentsMap(final boolean onlyExisting, final boolean computePresentableNames) { + public static MultiMap getExportableComponentsMap(boolean onlyExisting, boolean computePresentableNames) { + ApplicationImpl application = (ApplicationImpl)ApplicationManager.getApplication(); + StateStorageManager storageManager = ComponentsPackage.getStateStore(application).getStateStorageManager(); + return getExportableComponentsMap(onlyExisting, computePresentableNames, storageManager); + } + + @NotNull + public static MultiMap getExportableComponentsMap(final boolean onlyExisting, final boolean computePresentableNames, final @NotNull StateStorageManager storageManager) { @SuppressWarnings("deprecation") List components1 = ComponentsPackage.getComponents(ApplicationManager.getApplication(), ExportableApplicationComponent.class); List components2 = ServiceBean.loadServicesFromBeans(ExportableComponent.EXTENSION_POINT, ExportableComponent.class); @@ -180,9 +187,7 @@ public class ExportSettingsAction extends AnAction implements DumbAware { } } - ApplicationImpl application = (ApplicationImpl)ApplicationManager.getApplication(); - final StateStorageManager storageManager = ComponentsPackage.getStateStore(application).getStateStorageManager(); - ServiceManagerImpl.processAllImplementationClasses(application, new PairProcessor, PluginDescriptor>() { + ServiceManagerImpl.processAllImplementationClasses((ApplicationImpl)ApplicationManager.getApplication(), new PairProcessor, PluginDescriptor>() { @Override public boolean process(@NotNull Class aClass, @Nullable PluginDescriptor pluginDescriptor) { State stateAnnotation = StoreUtil.getStateSpec(aClass); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java index 03162b4f8005..82b1731b0932 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java @@ -48,12 +48,6 @@ public interface StateStorageManager { @NotNull String expandMacros(@NotNull String path); - @NotNull - /** - * @param path System-independent path. - */ - String collapseMacros(@NotNull String path); - interface ExternalizationSession { void setState(@NotNull Storage[] storageSpecs, @NotNull Object component, @NotNull String componentName, @NotNull Object state); diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 1a44e6525e4a..5c1e9ed3d08d 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -169,9 +169,9 @@ serviceImplementation="com.intellij.execution.process.impl.OSProcessManagerImpl"/> + serviceImplementation="com.intellij.configurationStore.SchemeManagerFactoryBase$ApplicationSchemeManagerFactory"/> + serviceImplementation="com.intellij.configurationStore.SchemeManagerFactoryBase$ProjectSchemeManagerFactory"/> () - private var sanitizedName: String? = null + private var sanitizedName: String by Delegates.notNull() override fun apply(base: Statement, description: Description): Statement { - sanitizedName = FileUtil.sanitizeFileName(description.getMethodName(), false) + sanitizedName = FileUtil.sanitizeFileName(description.methodName, false) return super.apply(base, description) } @@ -61,7 +61,7 @@ public class TemporaryDirectory : ExternalResource() { } private fun generatePath(suffix: String?): Path { - var fileName = sanitizedName!! + var fileName = sanitizedName if (suffix != null) { fileName += "_$suffix" } @@ -93,65 +93,4 @@ public fun generateTemporaryPath(fileName: String?): Path { throw IOException("Cannot generate unique random path") } return path -} - -public fun Path.exists(): Boolean = Files.exists(this) - -public fun Path.createDirectories(): Path = Files.createDirectories(this) - -public fun Path.deleteRecursively(): Path = if (exists()) Files.walkFileTree(this, object : SimpleFileVisitor() { - override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { - Files.delete(file) - return FileVisitResult.CONTINUE - } - - override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult { - Files.delete(dir) - return FileVisitResult.CONTINUE - } -}) else this - -public fun Path.getLastModifiedTime(): FileTime? = Files.getLastModifiedTime(this) - -public val Path.systemIndependentPath: String - get() = toString().replace(File.separatorChar, '/') - -public val Path.parentSystemIndependentPath: String - get() = getParent()!!.toString().replace(File.separatorChar, '/') - -public fun Path.readText(): String = Files.readAllBytes(this).toString(Charsets.UTF_8) - -public fun VirtualFile.writeChild(relativePath: String, data: String): VirtualFile = VfsTestUtil.createFile(this, relativePath, data) - -public fun Path.writeChild(relativePath: String, data: String): Path = writeChild(relativePath, data.toByteArray()) - -public fun Path.writeChild(relativePath: String, data: ByteArray): Path { - val path = resolve(relativePath) - path.getParent().createDirectories() - return Files.write(path, data) -} - -public fun Path.isDirectory(): Boolean = Files.isDirectory(this) - -public fun Path.isFile(): Boolean = Files.isRegularFile(this) - -/** - * Opposite to ugly Java, parent directories will be created - */ -public fun Path.createFile() { - getParent()?.createDirectories() - Files.createFile(this) -} - -public fun Path.refreshVfs() { - LocalFileSystem.getInstance()?.let { fs -> - // If a temp directory is reused from some previous test run, there might be cached children in its VFS. Ensure they're removed. - val virtualFile = fs.findFileByPath(systemIndependentPath) - if (virtualFile != null) { - VfsUtil.markDirtyAndRefresh(false, true, true, virtualFile) - } - } -} - -val VirtualFile.path: String - get() = getPath() +} \ No newline at end of file diff --git a/platform/testFramework/test-framework-java8/fsBuilder.kt b/platform/testFramework/test-framework-java8/fsBuilder.kt new file mode 100644 index 000000000000..ea32d71474af --- /dev/null +++ b/platform/testFramework/test-framework-java8/fsBuilder.kt @@ -0,0 +1,10 @@ +package com.intellij.testFramework + +import java.nio.file.FileSystem + +fun FileSystem.file(path: String, data: ByteArray): FileSystem { + getPath(path).write(data) + return this +} + +fun FileSystem.file(path: String, data: String) = file(path, data.toByteArray()) \ No newline at end of file diff --git a/platform/testFramework/test-framework-java8/path.kt b/platform/testFramework/test-framework-java8/path.kt new file mode 100644 index 000000000000..c7939e370422 --- /dev/null +++ b/platform/testFramework/test-framework-java8/path.kt @@ -0,0 +1,72 @@ +package com.intellij.testFramework + +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import java.io.File +import java.io.IOException +import java.nio.file.FileVisitResult +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes +import java.nio.file.attribute.FileTime + +fun Path.exists(): Boolean = Files.exists(this) + +fun Path.createDirectories(): Path = Files.createDirectories(this) + +fun Path.deleteRecursively(): Path = if (exists()) Files.walkFileTree(this, object : SimpleFileVisitor() { + override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { + Files.delete(file) + return FileVisitResult.CONTINUE + } + + override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult { + Files.delete(dir) + return FileVisitResult.CONTINUE + } +}) else this + +fun Path.getLastModifiedTime(): FileTime? = Files.getLastModifiedTime(this) + +val Path.systemIndependentPath: String + get() = toString().replace(File.separatorChar, '/') + +val Path.parentSystemIndependentPath: String + get() = parent!!.toString().replace(File.separatorChar, '/') + +fun Path.readText() = Files.readAllBytes(this).toString(Charsets.UTF_8) + +fun VirtualFile.writeChild(relativePath: String, data: String) = VfsTestUtil.createFile(this, relativePath, data) + +fun Path.writeChild(relativePath: String, data: ByteArray) = resolve(relativePath).write(data) + +fun Path.writeChild(relativePath: String, data: String) = writeChild(relativePath, data.toByteArray()) + +fun Path.write(data: ByteArray): Path { + parent?.createDirectories() + return Files.write(this, data) +} + +fun Path.isDirectory() = Files.isDirectory(this) + +fun Path.isFile() = Files.isRegularFile(this) + +/** + * Opposite to Java, parent directories will be created + */ +fun Path.createFile() { + parent?.createDirectories() + Files.createFile(this) +} + +fun Path.refreshVfs() { + LocalFileSystem.getInstance()?.let { fs -> + // If a temp directory is reused from some previous test run, there might be cached children in its VFS. Ensure they're removed. + val virtualFile = fs.findFileByPath(systemIndependentPath) + if (virtualFile != null) { + VfsUtil.markDirtyAndRefresh(false, true, true, virtualFile) + } + } +} \ No newline at end of file diff --git a/plugins/settings-repository/lib/dev/memoryfilesystem-0.6.7-sources.jar b/plugins/settings-repository/lib/dev/memoryfilesystem-0.6.7-sources.jar new file mode 100644 index 000000000000..3208f4cc636f Binary files /dev/null and b/plugins/settings-repository/lib/dev/memoryfilesystem-0.6.7-sources.jar differ diff --git a/plugins/settings-repository/lib/dev/memoryfilesystem-0.6.7.jar b/plugins/settings-repository/lib/dev/memoryfilesystem-0.6.7.jar new file mode 100644 index 000000000000..f65126e0440c Binary files /dev/null and b/plugins/settings-repository/lib/dev/memoryfilesystem-0.6.7.jar differ diff --git a/plugins/settings-repository/settings-repository-tests.iml b/plugins/settings-repository/settings-repository-tests.iml index 093c3f20dc5b..50acc4cea1bb 100644 --- a/plugins/settings-repository/settings-repository-tests.iml +++ b/plugins/settings-repository/settings-repository-tests.iml @@ -23,5 +23,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/settings-repository/src/autoSync.kt b/plugins/settings-repository/src/autoSync.kt index 808d13c5c46c..ddda5e05e34b 100644 --- a/plugins/settings-repository/src/autoSync.kt +++ b/plugins/settings-repository/src/autoSync.kt @@ -34,7 +34,7 @@ import com.intellij.openapi.vcs.VcsNotifier import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier import java.util.concurrent.Future -class AutoSyncManager(private val icsManager: IcsManager) { +internal class AutoSyncManager(private val icsManager: IcsManager) { private @Volatile var autoSyncFuture: Future<*>? = null fun waitAutoSync(indicator: ProgressIndicator) { diff --git a/plugins/settings-repository/src/copyAppSettingsToRepository.kt b/plugins/settings-repository/src/copyAppSettingsToRepository.kt index fe220a228ef1..d638b61f4303 100644 --- a/plugins/settings-repository/src/copyAppSettingsToRepository.kt +++ b/plugins/settings-repository/src/copyAppSettingsToRepository.kt @@ -15,6 +15,7 @@ */ package org.jetbrains.settingsRepository +import com.intellij.configurationStore.ROOT_CONFIG import com.intellij.configurationStore.StateStorageManagerImpl import com.intellij.ide.actions.ExportSettingsAction import com.intellij.openapi.application.ApplicationManager @@ -23,14 +24,14 @@ import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtilRt import java.io.File -fun copyLocalConfig() { - val storageManager = ApplicationManager.getApplication()!!.stateStore.stateStorageManager as StateStorageManagerImpl +fun copyLocalConfig(storageManager: StateStorageManagerImpl = ApplicationManager.getApplication()!!.stateStore.stateStorageManager as StateStorageManagerImpl) { val streamProvider = storageManager.streamProvider!! as IcsManager.IcsStreamProvider - val fileToComponents = ExportSettingsAction.getExportableComponentsMap(true, false) + val fileToComponents = ExportSettingsAction.getExportableComponentsMap(true, false, storageManager) for (file in fileToComponents.keySet()) { val absolutePath = FileUtilRt.toSystemIndependentName(file.absolutePath) var fileSpec = storageManager.collapseMacros(absolutePath) + LOG.assertTrue(!fileSpec.contains(ROOT_CONFIG)) if (fileSpec.equals(absolutePath)) { // we have not experienced such problem yet, but we are just aware val canonicalPath = FileUtilRt.toSystemIndependentName(file.canonicalPath) diff --git a/plugins/settings-repository/src/sync.kt b/plugins/settings-repository/src/sync.kt index a7dfedd96e74..4da0728f698e 100644 --- a/plugins/settings-repository/src/sync.kt +++ b/plugins/settings-repository/src/sync.kt @@ -83,13 +83,19 @@ class SyncManager(private val icsManager: IcsManager, private val autoSyncManage when (syncType) { SyncType.MERGE -> { updateResult = repositoryManager.pull(indicator) + var doPush = true if (localRepositoryInitializer != null) { // must be performed only after initial pull, so, local changes will be relative to remote files localRepositoryInitializer() - repositoryManager.commit(indicator) - updateResult = updateResult.concat(repositoryManager.pull(indicator)) + if (!repositoryManager.commit(indicator, syncType) || repositoryManager.getAheadCommitsCount() == 0) { + // avoid error during findRemoteRefUpdatesFor on push - if localRepositoryInitializer specified and nothing to commit (failed or just no files to commit (empty local configuration - no files)), + // so, nothing to push + doPush = false + } + } + if (doPush) { + repositoryManager.push(indicator) } - repositoryManager.push(indicator) } SyncType.OVERWRITE_LOCAL -> { // we don't push - probably, repository will be modified/removed (user can do something, like undo) before any other next push activities (so, we don't want to disturb remote) diff --git a/plugins/settings-repository/testSrc/GitTest.kt b/plugins/settings-repository/testSrc/GitTest.kt index 707f32ec1acd..49d07892b0ba 100644 --- a/plugins/settings-repository/testSrc/GitTest.kt +++ b/plugins/settings-repository/testSrc/GitTest.kt @@ -15,13 +15,15 @@ */ package org.jetbrains.settingsRepository.test +import com.intellij.configurationStore.ApplicationStoreImpl import com.intellij.configurationStore.write -import com.intellij.mock.MockVirtualFileSystem +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vcs.merge.MergeSession import com.intellij.openapi.vfs.CharsetToolkit import com.intellij.testFramework.ProjectRule -import com.intellij.testFramework.path +import com.intellij.testFramework.file +import com.intellij.testFramework.writeChild import com.intellij.util.PathUtilRt import org.assertj.core.api.Assertions.assertThat import org.eclipse.jgit.api.Git @@ -31,6 +33,7 @@ import org.jetbrains.jgit.dirCache.writePath import org.jetbrains.settingsRepository.CannotResolveConflictInTestMode import org.jetbrains.settingsRepository.SyncType import org.jetbrains.settingsRepository.conflictResolver +import org.jetbrains.settingsRepository.copyLocalConfig import org.jetbrains.settingsRepository.git.GitRepositoryManager import org.jetbrains.settingsRepository.git.commit import org.jetbrains.settingsRepository.git.computeIndexDiff @@ -39,16 +42,17 @@ import org.junit.ClassRule import org.junit.Test import java.io.File import java.nio.charset.StandardCharsets -import java.util.Arrays +import java.nio.file.FileSystem +import java.util.* import kotlin.properties.Delegates // kotlin bug, cannot be val (.NoSuchMethodError: org.jetbrains.settingsRepository.SettingsRepositoryPackage.getMARKER_ACCEPT_MY()[B) -object AM { +internal object AM { val MARKER_ACCEPT_MY: ByteArray = "__accept my__".toByteArray() val MARKER_ACCEPT_THEIRS: ByteArray = "__accept theirs__".toByteArray() } -class GitTest : IcsTestCase() { +internal class GitTest : IcsTestCase() { companion object { @ClassRule val projectRule = ProjectRule() } @@ -175,7 +179,7 @@ class GitTest : IcsTestCase() { repositoryManager.commit() repositoryManager.pull() assertThat(FileUtil.loadFile(File(repository.getWorkTree(), file.name))).isEqualTo(String(file.data, CharsetToolkit.UTF8_CHARSET)) - compareFiles(repository.workTree, remoteRepository.workTree, null, PathUtilRt.getFileName(file.name)) + compareFiles(repository.workTree, remoteRepository.workTree, PathUtilRt.getFileName(file.name)) } private fun createLocalRepository(remoteBranchName: String? = null) { @@ -188,15 +192,20 @@ class GitTest : IcsTestCase() { return addAndCommit("local.xml") } - private fun MockVirtualFileSystem.compare() { - compareFiles(repository.workTree, remoteRepository.workTree, root) + private fun FileSystem.compare(): FileSystem { + val root = getPath("/")!! + compareFiles(root, repository.workTree) + compareFiles(root, remoteRepository.workTree) + return this } // never was merged. we reset using "merge with strategy "theirs", so, we must test - what's happen if it is not first merge? - see next test @Test fun resetToTheirsIfFirstMerge() { createLocalRepositoryAndCommit(null) sync(SyncType.OVERWRITE_LOCAL) - fs().file(SAMPLE_FILE_NAME, SAMPLE_FILE_CONTENT).compare() + fs + .file(SAMPLE_FILE_NAME, SAMPLE_FILE_CONTENT) + .compare() } @Test fun resetToTheirsISecondMergeIsNull() { @@ -205,8 +214,6 @@ class GitTest : IcsTestCase() { restoreRemoteAfterPush() - val fs = MockVirtualFileSystem() - fun testRemote() { fs .file("local.xml", """""") @@ -232,7 +239,7 @@ class GitTest : IcsTestCase() { createLocalRepositoryAndCommit() sync(SyncType.OVERWRITE_REMOTE) restoreRemoteAfterPush() - fs().file("local.xml", """""").compare() + fs.file("local.xml", """""").compare() } @Test fun `reset to my, second merge is null`() { @@ -241,8 +248,10 @@ class GitTest : IcsTestCase() { restoreRemoteAfterPush() - val fs = fs().file("local.xml", """""").file(SAMPLE_FILE_NAME, SAMPLE_FILE_CONTENT) - fs.compare() + val fs = fs + .file("local.xml", """""") + .file(SAMPLE_FILE_NAME, SAMPLE_FILE_CONTENT) + .compare() val localToFilePath = "_mac/local2.xml" addAndCommit(localToFilePath) @@ -269,7 +278,7 @@ class GitTest : IcsTestCase() { sync(SyncType.MERGE) restoreRemoteAfterPush() - fs().file(SAMPLE_FILE_NAME, data.toString(StandardCharsets.UTF_8)).compare() + fs.file(SAMPLE_FILE_NAME, data.toString(StandardCharsets.UTF_8)).compare() } @Test fun `merge - theirs file deleted, my modified, accept theirs`() { @@ -286,7 +295,7 @@ class GitTest : IcsTestCase() { sync(SyncType.MERGE) - fs().compare() + fs.compare() } @Test fun `merge - my file deleted, theirs modified, accept my`() { @@ -294,16 +303,16 @@ class GitTest : IcsTestCase() { sync(SyncType.MERGE) - provider.delete("remote.xml") + provider.delete(SAMPLE_FILE_NAME) repositoryManager.commit() - remoteRepository.writePath("remote.xml", AM.MARKER_ACCEPT_THEIRS) + remoteRepository.writePath(SAMPLE_FILE_NAME, AM.MARKER_ACCEPT_THEIRS) remoteRepository.commit("") sync(SyncType.MERGE) restoreRemoteAfterPush() - fs().compare() + fs.compare() } @Test fun `commit if unmerged`() { @@ -327,7 +336,7 @@ class GitTest : IcsTestCase() { } sync(SyncType.MERGE) - fs().file(SAMPLE_FILE_NAME, SAMPLE_FILE_CONTENT).compare() + fs.file(SAMPLE_FILE_NAME, SAMPLE_FILE_CONTENT).compare() } // remote is uninitialized (empty - initial commit is not done) @@ -368,6 +377,51 @@ class GitTest : IcsTestCase() { } } + @Test fun `initial copy to repository: no local files`() { + // check error during findRemoteRefUpdatesFor (no master ref) + testInitialCopy(false) + } + + @Test fun `initial copy to repository: some local files`() { + // check error during findRemoteRefUpdatesFor (no master ref) + testInitialCopy(true) + } + + private fun testInitialCopy(addLocalFiles: Boolean) { + createRemoteRepository(initialCommit = false) + + repositoryManager.createRepositoryIfNeed() + repositoryManager.setUpstream(remoteRepository.getWorkTree().absolutePath) + + val store = ApplicationStoreImpl(ApplicationManager.getApplication()!!) + val localConfigPath = tempDirManager.newPath("local_config") + + val lafData = """ + + + """ + if (addLocalFiles) { + localConfigPath.writeChild("options/ui.lnf.xml", lafData) + } + + store.setPath(localConfigPath.toString()) + store.storageManager.streamProvider = provider + + icsManager.sync(SyncType.MERGE, projectRule.project, { copyLocalConfig(store.storageManager) }) + + if (addLocalFiles) { + assertThat(localConfigPath).isDirectory() + fs + .file("ui.lnf.xml", lafData) + restoreRemoteAfterPush() + } + else { + assertThat(localConfigPath).doesNotExist() + } + fs.compare() + } + private fun createRemoteRepository(branchName: String? = null, initialCommit: Boolean = true) { val repository = tempDirManager.createRepository("upstream") if (initialCommit) { @@ -396,7 +450,6 @@ class GitTest : IcsTestCase() { sync(syncType) - val fs = MockVirtualFileSystem() if (syncType != SyncType.OVERWRITE_LOCAL) { fs.file(path, data) } diff --git a/plugins/settings-repository/testSrc/IcsTestCase.kt b/plugins/settings-repository/testSrc/IcsTestCase.kt index c07ee3a10825..44ced1f59e2b 100644 --- a/plugins/settings-repository/testSrc/IcsTestCase.kt +++ b/plugins/settings-repository/testSrc/IcsTestCase.kt @@ -23,8 +23,8 @@ import org.jetbrains.jgit.dirCache.edit import org.jetbrains.settingsRepository.IcsManager import org.jetbrains.settingsRepository.git import org.junit.Rule +import java.nio.file.FileSystem import java.nio.file.Path -import kotlin.properties.Delegates fun Repository.add(path: String, data: String) = add(path, data.toByteArray()) @@ -46,6 +46,12 @@ abstract class IcsTestCase { val tempDirManager = TemporaryDirectory() @Rule fun getTemporaryFolder() = tempDirManager + private val fsRule = InMemoryFsRule() + @Rule fun _inMemoryFsRule() = fsRule + + val fs: FileSystem + get() = fsRule.fs + val icsManager by lazy(LazyThreadSafetyMode.NONE) { val icsManager = IcsManager(tempDirManager.newDirectory()) icsManager.repositoryManager.createRepositoryIfNeed() diff --git a/plugins/settings-repository/testSrc/RespositoryHelper.kt b/plugins/settings-repository/testSrc/RespositoryHelper.kt index 16bf2d86bf01..4ef156277328 100644 --- a/plugins/settings-repository/testSrc/RespositoryHelper.kt +++ b/plugins/settings-repository/testSrc/RespositoryHelper.kt @@ -15,31 +15,58 @@ */ package org.jetbrains.settingsRepository.test -import com.intellij.mock.MockVirtualFileSystem -import com.intellij.openapi.vfs.VfsUtilCore -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.testFramework.LightVirtualFile +import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder import com.intellij.testFramework.isFile import gnu.trove.THashSet import org.assertj.core.api.Assertions.assertThat import org.eclipse.jgit.lib.Constants +import org.junit.rules.ExternalResource +import org.junit.runner.Description +import org.junit.runners.model.Statement +import java.net.URLEncoder +import java.nio.file.FileSystem import java.nio.file.Files import java.nio.file.Path +import kotlin.properties.Delegates data class FileInfo(val name: String, val data: ByteArray) -fun fs() = MockVirtualFileSystem() +class InMemoryFsRule : ExternalResource() { + private var _fs: FileSystem? = null + + private var sanitizedName: String by Delegates.notNull() + + override fun apply(base: Statement, description: Description): Statement { + sanitizedName = URLEncoder.encode(description.methodName, Charsets.UTF_8.name()) + return super.apply(base, description) + } + + val fs: FileSystem + get() { + var r = _fs + if (r == null) { + r = MemoryFileSystemBuilder + .newLinux() + .setCurrentWorkingDirectory("/") + .build(sanitizedName) + _fs = r + } + return r!! + } + + override fun after() { + _fs?.close() + _fs = null + } +} private fun getChildrenStream(path: Path, excludes: Array? = null) = Files.list(path) .filter { !it.endsWith(Constants.DOT_GIT) && (excludes == null || !excludes.contains(it.fileName.toString())) } .sorted() -internal fun compareFiles(path1: Path, path2: Path, path3: VirtualFile? = null, vararg localExcludes: String) { +fun compareFiles(path1: Path, path2: Path, vararg localExcludes: String) { assertThat(path1).isDirectory() assertThat(path2).isDirectory() - if (path3 != null) { - assertThat(path3.isDirectory).isTrue() - } val notFound = THashSet() for (path in getChildrenStream(path1, localExcludes)) { @@ -47,21 +74,15 @@ internal fun compareFiles(path1: Path, path2: Path, path3: VirtualFile? = null, } for (child2 in getChildrenStream(path2)) { - val fileName = child2.fileName - val child1 = path1.resolve(fileName) - val child3 = path3?.findChild(fileName.toString()) + val child1 = path1.resolve(child2.fileName.toString()) if (child1.isFile()) { assertThat(child2).hasSameContentAs(child1) - if (child3 != null) { - assertThat(child3.isDirectory).isFalse() - assertThat(child1).hasContent(if (child3 is LightVirtualFile) child3.content.toString() else VfsUtilCore.loadText(child3)) - } } else { - compareFiles(child1, child2, child3, *localExcludes) + compareFiles(child1, child2, *localExcludes) } notFound.remove(child1) } - assertThat(notFound).isEmpty() + assertThat(notFound).`as`("Path '$path1' contains other files than '$path2'").isEmpty() } \ No newline at end of file