Migrates Treeview.scala to use Contraband
This commit is contained in:
eugene yokota 2021-11-12 19:04:10 +01:00 committed by GitHub
commit be76165054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 129 additions and 50 deletions

View File

@ -10,24 +10,20 @@ package internal
package graph
package rendering
import sbt.internal.graph.codec.JsonProtocol.ModuleModelFormat
import sbt.io.IO
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.io.{ File, FileOutputStream, InputStream, OutputStream }
import java.net.URI
import scala.annotation.nowarn
import scala.annotation.tailrec
import scala.util.parsing.json.JSONArray
import scala.util.parsing.json.JSONObject
import scala.annotation.{ nowarn, tailrec }
@nowarn object TreeView {
def createJson(graph: ModuleGraph): String = {
val trees = graph.roots
val moduleModels = graph.roots
.map(module => processSubtree(graph, module))
.toList
JSONArray(trees).toString
val js = moduleModels.map(Converter.toJsonUnsafe(_))
js.map(CompactPrinter).mkString("[", ",", "]")
}
def createLink(graphJson: String, targetDirectory: File): URI = {
@ -43,24 +39,26 @@ import scala.util.parsing.json.JSONObject
graph: ModuleGraph,
module: Module,
parents: Set[GraphModuleId] = Set()
): JSONObject = {
): ModuleModel = {
val cycle = parents.contains(module.id)
val dependencies = if (cycle) List() else graph.dependencyMap.getOrElse(module.id, List())
val children =
dependencies.map(dependency => processSubtree(graph, dependency, parents + module.id)).toList
moduleAsJson(module, cycle, children)
dependencies
.map(dependency => processSubtree(graph, dependency, parents + module.id))
.toVector
moduleAsModuleAgain(module, cycle, children)
}
private def moduleAsJson(
private def moduleAsModuleAgain(
module: Module,
isCycle: Boolean,
children: List[JSONObject]
): JSONObject = {
children: Vector[ModuleModel]
): ModuleModel = {
val eviction = module.evictedByVersion.map(version => s" (evicted by $version)").getOrElse("")
val cycle = if (isCycle) " (cycle)" else ""
val error = module.error.map(err => s" (errors: $err)").getOrElse("")
val text = module.id.idString + eviction + error + cycle
JSONObject(Map("text" -> text, "children" -> JSONArray(children)))
ModuleModel(text, children)
}
def saveResource(resourcePath: String, to: File): Unit = {

View File

@ -5,51 +5,48 @@
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package graph
package rendering
package sbt.internal.graph.rendering
import org.scalatest.DiagrammedAssertions
import org.scalatest.FunSuite
import scala.annotation.nowarn
import scala.util.parsing.json.JSONArray
import scala.util.parsing.json.JSONObject
@nowarn("msg=class JSONObject in package json is deprecated")
class TreeViewTest extends FunSuite with DiagrammedAssertions {
import org.scalatest.{ FlatSpec, Matchers }
import sbt.internal.graph.rendering.TreeView.createJson
import sbt.internal.graph.{ GraphModuleId, Module, ModuleGraph, ModuleModel }
class TreeViewTest extends FlatSpec with Matchers {
val modA = GraphModuleId("orgA", "nameA", "1.0")
val modB = GraphModuleId("orgB", "nameB", "2.0")
val modC = GraphModuleId("orgC", "nameC", "3.0")
val graph = ModuleGraph(
nodes = Seq(Module(modA), Module(modB)),
nodes = Seq(Module(modA), Module(modB), Module(modC)),
edges = Seq(
modA -> modA,
modA -> modB,
modC -> modA,
)
)
test("TreeView should detect cycles and truncate") {
val json = TreeView.processSubtree(graph, Module(modA))
val (rootText, children) = parseTree(json)
assert(rootText == modA.idString)
val childrenText = children.map(parseTree).map(_._1)
val expected = List(s"${modA.idString} (cycle)", modB.idString)
assert(childrenText == expected)
"createJson" should "convert ModuleGraph into JSON correctly" in {
val expected =
"[{\"text\":\"orgC:nameC:3.0\",\"children\":[{\"text\":\"orgA:nameA:1.0\",\"children\":[{\"text\":\"orgA:nameA:1.0 (cycle)\",\"children\":[]},{\"text\":\"orgB:nameB:2.0\",\"children\":[]}]}]}]"
Predef.assert(
createJson(graph) == expected,
s"Expected $expected, but got ${createJson(graph)}"
)
}
@nowarn("cat=unchecked")
def parseTree(json: JSONObject): (String, List[JSONObject]) = {
(json.obj.get("text"), json.obj.get("children")) match {
case (Some(text: String), Some(JSONArray(children: List[JSONObject])))
if children.forall(_.isInstanceOf[JSONObject]) =>
text -> children
case _ =>
fail("a string field 'text' and an array of objects in 'children' field were expected!")
}
"processSubtree" should "detect cycles and truncate" in {
val expected = ModuleModel(
"orgC:nameC:3.0",
Vector(
ModuleModel(
"orgA:nameA:1.0",
Vector(
ModuleModel("orgA:nameA:1.0 (cycle)", Vector()),
ModuleModel("orgB:nameB:2.0", Vector())
)
)
)
)
assert(TreeView.processSubtree(graph, Module(modC), Set()) == expected)
}
}

View File

@ -11,3 +11,4 @@ addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.14")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1")
addSbtPlugin("com.swoval" % "sbt-java-format" % "0.3.1")
addDependencyTreePlugin

View File

@ -0,0 +1,36 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.graph
final class ModuleModel private (
val text: String,
val children: Vector[sbt.internal.graph.ModuleModel]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: ModuleModel => (this.text == x.text) && (this.children == x.children)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.graph.ModuleModel".##) + text.##) + children.##)
}
override def toString: String = {
"ModuleModel(" + text + ", " + children + ")"
}
private[this] def copy(text: String = text, children: Vector[sbt.internal.graph.ModuleModel] = children): ModuleModel = {
new ModuleModel(text, children)
}
def withText(text: String): ModuleModel = {
copy(text = text)
}
def withChildren(children: Vector[sbt.internal.graph.ModuleModel]): ModuleModel = {
copy(children = children)
}
}
object ModuleModel {
def apply(text: String, children: Vector[sbt.internal.graph.ModuleModel]): ModuleModel = new ModuleModel(text, children)
}

View File

@ -0,0 +1,9 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.graph.codec
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.graph.codec.ModuleModelFormats
object JsonProtocol extends JsonProtocol

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.graph.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ModuleModelFormats { self: sbt.internal.graph.codec.ModuleModelFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ModuleModelFormat: JsonFormat[sbt.internal.graph.ModuleModel] = new JsonFormat[sbt.internal.graph.ModuleModel] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.graph.ModuleModel = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val text = unbuilder.readField[String]("text")
val children = unbuilder.readField[Vector[sbt.internal.graph.ModuleModel]]("children")
unbuilder.endObject()
sbt.internal.graph.ModuleModel(text, children)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.graph.ModuleModel, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("text", obj.text)
builder.addField("children", obj.children)
builder.endObject()
}
}
}

View File

@ -0,0 +1,9 @@
package sbt.internal.graph
@target(Scala)
@codecPackage("sbt.internal.graph.codec")
@fullCodec("JsonProtocol")
type ModuleModel {
text: String!
children: [sbt.internal.graph.ModuleModel]
}