<NME> ElmWorkspaceServiceTest.kt
<BEF> ADDFILE
<MSG> Use elm.json files to understand app dependencies

Now that Elm 0.19 stores dependencies outside of
the elm-stuff directory and instead stores them
in a per-user cache (e.g. ~/.elm), it is necessary
to parse the `elm.json` file in order to understand
which packages an application depends on in order
to distinguish between other packages that should
not be in scope.

This implementation is merely meant as a starting
point. Current limitations:

- cannot attach multiple `elm.json` files
- cannot attach a package's `elm.json` files
  (only 'application' projects are supported right now)
- there is no UI for seeing which projects are attached
- the PsiReference resolve system does not fully leverage
  the information from the `elm.json` file. Specifically,
  it does not treat 'test-directories' differently, nor
  does it use 'source-directories' to create src roots,
  nor does it use a package's 'exposed-modules' to
  determine which modules are visible from outside of a
  package.

I added support for configuring the location of the
Elm binary, but right now it is not being used.
In the very near future we will need this in order to
properly construct a versioned path into the per-user
cache. And it will be useful for adding new features
like adding a run-config to launch Elm Reactor.

<DFF> @@ -0,0 +1,118 @@
+package org.elmSlowTests
+
+import org.elm.fileTree
+import org.elm.openapiext.elementFromXmlString
+import org.elm.openapiext.pathAsPath
+import org.elm.openapiext.toXmlString
+import org.elm.workspace.ElmWorkspaceTestBase
+import org.elm.workspace.elmWorkspace
+
+class ElmWorkspaceServiceTest: ElmWorkspaceTestBase() {
+
+
+    fun `test finds Elm project for source file`() {
+        val testProject = fileTree {
+            dir("a") {
+                project("elm.json", """
+                    {
+                      "type": "application",
+                      "source-directories": [ "src" ],
+                      "elm-version": "0.19.0",
+                      "dependencies": { },
+                      "test-dependencies": {},
+                      "do-not-edit-this-by-hand": {
+                        "transitive-dependencies": { }
+                      }
+                    }
+                    """)
+                dir("src") {
+                    elm("Main.elm", "")
+                    elm("Utils.elm", "")
+                }
+            }
+        }.create(project, elmWorkspaceDirectory)
+
+        val rootPath = testProject.root.pathAsPath
+        val workspace = project.elmWorkspace.apply {
+            attachElmProject(rootPath.resolve("a/elm.json"))
+        }
+
+        fun checkFile(relativePath: String, projectName: String?) {
+            val vFile = testProject.root.findFileByRelativePath(relativePath)!!
+            val project = workspace.findProjectForFile(vFile)
+            if (project?.presentableName != projectName) {
+                error("Expected $projectName, found $project for $relativePath")
+            }
+        }
+
+        checkFile("a/src/Main.elm", "a")
+        checkFile("a/src/Utils.elm", "a")
+    }
+
+
+    fun `test persistence`() {
+        // setup real files on disk
+        val testProject = fileTree {
+            dir("a") {
+                project("elm.json", """
+                    {
+                      "type": "application",
+                      "source-directories": [ "src" ],
+                      "elm-version": "0.19.0",
+                      "dependencies": { },
+                      "test-dependencies": {},
+                      "do-not-edit-this-by-hand": {
+                        "transitive-dependencies": { }
+                      }
+                    }
+                    """)
+                dir("src") {
+                    elm("Main.elm", "")
+                }
+            }
+        }.create(project, elmWorkspaceDirectory)
+
+        val workspace = project.elmWorkspace
+        val rootPath = testProject.root.pathAsPath
+
+        // The known-good, serialized state that we must be able to handle
+        val xml = """
+            <state>
+              <elmProjects>
+                <project path="$rootPath/a/elm.json" />
+              </elmProjects>
+              <settings binDirPath="/usr/local/bin" />
+            </state>
+            """.trimIndent()
+
+        // ... must be able to load from serialized state ...
+        workspace.loadState(elementFromXmlString(xml))
+        val actualProjects = workspace.allProjects.map { it.manifestPath.toString() }
+        val expectedProjects = listOf("$rootPath/a/elm.json")
+        checkEquals(expectedProjects, actualProjects)
+
+        // ... and serialize the resulting state ...
+        val actualXml = workspace.state.toXmlString()
+        checkEquals(xml, actualXml)
+    }
+
+
+    fun `test persistence with empty toolchain binDirPath`() {
+        val workspace = project.elmWorkspace
+
+        // The missing binDirPath is treated as the empty string
+        val xml = """
+            <state>
+              <elmProjects />
+              <settings binDirPath="" />
+            </state>
+            """.trimIndent()
+
+        // ... must be able to load from serialized state ...
+        workspace.loadState(elementFromXmlString(xml))
+
+        // ... and serialize the resulting state ...
+        val actualXml = workspace.state.toXmlString()
+        checkEquals(xml, actualXml)
+    }
+}

Use elm.json files to understand app dependencies