Skip to content

Commit

Permalink
Merge 144764d into d2d6792
Browse files Browse the repository at this point in the history
  • Loading branch information
reid-spencer committed May 17, 2024
2 parents d2d6792 + 144764d commit efc9e26
Show file tree
Hide file tree
Showing 18 changed files with 409 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ class DiagramsPass(input: PassInput, outputs: PassesOutput) extends Pass(input,
definition match
case c: Context =>
val aggregates = c.entities.filter(_.hasOption("aggregate"))
val relationships = makeRelationships(c)
val domain = parents.head.asInstanceOf[Domain]
val root = parents.find(c => c.isRootContainer && c.isInstanceOf[Root]).get.asInstanceOf[Root]
val relationships = makeRelationships(c,root)
contextDiagrams.put(c, ContextDiagramData(domain, aggregates, relationships))
case epic: Epic =>
epic.cases.foreach { uc =>
Expand All @@ -100,13 +101,15 @@ class DiagramsPass(input: PassInput, outputs: PassesOutput) extends Pass(input,
case _ => ()
}

private def makeRelationships(context: Context): Seq[ContextRelationship] = {
val allProcessors = findProcessors(context)
private def makeRelationships(context: Context, root: Root): Seq[ContextRelationship] = {
val domains = AST.getAllDomains(root)
val applications = domains.flatMap(AST.getApplications)
val allProcessors = findProcessors(context) ++ applications
for {
processor <- allProcessors
relationship <- makeProcessorRelationships(context, processor)
relationships <- makeProcessorRelationships(context, processor)
} yield {
relationship
relationships
}
}

Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ lazy val command = Module("command", "riddl-command")
description := "Command infrastructure needed to define a command",
libraryDependencies ++= Seq(Dep.scopt, Dep.pureconfig) ++ Dep.testing
)
.dependsOn(comptest(utils), comptest(language), passes)
.dependsOn(comptest(utils), comptest(language), comptest(passes))

def testDep(project: Project): ClasspathDependency = project % "compile->compile;compile->test;test->test"

Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ trait PassCommandOptions extends CommandOptions {
def outputDir: Option[Path]
override def check: Messages = {
val msgs1 = super.check
val msgs2 = if inputFile.isEmpty then {
val msgs2 = if outputDir.isEmpty then {
Messages.errors("An output directory was not provided.")
} else {
Messages.empty
Expand All @@ -37,12 +37,24 @@ trait PassCommandOptions extends CommandOptions {
*/
abstract class PassCommand[OPT <: PassCommandOptions: ClassTag](name: String) extends CommandPlugin[OPT](name) {

/** Get the passes to run given basic input for pass creation
*
* @param log
* The logger to use
* @param commonOptions
* The common options for the command
* @param options
* The command options
* @return
* A [[PassCreator]] function that creates the pass
*/
def getPasses(
log: Logger,
commonOptions: CommonOptions,
options: OPT
): PassesCreator

/** A method to override the options */
def overrideOptions(options: OPT, newOutputDir: Path): OPT

private final def doRun(
Expand All @@ -68,6 +80,19 @@ abstract class PassCommand[OPT <: PassCommandOptions: ClassTag](name: String) ex
}
}

/** The basic implementation of the command. This should be called with `super.run(...)` from the subclass
* implementation.
* @param originalOptions
* The original options to the command
* @param commonOptions
* The options common to all commands
* @param log
* A logger for logging errors, warnings, and info
* @param outputDirOverride
* Any override to the outputDir option from the command line
* @return
* Either a set of Messages on error or a Unit on success
*/
override def run(
originalOptions: OPT,
commonOptions: CommonOptions,
Expand Down
15 changes: 15 additions & 0 deletions command/src/test/input/test-pass-command.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
common {
show-times = true
verbose = false
quiet = false
debug = true
dry-run = false
show-warnings = true
show-missing-warnings = false
show-style-warnings = false
}

test-pass-command {
input-file = "test.riddl"
output-directory = "target/test-output"
}
14 changes: 14 additions & 0 deletions command/src/test/input/test-pass-command2.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
common {
show-times = true
verbose = false
quiet = false
debug = true
dry-run = false
show-warnings = true
show-missing-warnings = false
show-style-warnings = false
}

test-pass-command {
input-file = "test.riddl"
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
com.ossuminc.riddl.command.TestPassCommand
com.ossuminc.riddl.command.ASimpleTestCommand
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ package com.ossuminc.riddl.command

/** Unit Tests For Running Riddlc Commands from Plugins */

import com.ossuminc.riddl.utils.{Plugin, PluginSpecBase}
import com.ossuminc.riddl.utils.{Plugin, PluginSpecBase, SysLogger}
import com.ossuminc.riddl.language.CommonOptions
import com.ossuminc.riddl.language.Messages
import com.ossuminc.riddl.language.Messages.Message

import java.nio.file.Path
import scopt.*
Expand Down Expand Up @@ -77,6 +80,29 @@ class CommandPluginTest
rc must be(0)
}

"generates error on wrong plugin name" in {
CommandPlugin.loadCommandNamed("foo", CommonOptions()) match
case Left(messages) =>
val errors = messages.justErrors
errors must not be (empty)
errors.find { (msg: Messages.Message) =>
msg.message.contains("No plugin command matches")
} match
case Some(msg) => succeed
case None => fail("Didn't find expected message")
case Right(plugin) =>
fail(s"Should have failed but got: $plugin")
}

"has runCommandNamed method" in {
val optionsPath: Path = Path.of("command/src/test/input/test.conf")
CommandPlugin.runCommandNamed("test",optionsPath, SysLogger()) match {
case Left(messages) =>
messages.justErrors must be(empty)
case Right(passesResult) => succeed
}
}

"handle wrong command as target" in {
val args = Array(
"--verbose",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ trait CommandTestBase(val inputDir: String = "command/src/test/input/") extends
}

def check[OPTS <: CommandOptions](
cmd: CommandPlugin[?],
cmd: CommandPlugin[OPTS],
expected: OPTS,
file: Path = Path.of(confFile)
): Assertion = {
)(checker: (opts: OPTS) => Assertion = { (opts: OPTS) => opts.check must be(empty) }): Assertion = {
cmd.loadOptionsFrom(file) match {
case Left(errors) => fail(errors.format)
case Right(options) => options must be(expected)
case Left(errors) => fail(errors.format)
case Right(options: OPTS) =>
checker(options)
options must be(expected)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.ossuminc.riddl.command

import java.nio.file.Path
import com.ossuminc.riddl.language.Messages
import org.scalatest.exceptions.TestFailedException

class PassCommandTest extends CommandTestBase {

override val confFile = s"$inputDir/test-pass-command.conf"

"PassCommandOptions" must {
"check content" in {
val pco = TestPassCommand.Options(
Some(Path.of("command/src/test/input/simple.riddl")),
None
)
val messages = pco.check
messages.size must be(2)
val message_text = messages.format
message_text must include("An output directory was not provided")
message_text must include("check called")
}
}

"PassCommand" must {
"check filled options" in {
pending // fails to find command when run in parallel :(
CommandPlugin.loadCommandNamed(TestPassCommand.name) match {
case Left(messages) => fail(messages.format)
case Right(plugin: CommandPlugin[TestPassCommand.Options] @unchecked) =>
val expected = TestPassCommand.Options(
Some(Path.of("test.riddl")),
Some(Path.of("target/test-output"))
)
super.check[TestPassCommand.Options](plugin, expected) { (options: TestPassCommand.Options) =>
val messages = options.check
messages.size must be(1)
messages.head.message must include("check called")
}
}
}
"check empty input options" in {
pending // fails to find command when run in parallel :(
CommandPlugin.loadCommandNamed(TestPassCommand.name) match {
case Left(messages) => fail(messages.format)
case Right(plugin: CommandPlugin[TestPassCommand.Options] @unchecked) =>
val expected = TestPassCommand.Options(
Some(Path.of("test.riddl")),
Some(Path.of("none"))
)
val path = Path.of(s"$inputDir/test-pass-command2.conf")
intercept[TestFailedException] {
super.check[TestPassCommand.Options](plugin, expected, path) { (options: TestPassCommand.Options) =>
val messages = options.check
messages.size must be(1)
messages(0).message must include("check called")
}
}
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.ossuminc.riddl.command

import com.ossuminc.riddl.language.{At, CommonOptions, Messages}
import com.ossuminc.riddl.passes.{Pass, PassOptions, PassesCreator, TestPass}
import com.ossuminc.riddl.utils.Logger
import pureconfig.{ConfigCursor, ConfigReader}
import scopt.{OParser, OParserBuilder}

import java.io.File
import java.nio.file.Path

case class TestPassCommand() extends PassCommand[TestPassCommand.Options](TestPassCommand.name):
override def getPasses(log: Logger, commonOptions: CommonOptions, options: TestPassCommand.Options): PassesCreator =
Pass.standardPasses :+ TestPass.creator(options)

override def overrideOptions(options: TestPassCommand.Options, newOutputDir: Path): TestPassCommand.Options =
options.copy(outputDir = Some(newOutputDir))

override def getOptions: (OParser[Unit, TestPassCommand.Options], TestPassCommand.Options) =
TestPassCommand.optionsParser

override def getConfigReader: ConfigReader[TestPassCommand.Options] = TestPassCommand.configReader

object TestPassCommand {
val name = "test-pass-command"

case class Options(inputFile: Option[Path], outputDir: Option[Path]) extends PassCommandOptions with PassOptions {
override def check: Messages.Messages = {
super.check ++ Seq(Messages.info("check called", At()))
}
def command: String = TestPassCommand.name
}

def optionsParser: (OParser[Unit, Options], Options) = {
val builder: OParserBuilder[Options] = scopt.OParser.builder[Options]
import builder.*

cmd(TestPassCommand.name)
.children(
builder
.arg[File]("input-file")
.action { (file, opt) => opt.copy(inputFile = Some(file.toPath)) }
.text("The input to be parsed"),
arg[String]("output-directory")
.action { (dir, opt) => opt.copy(outputDir = Some(Path.of(dir))) }
.text("The path to the directory where the output will be placed")
)
.text("Just used for testing") -> Options(None, None)
}

def configReader: ConfigReader[Options] = { (cur: ConfigCursor) =>
for
topCur <- cur.asObjectCursor
topRes <- topCur.atKey(name)
objCur <- topRes.asObjectCursor
inFileRes <- objCur.atKey("input-file").map(_.asString)
inFile <- inFileRes
targetRes <- objCur.atKey("output-directory").map(_.asString)
target <- targetRes
yield {
Options(inputFile = Some(Path.of(inFile)), outputDir = Some(Path.of(target)))
}
}
}

0 comments on commit efc9e26

Please sign in to comment.