Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 9 additions & 19 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
lazy val netLogoVersion = "7.0.0-internal1"
lazy val netLogoVersion = "7.0.0-internal1-6784458"

scalaVersion := "2.12.18"
scalaVersion := "2.13.16"

scalacOptions ++= Seq("-feature", "-unchecked", "-deprecation")
scalacOptions ++= Seq("-feature", "-unchecked", "-deprecation", "-Xfatal-warnings")

resolvers ++= Seq(
"Typesafe Repo" at "https://repo.typesafe.com/typesafe/releases/"
Expand All @@ -13,38 +13,28 @@ javaOptions ++= Seq(
"-Dorg.nlogo.onLocal=" + Option(System.getProperty("org.nlogo.onLocal")).getOrElse("false"),
"-Dorg.nlogo.is3d=" + Option(System.getProperty("org.nlogo.is3d")).getOrElse("false"),
"-Dcom.sun.media.jai.disableMediaLib=true", // see https://github.com/NetLogo/GIS-Extension/issues/4
"-Dnetlogo.extensions.dir=" + (baseDirectory.value.getParentFile / "extensions").getAbsolutePath.toString,
"-Xmx4G"
)

fork := true

lazy val installExtensions = TaskKey[Unit]("install bundled extensions so we can test models that use them")
installExtensions := {
val main = "org.nlogo.workspace.ExtensionInstaller"
// We even install the extensions we can't or won't test just to avoid compile errors with them.
// -Jeremy B May 2021
val args = Seq("arduino", "array", "bitmap", "csv", "gis", "gogo", "ls", "matrix", "nw", "palette", "profiler", "py", "r", "rnd", "sound", "table", "time", "vid", "view2.5d")
val s = streams.value
val cp = (Test / dependencyClasspath).value
(Test / run / runner).value.run(main, cp.files, args, s.log)
}

(Test / test) := (Test / test).dependsOn(installExtensions).value

libraryDependencies ++= Seq(
"org.nlogo" % "netlogo" % netLogoVersion,
"org.scalatest" %% "scalatest" % "3.0.1" % Test,
"org.scalatest" %% "scalatest" % "3.2.19" % Test,
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.2.0",
"commons-io" % "commons-io" % "2.4",
"commons-validator" % "commons-validator" % "1.4.1",
"org.jogamp.jogl" % "jogl-all" % "2.4.0" from "https://jogamp.org/deployment/archive/rc/v2.4.0/jar/jogl-all.jar",
"org.jogamp.gluegen" % "gluegen-rt" % "2.4.0" from "https://jogamp.org/deployment/archive/rc/v2.4.0/jar/gluegen-rt.jar",
"org.apache.commons" % "commons-lang3" % "3.5",
"org.asynchttpclient" % "async-http-client" % "2.12.3",
"com.github.wookietreiber" %% "scala-chart" % "0.5.1",
"com.googlecode.java-diff-utils" % "diffutils" % "1.2" % "test",
"org.jfree" % "jfreechart" % "1.0.19",
"org.jfree" % "jfreesvg" % "3.0",
"com.typesafe" % "config" % "1.3.1" % "test",
"com.vladsch.flexmark" % "flexmark" % "0.20.0" % "test",
"com.vladsch.flexmark" % "flexmark-ext-autolink" % "0.20.0" % "test",
"com.vladsch.flexmark" % "flexmark-util" % "0.20.0" % "test"
"com.vladsch.flexmark" % "flexmark-util" % "0.20.0" % "test",
"org.json4s" %% "json4s-jackson" % "4.0.7"
)
4 changes: 2 additions & 2 deletions src/main/scala/org/nlogo/models/LegalInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ case class LegalInfo(model: Model) {
builder.append("Center for Connected Learning and Computer-Based Modeling, ")
if (model.isIABM) builder.append("Northwestern Institute on Complex Systems, ")
builder.append("Northwestern University, Evanston, IL.")
builder.result
builder.result()
}

val acknowledgment: Option[String] = {
Expand Down Expand Up @@ -143,7 +143,7 @@ case class LegalInfo(model: Model) {
builder.append("Design guidelines for agent based model visualization. ")
builder.append("Journal of Artificial Societies and Social Simulation (JASSS), 12(2), 1. ")
builder.append("http://ccl.northwestern.edu/papers/2009/Kornhauser,Wilensky&Rand_DesignGuidelinesABMViz.pdf.")
Some(builder.result)
Some(builder.result())
case _ => None
}
}
Expand Down
64 changes: 28 additions & 36 deletions src/main/scala/org/nlogo/models/Stats.scala
Original file line number Diff line number Diff line change
@@ -1,73 +1,65 @@
package org.nlogo.models

import java.awt.Font
import java.awt.{ Dimension, Font, Rectangle }
import java.io.File
import java.lang.RuntimeException
import java.util.Date

import org.jfree.chart.labels.ItemLabelAnchor
import org.jfree.chart.labels.ItemLabelPosition
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator
import org.jfree.chart.{ ChartFactory, JFreeChart }
import org.jfree.data.category.DefaultCategoryDataset
import org.jfree.chart.labels.{ ItemLabelAnchor, ItemLabelPosition, StandardCategoryItemLabelGenerator }
import org.jfree.chart.plot.PlotOrientation
import org.jfree.chart.renderer.category.BarRenderer
import org.jfree.chart.title.TextTitle
import org.jfree.graphics2d.svg.SVGGraphics2D
import org.jfree.graphics2d.svg.SVGUtils
import org.jfree.ui.TextAnchor

import scalax.chart.Chart
import scalax.chart.api.BarChart

object Stats {

def exportPrimitivesUsagePlot(): Unit = {

val data = libraryModels
.flatMap(model =>
try new Tokens(model).primitiveTokenNames.distinct.map(_ -> model)
catch {
case e: java.lang.RuntimeException =>
Console.err.println(model.file.getPath)
e.printStackTrace()
Seq.empty
}
)
.groupBy(_._1).mapValues(_.size)
.toSeq
.sortBy(t => (0 - t._2, t._1))
val data = new DefaultCategoryDataset

libraryModels.flatMap { model =>
try {
new Tokens(model).primitiveTokenNames.distinct.map(_ -> model)
} catch {
case e: RuntimeException =>
Console.err.println(model.file.getPath)
e.printStackTrace()
Seq()
}
}.groupBy(_._1).view.mapValues(_.size).toSeq.sortBy(t => (0 - t._2, t._1)).foreach {
case (name, count) => data.addValue(count, "", name)
case _ =>
}

val chart = BarChart(data)
chart.title = "Usage of primitives in the Models Library"
chart.peer.removeLegend()
chart.peer.addSubtitle(new TextTitle(new Date().toString))
val chart = ChartFactory.createBarChart("Usage of primitives in the Models Library", null, "Number of models",
data, PlotOrientation.HORIZONTAL, false, false, false)
chart.addSubtitle(new TextTitle(new Date().toString))

val renderer = new BarRenderer
renderer.setShadowVisible(false)
renderer.setDrawBarOutline(true)
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator)
renderer.setBaseItemLabelsVisible(true)
renderer.setBasePositiveItemLabelPosition(
new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.CENTER_LEFT)
)
renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.CENTER_LEFT))

