summaryrefslogtreecommitdiff
path: root/platform/workspaceModel/jps/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectReloadingTest.kt
blob: 0f3593adacdff7ee85cfbf24831d5ab7bf7d2d76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package com.intellij.workspaceModel.ide.impl.jps.serialization

import com.intellij.openapi.application.ex.PathManagerEx
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.HeavyPlatformTestCase
import com.intellij.workspaceModel.ide.getInstance
import com.intellij.workspaceModel.storage.WorkspaceEntityStorageBuilder
import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity
import com.intellij.workspaceModel.storage.bridgeEntities.projectLibraries
import com.intellij.workspaceModel.storage.url.VirtualFileUrlManager
import org.jetbrains.jps.util.JpsPathUtil
import java.io.File

class JpsProjectReloadingTest : HeavyPlatformTestCase() {
  private lateinit var virtualFileManager: VirtualFileUrlManager

  override fun setUp() {
    super.setUp()
    virtualFileManager = VirtualFileUrlManager.getInstance(project)
  }

  fun `test modify iml`() {
    checkProjectAfterReload("common/modifyIml", "common/modifyIml") { (storage, projectDirUrl) ->
      val modules = storage.entities(ModuleEntity::class.java).sortedBy { it.name }.toList()
      assertEquals(3, modules.size)
      val utilModule = modules[1]
      assertEquals("util", utilModule.name)
      val utilModuleSrc = assertOneElement(utilModule.sourceRoots.toList())
      assertEquals("$projectDirUrl/util/src2", utilModuleSrc.url.url)
      assertEquals("""<component>
  <annotation-paths>
    <root url="$projectDirUrl/lib/anno2" />
  </annotation-paths>
  <javadoc-paths>
    <root url="$projectDirUrl/lib/javadoc2" />
  </javadoc-paths>
</component>""", utilModule.customImlData!!.rootManagerTagCustomData)
    }
  }

  fun `test add library`() {
    checkProjectAfterReload( "directoryBased/addLibrary", "fileBased/addLibrary") { (storage, projectDirUrl) ->
      val libraries = storage.projectLibraries.sortedBy { it.name }.toList()
      assertEquals(4, libraries.size)
      val junitLibrary = libraries[2]
      assertEquals("junit2", junitLibrary.name)
      val root = assertOneElement(junitLibrary.roots.toList())
      assertEquals("jar://${JpsPathUtil.urlToPath(projectDirUrl)}/lib/junit2.jar!/", root.url.url)
    }
  }

  fun `test add all libraries for directory based project`() {
    val projectDir = File(PathManagerEx.getCommunityHomePath(), "jps/model-serialization/testData/imlUnderDotIdea")
    val (storage, projectDirUrl) = reload(projectDir, "directoryBased/addLibrary")

    val libraries = storage.projectLibraries.sortedBy { it.name }.toList()
    assertEquals(1, libraries.size)
    val junitLibrary = libraries[0]
    assertEquals("junit2", junitLibrary.name)
    val root = assertOneElement(junitLibrary.roots.toList())
    assertEquals("jar://${JpsPathUtil.urlToPath(projectDirUrl)}/lib/junit2.jar!/", root.url.url)
  }

  fun `test add module`() {
    checkProjectAfterReload("directoryBased/addModule", "fileBased/addModule") { (storage, projectDirUrl) ->
      val modules = storage.entities(ModuleEntity::class.java).sortedBy { it.name }.toList()
      assertEquals(4, modules.size)
      val newModule = modules[1]
      assertEquals("newModule", newModule.name)
      val root = assertOneElement(newModule.sourceRoots.toList())
      assertEquals("$projectDirUrl/new", root.url.url)
    }
  }

  fun `test remove module`() {
    checkProjectAfterReload("directoryBased/removeModule", "fileBased/removeModule") { (storage, _) ->
      assertEquals(setOf("main", "xxx"), storage.entities(ModuleEntity::class.java).mapTo(HashSet()) {it.name})
    }
  }

  fun `test modify library`() {
    checkProjectAfterReload("directoryBased/modifyLibrary", "fileBased/modifyLibrary") { (storage, projectDirUrl) ->
      val libraries = storage.projectLibraries.sortedBy { it.name }.toList()
      assertEquals(3, libraries.size)
      val junitLibrary = libraries[1]
      assertEquals("junit", junitLibrary.name)
      val root = assertOneElement(junitLibrary.roots.toList())
      assertEquals("jar://${JpsPathUtil.urlToPath(projectDirUrl)}/lib/junit2.jar!/", root.url.url)
    }
  }