val plot = chart.peer.getCategoryPlot
val plot = chart.getCategoryPlot
plot.setRenderer(renderer)
plot.setOrientation(PlotOrientation.HORIZONTAL)
plot.getRangeAxis.setLabel("Number of models")
plot.getDomainAxis.setTickLabelFont(new Font("Monospaced", Font.PLAIN, 12))
plot.getDomainAxis.setUpperMargin(0.005)
plot.getDomainAxis.setLowerMargin(0.005)
saveAsSVG(chart, "test/stats/usage_of_primitives.svg", (1000, 4500))
saveAsSVG(chart, "test/stats/usage_of_primitives.svg", 1000, 4500)
}

// shouldn't be needed anymore once
// https://github.com/wookietreiber/scala-chart/issues/12
// makes it into a scala-chart release
def saveAsSVG(chart: Chart, file: String, resolution: (Int, Int)): Unit = {
val (width, height) = resolution
def saveAsSVG(chart: JFreeChart, file: String, width: Int, height: Int): Unit = {
val g2 = new SVGGraphics2D(width, height)
chart.peer.draw(g2, new java.awt.Rectangle(
new java.awt.Dimension(width, height))
)
chart.draw(g2, new Rectangle(new Dimension(width, height)))
val svg = g2.getSVGElement
g2.dispose()
SVGUtils.writeToSVG(new File(file), svg)
Expand Down
10 changes: 5 additions & 5 deletions src/main/scala/org/nlogo/models/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import org.nlogo.headless.{ HeadlessWorkspace, Main }
import org.nlogo.models.InfoTabParts

import scala.Boolean
import scala.collection.JavaConverters.collectionAsScalaIterableConverter
import scala.jdk.CollectionConverters.CollectionHasAsScala
import scala.util.Try

package object models {
Expand Down Expand Up @@ -56,20 +56,20 @@ package object models {
(for {
(file, modelTry, _) <- modelTries
model <- modelTry.toOption
} yield model -> file)(collection.breakOut)
} yield model -> file).toMap

val modelContent: Map[Model, String] =
(for {
(file, modelTry, contentTry) <- modelTries
model <- modelTry.toOption
content <- contentTry.toOption
} yield model -> content)(collection.breakOut)
} yield model -> content).toMap

val allModels: Iterable[Model] = modelFiles.keys.toSeq
val libraryModels: Iterable[Model] = allModels.filterNot(_.isTestModel)

val allModelsNamed: Map[Model, String] = modelFiles.mapValues(_.getName)
val libraryModelsNamed: Map[Model, String] = modelFiles.mapValues(_.getName)
val allModelsNamed: Map[Model, String] = modelFiles.view.mapValues(_.getName).toMap
val libraryModelsNamed: Map[Model, String] = modelFiles.view.mapValues(_.getName).toMap

implicit class EnrichedModel(val model: Model) {
def file = modelFiles(model)
Expand Down
11 changes: 6 additions & 5 deletions src/test/scala/org/nlogo/models/ButtonTests.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.nlogo.models

import scala.collection.parallel.ParMap
import scala.collection.parallel.CollectionConverters.IterableIsParallelizable
import scala.util.Try

import org.nlogo.api.SimpleJobOwner
Expand All @@ -14,7 +15,7 @@ import org.nlogo.core.Model

class ButtonTests extends TestModels {

val models = libraryModelsNamed.filterKeys { model =>
val models = libraryModelsNamed.view.filterKeys { model =>
(model.is3D == Version.is3D) &&
(model.isCompilable) &&
(!Set(
Expand All @@ -24,7 +25,7 @@ class ButtonTests extends TestModels {
"2.5d Patch View Example", "2.5d Turtle View Example", // https://github.com/NetLogo/View2.5D/issues/4
"Python Flocking Clusters" // issues with sklearn install on GitHub Actions
).contains(model.name))
}.par
}.par.toMap

def run(model: Model, button: Button): Try[World] = Try {
withWorkspace(model) { ws =>
Expand Down Expand Up @@ -55,9 +56,9 @@ class ButtonTests extends TestModels {
val buttons: ParMap[Model, Iterable[Button]] =
models.map { case (model, _) =>
model -> model.widgets.collect { case b: Button => b }
}(collection.breakOut)
}.toMap

testModels(models, "Buttons should be disabled until ticks start if they trigger a runtime error") { model =>
testModels(models.seq, "Buttons should be disabled until ticks start if they trigger a runtime error") { model =>
for {
button <- buttons(model)
source <- button.source
Expand All @@ -66,7 +67,7 @@ class ButtonTests extends TestModels {
} yield "\"" + button.display.getOrElse(source) + "\" button: " + exception.getMessage
}

testModels(models,
testModels(models.seq,
"If any button is disabled until ticks start, ensure at least one enabled button that resets ticks") {
model =>
for {
Expand Down
6 changes: 3 additions & 3 deletions src/test/scala/org/nlogo/models/CrossReferenceTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import java.nio.file.{ Files, Paths }

import com.typesafe.config.{ Config, ConfigFactory }

import org.scalatest.FunSuite
import org.scalatest.funsuite.AnyFunSuite

import scala.collection.JavaConverters._
import scala.jdk.CollectionConverters.ListHasAsScala

class CrossReferenceTests extends FunSuite {
class CrossReferenceTests extends AnyFunSuite {

val crossReference = ConfigFactory.parseFile(new File("crossReference.conf"))

Expand Down
10 changes: 6 additions & 4 deletions src/test/scala/org/nlogo/models/InfoTabUrlTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.collection.JavaConverters._
import scala.collection.parallel.CollectionConverters.IterableIsParallelizable
import scala.jdk.CollectionConverters.IterableHasAsScala
import scala.util.Try

import com.vladsch.flexmark.Extension
Expand All @@ -21,12 +22,12 @@ import org.apache.commons.validator.routines.UrlValidator.ALLOW_2_SLASHES
import org.nlogo.api.Version
import org.nlogo.core.Model
import org.scalatest.BeforeAndAfterAll
import org.scalatest.FunSuite
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.concurrent.ScalaFutures

import org.asynchttpclient.{ DefaultAsyncHttpClient, DefaultAsyncHttpClientConfig, Response }

class InfoTabUrlTests extends FunSuite with ScalaFutures with BeforeAndAfterAll {
class InfoTabUrlTests extends AnyFunSuite with ScalaFutures with BeforeAndAfterAll {

private val parserBuilder = {
val options = new MutableDataSet()
Expand Down Expand Up @@ -66,7 +67,8 @@ class InfoTabUrlTests extends FunSuite with ScalaFutures with BeforeAndAfterAll
.filter(_.is3D == Version.is3D)
.flatMap(m => linksInMarkdown(m.info).map(_ -> m)) // (link, model) pairs
.groupBy(_._1) // group by links
.mapValues(_.unzip._2) // keep only models in the map's values
.view.mapValues(_.unzip._2) // keep only models in the map's values
.toMap

val builder = new DefaultAsyncHttpClientConfig.Builder()
val client = new DefaultAsyncHttpClient(builder.build())
Expand Down
12 changes: 7 additions & 5 deletions src/test/scala/org/nlogo/models/InfoTabsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package org.nlogo.models

import java.util.{ ArrayList => JArrayList }

import scala.collection.JavaConverters._
import scala.collection.immutable.ArraySeq
import scala.jdk.CollectionConverters.IterableHasAsScala
import scala.language.postfixOps
import scala.util.{ Failure, Try }

Expand Down Expand Up @@ -39,15 +40,15 @@ class InfoTabsTests extends TestModels {
val maxLen = 700
testModels(s"Length of first paragraph of WHAT IS IT should be >= $minLen and <= $maxLen") { model =>
for {
paragraph <- model.infoTabParts.sectionMap.get(whatIsIt).map(_.linesIterator.next)
paragraph <- model.infoTabParts.sectionMap.get(whatIsIt).map(_.linesIterator.next())
length = paragraph.length
if length <= minLen || length > maxLen
} yield s"Length is $length in ${model.quotedPath}"
}

test("First paragraph of WHAT IS IT should be unique") {
val failures = for {
(optWhatItIs, groupedModels) <- libraryModels.groupBy(_.infoTabParts.sectionMap.get(whatIsIt).map(_.linesIterator.next))
(optWhatItIs, groupedModels) <- libraryModels.groupBy(_.infoTabParts.sectionMap.get(whatIsIt).map(_.linesIterator.next()))
whatItIs <- optWhatItIs
if groupedModels.size > 1
} yield whatItIs + "\n " + groupedModels.map(_.quotedPath).mkString("\n ")
Expand Down Expand Up @@ -106,8 +107,9 @@ class InfoTabsTests extends TestModels {
def getErrors(node: Node): Seq[(String, String)] =
node match {
case f: FencedCodeBlock if f.getInfo.trim.isEmpty =>
check(f.getContentChars.toString)
case i: IndentedCodeBlock => check(i.getContentChars.toString)
ArraySeq.unsafeWrapArray(check(f.getContentChars.toString))
case i: IndentedCodeBlock =>
ArraySeq.unsafeWrapArray(check(i.getContentChars.toString))
case other => other.getChildren.asScala.flatMap(s => getErrors(s)).toSeq
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/scala/org/nlogo/models/LegalInformationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import java.io.ByteArrayInputStream

import difflib.DiffUtils

import scala.collection.JavaConverters._
import scala.jdk.CollectionConverters.{ ListHasAsScala, SeqHasAsJava }
import scala.io.Source
import scala.util.Try

Expand All @@ -26,7 +26,7 @@ class LegalInformationTests extends TestModels {
}

testModels("Model file should be identical to output of Notarizer") { model =>
val savedLines = Source.fromFile(model.file.getPath).getLines.toSeq.asJava
val savedLines = Source.fromFile(model.file.getPath).getLines().toSeq.asJava
for {
notarizedModel <- Try(Notarizer.notarize(model)).toOption
patch = DiffUtils.diff(savedLines, notarizedModel.linesIterator.toSeq.asJava)
Expand Down
4 changes: 2 additions & 2 deletions src/test/scala/org/nlogo/models/NameTests.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.nlogo.models

import org.scalatest.FunSuite
import org.scalatest.funsuite.AnyFunSuite

class NameTests extends FunSuite {
class NameTests extends AnyFunSuite {

test("There should be no duplicate model names") {
val duplicates = libraryModels.groupBy(_.name).filter(_._2.size > 1)
Expand Down
4 changes: 2 additions & 2 deletions src/test/scala/org/nlogo/models/SpellCheckTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.io.File
import java.lang.{ ProcessBuilder => JProcessBuilder }
import java.nio.file.{ Files, Path, Paths }

import scala.collection.JavaConverters._
import scala.jdk.CollectionConverters.ListHasAsScala
import scala.sys.process.ProcessBuilder
import scala.sys.process.ProcessLogger
import scala.sys.process.stringSeqToProcess
Expand Down Expand Up @@ -53,7 +53,7 @@ class SpellCheckTests extends TestModels with BeforeAndAfterAll {
val tmpPath = Files.createTempFile(model.file.getName, ".txt")
val contentWithoutEscapes = escapes.foldLeft(model.content)(_.replace(_, " "))
Files.write(tmpPath, contentWithoutEscapes.getBytes("UTF-8"))
val lines = model.content.linesIterator.zipWithIndex.toStream
val lines = model.content.linesIterator.zipWithIndex.to(LazyList)
val (runCheck, outFile) = aspell(tmpPath)
runCheck.start().waitFor()
Files.readAllLines(outFile)
Expand Down
Loading
Loading