  fun `test remove library`() {
    checkProjectAfterReload("directoryBased/removeLibrary", "fileBased/removeLibrary") { (storage, _) ->
      assertEquals(setOf("jarDir", "log4j"), storage.projectLibraries.mapTo(HashSet()) {it.name})
    }
  }

  fun `test remove all libraries`() {
    checkProjectAfterReload("directoryBased/removeAllLibraries", "fileBased/removeAllLibraries") { (storage, _) ->
      assertEquals(emptySet<String>(), storage.projectLibraries.mapTo(HashSet()) {it.name})
    }
  }

  private fun checkProjectAfterReload(directoryNameForDirectoryBased: String,
                                      directoryNameForFileBased: String,
                                      checkAction: (ReloadedProjectData) -> Unit) {
    val dirBasedData = reload(sampleDirBasedProjectFile, directoryNameForDirectoryBased)
    checkAction(dirBasedData)
    val fileBasedData = reload(sampleFileBasedProjectFile, directoryNameForFileBased)
    checkAction(fileBasedData)
  }

  private fun reload(originalProjectFile: File,
                     updateAction: (LoadedProjectData) -> JpsConfigurationFilesChange): ReloadedProjectData {
    val projectData = copyAndLoadProject(originalProjectFile, virtualFileManager)
    val change = updateAction(projectData)
    val (changedEntities, builder) =
      projectData.serializers.reloadFromChangedFiles(change, CachingJpsFileContentReader(projectData.configLocation), TestErrorReporter)
    val originalBuilder = WorkspaceEntityStorageBuilder.from(projectData.storage)
    originalBuilder.replaceBySource({it in changedEntities}, builder)
    projectData.serializers.checkConsistency(projectData.configLocation, originalBuilder, virtualFileManager)
    return ReloadedProjectData(originalBuilder, projectData.projectDirUrl)
  }

  private fun reload(originalProjectDir: File, directoryName: String): ReloadedProjectData {
    return reload(originalProjectDir) { projectData ->
      val changedDir = PathManagerEx.findFileUnderCommunityHome("platform/workspaceModel/jps/tests/testData/serialization/reload/$directoryName")
      val newUrls = collectFileUrlsRec(changedDir, projectData.projectDirUrl, true) { it != "<delete/>"}
      val urlsToDelete = collectFileUrlsRec(changedDir, projectData.projectDirUrl, true) { it == "<delete/>"}
      val oldUrls = collectFileUrlsRec(projectData.projectDir, projectData.projectDirUrl, false) { true }
      FileUtil.copyDir(changedDir, projectData.projectDir)
      projectData.projectDir.walk().filter { it.isFile && it.readText().trim() == "<delete/>" }.forEach {
        FileUtil.delete(it)
      }

      JpsConfigurationFilesChange(addedFileUrls = newUrls - oldUrls, removedFileUrls = urlsToDelete,
                                  changedFileUrls = newUrls.intersect(oldUrls).toList())
    }
  }

  private fun collectFileUrlsRec(dir: File,
                                 baseUrl: String,
                                 replaceByParent: Boolean,
                                 contentFilter: (String) -> Boolean): Set<String> {
    val res = HashSet<String>()

    dir.walkBottomUp().forEach { file ->
      if (file.isFile && contentFilter(file.readText().trim())) {
        res += replaceBaseUrl(baseUrl, dir, file)
      }
      else if (file.isDirectory) {
        // If all removed files relate to the one directory, the directory event is sent
        val relativeFileUrl = FileUtil.getRelativePath(dir, file) ?: return@forEach
        if ("." == relativeFileUrl) return@forEach
        if (!relativeFileUrl.endsWith(".idea/libraries") && !relativeFileUrl.endsWith(".idea/artifacts")) return@forEach

        // Existing files + files that will be copied
        val children = (JpsPathUtil.urlToFile(replaceBaseUrl(baseUrl, dir, file)).listFiles()?.map { JpsPathUtil.pathToUrl(it.absolutePath) } ?: emptyList()) +
                       (file.listFiles()?.map { replaceBaseUrl(baseUrl, dir, it) } ?: emptyList())
        if (children.all { it in res }) {
          if (replaceByParent) children.forEach { res.remove(it) }
          res += replaceBaseUrl(baseUrl, dir, file)
        }
      }
    }

    return res
  }

  private fun replaceBaseUrl(newBaseUrl: String, oldBaseUrl: File, file: File): String {
    return "$newBaseUrl/${FileUtil.toSystemIndependentName(FileUtil.getRelativePath(oldBaseUrl, file)!!)}"
  }

  private data class ReloadedProjectData(val storage: WorkspaceEntityStorageBuilder, val projectDirUrl: String)
}