Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test for parsing CLI arguments in lfc #1668

Merged
merged 12 commits into from
Apr 4, 2023
49 changes: 0 additions & 49 deletions .github/scripts/test-lfc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,54 +16,5 @@ function test_with_links() {

bin/lfc test/C/src/Minimal.lf

# -c,--clean Clean before building.
bin/lfc -c test/C/src/Minimal.lf
bin/lfc --clean test/C/src/Minimal.lf

# --external-runtime-path <arg> Specify an external runtime library to
# be used by the compiled binary.

# -f,--federated Treat main reactor as federated.
bin/lfc -f test/C/src/Minimal.lf
bin/lfc --federated test/C/src/Minimal.lf

# --rti Specify address of RTI.
bin/lfc -f --rti rti test/C/src/Minimal.lf
bin/lfc --federated --rti rti test/C/src/Minimal.lf

# -h,--help Display this information.
bin/lfc -h
bin/lfc --help

# -l, --lint Enable linting during build.
bin/lfc -l test/Python/src/Minimal.lf
bin/lfc --lint test/Python/src/Minimal.lf

# -n,--no-compile Do not invoke target compiler.
bin/lfc -n test/C/src/Minimal.lf
bin/lfc --no-compile test/C/src/Minimal.lf

# -o,--output-path <arg> Specify the root output directory.
bin/lfc -o . test/C/src/Minimal.lf
bin/lfc --output-path . test/C/src/Minimal.lf

# --runtime-version <arg> Specify the version of the runtime
# library used for compiling LF
# programs.
bin/lfc --runtime-version e80cd36ce5bd625a7b167e7dfd65d25f78b0dd01 test/Cpp/src/Minimal.lf

# -w,--workers Specify the default number of worker threads.
bin/lfc -w 2 test/C/src/Minimal.lf
bin/lfc --workers 2 test/C/src/Minimal.lf
bin/lfc --threading true test/C/src/Minimal.lf
bin/lfc --threading false test/C/src/Minimal.lf

# --target-compiler <arg> Target compiler to invoke.
# (Added no-compile to avoid adding dependency.)
bin/lfc --target-compiler gcc --no-compile test/C/src/Minimal.lf

# --version
bin/lfc --version

# Ensure that lfc is robust to symbolic links.
test_with_links "lfc"
126 changes: 123 additions & 3 deletions org.lflang.tests/src/org/lflang/tests/cli/LfcCliTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,30 @@

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.lflang.tests.TestUtils.TempDirBuilder.dirBuilder;
import static org.lflang.tests.TestUtils.TempDirChecker.dirChecker;
import static org.lflang.tests.TestUtils.isDirectory;
import static org.lflang.tests.TestUtils.isRegularFile;

import com.google.inject.Injector;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Properties;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import org.lflang.LocalStrings;
import org.lflang.cli.Io;
import org.lflang.cli.Lfc;
import org.lflang.generator.LFGeneratorContext.BuildParm;
import org.lflang.tests.TestUtils.TempDirBuilder;

/**
* @author Clément Fournier
* @author Atharva Patil
*/
public class LfcCliTest {

Expand All @@ -55,7 +62,6 @@ public class LfcCliTest {
}
""";


@Test
public void testHelpArg() {
lfcTester.run("--help", "--version")
Expand Down Expand Up @@ -86,6 +92,63 @@ public void testWrongCliArg() {
});
}

@Test
public void testInvalidArgs(@TempDir Path tempDir) throws IOException {
dirBuilder(tempDir).file("src/File.lf", LF_PYTHON_FILE);
LfcOneShotTestFixture fixture = new LfcOneShotTestFixture();

// Invalid src file.
fixture.run(tempDir, "unknown.lf")
.verify(result -> {
result.checkStdErr(containsString("No such file or directory."));
result.checkFailed();
});

// Invalid output path.
fixture.run(tempDir, "--output-path", "unknown/output/path", "src/File.lf")
.verify(result -> {
result.checkStdErr(containsString("Output location does not exist."));
result.checkFailed();
});

// Invalid build type.
fixture.run(tempDir, "--build-type", "unknown-build-type", "src/File.lf")
.verify(result -> {
result.checkStdErr(containsString("Invalid build type."));
result.checkFailed();
});

// Invalid logging level.
fixture.run(tempDir, "--logging", "unknown_level", "src/File.lf")
.verify(result -> {
result.checkStdErr(containsString("Invalid log level."));
result.checkFailed();
});

// Invalid RTI path.
fixture.run(tempDir, "--rti", "unknown/rti/path", "src/File.lf")
.verify(result -> {
result.checkStdErr(containsString("Invalid RTI path."));
result.checkFailed();
});

// Invalid scheduler.
fixture.run(tempDir, "--scheduler", "unknown-scheduler", "src/File.lf")
.verify(result -> {
result.checkStdErr(containsString("Invalid scheduler."));
result.checkFailed();
});

// Invalid workers.
fixture.run(tempDir, "--workers", "notaninteger", "src/File.lf")
.verify(result -> {
result.checkStdErr(containsString("Invalid value for option '--workers'"));
result.checkStdErr(containsString("is not an int"));
result.checkFailed();
});

}

@Test
public void testGenInSrcDir(@TempDir Path tempDir) throws IOException {
dirBuilder(tempDir).file("src/File.lf", LF_PYTHON_FILE);
Expand All @@ -98,17 +161,74 @@ public void testGenInSrcDir(@TempDir Path tempDir) throws IOException {
.check("bin", isDirectory())
.check("src-gen/File/File.py", isRegularFile());
});

}

@Test
public void testGeneratorArgs(@TempDir Path tempDir)
throws IOException {
TempDirBuilder dir = dirBuilder(tempDir);
dir.file("src/File.lf", LF_PYTHON_FILE);
dir.mkdirs("path//to/rti");

String[] args = {
"src/File.lf",
"--output-path", "src",
"--build-type", "Release",
"--clean",
"--target-compiler", "gcc",
"--external-runtime-path", "src",
"--federated",
"--logging", "info",
"--lint",
"--no-compile",
"--quiet",
"--rti", "path/to/rti",
"--runtime-version", "rs",
"--scheduler", "GEDF_NP",
"--threading", "false",
"--workers", "1",
};
LfcOneShotTestFixture fixture = new LfcOneShotTestFixture();

fixture.run(tempDir, args)
.verify(result -> {
// Don't validate execution because args are dummy args.
Properties properties = fixture.lfc.getGeneratorArgs();
assertEquals(properties.getProperty(BuildParm.BUILD_TYPE.getKey()), "Release");
assertEquals(properties.getProperty(BuildParm.CLEAN.getKey()), "true");
assertEquals(properties.getProperty(BuildParm.TARGET_COMPILER.getKey()), "gcc");
assertEquals(properties.getProperty(BuildParm.EXTERNAL_RUNTIME_PATH.getKey()), "src");
assertEquals(properties.getProperty(BuildParm.LOGGING.getKey()), "info");
assertEquals(properties.getProperty(BuildParm.LINT.getKey()), "true");
assertEquals(properties.getProperty(BuildParm.NO_COMPILE.getKey()), "true");
assertEquals(properties.getProperty(BuildParm.QUIET.getKey()), "true");
assertEquals(properties.getProperty(BuildParm.RTI.getKey()), "path/to/rti");
assertEquals(properties.getProperty(BuildParm.RUNTIME_VERSION.getKey()), "rs");
assertEquals(properties.getProperty(BuildParm.SCHEDULER.getKey()), "GEDF_NP");
assertEquals(properties.getProperty(BuildParm.THREADING.getKey()), "false");
assertEquals(properties.getProperty(BuildParm.WORKERS.getKey()), "1");
});
}

static class LfcTestFixture extends CliToolTestFixture {


@Override
protected void runCliProgram(Io io, String[] args) {
Lfc.main(io, args);
}
}

static class LfcOneShotTestFixture extends CliToolTestFixture {

private Lfc lfc;

@Override
protected void runCliProgram(Io io, String[] args) {
// Injector used to obtain Main instance.
final Injector injector = Lfc.getInjector("lfc", io);
// Main instance.
this.lfc = injector.getInstance(Lfc.class);
lfc.doExecute(io, args);
}
}
}
2 changes: 1 addition & 1 deletion org.lflang.tests/src/org/lflang/tests/cli/LffCliTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public void testNoSuchFile(@TempDir Path tempDir) {
result.checkFailed();

result.checkStdErr(containsString(
tempDir.resolve("nosuchdir") + ": No such file or directory"));
tempDir.resolve("nosuchdir") + ": No such file or directory."));
}


Expand Down
4 changes: 0 additions & 4 deletions org.lflang/src/org/lflang/TargetProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,6 @@ public enum TargetProperty {
(config, value, err) -> {
config.logLevel = (LogLevel) UnionType.LOGGING_UNION
.forName(ASTUtils.elementToSingleString(value));
},
(config, value, err) -> {
config.logLevel = (LogLevel) UnionType.LOGGING_UNION
.forName(ASTUtils.elementToSingleString(value));
}),

/**
Expand Down
15 changes: 10 additions & 5 deletions org.lflang/src/org/lflang/cli/CliBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ protected static void cliMain(
// Main instance.
final CliBase main = injector.getInstance(toolClass);
// Parse arguments and execute main logic.
CommandLine cmd = new CommandLine(main)
main.doExecute(io, args);
}

public void doExecute(Io io, String[] args) {
CommandLine cmd = new CommandLine(this)
.setOut(new PrintWriter(io.getOut()))
.setErr(new PrintWriter(io.getErr()));
int exitCode = cmd.execute(args);
Expand All @@ -111,7 +115,7 @@ protected static void cliMain(
*/
public abstract void run();

protected static Injector getInjector(String toolName, Io io) {
public static Injector getInjector(String toolName, Io io) {
final ReportingBackend reporter
= new ReportingBackend(io, toolName + ": ");

Expand Down Expand Up @@ -142,7 +146,7 @@ protected List<Path> getInputPaths() {
for (Path path : paths) {
if (!Files.exists(path)) {
reporter.printFatalErrorAndExit(
path + ": No such file or directory");
path + ": No such file or directory.");
}
}

Expand All @@ -160,11 +164,11 @@ protected Path getOutputRoot() {
root = io.getWd().resolve(outputPath).normalize();
if (!Files.exists(root)) { // FIXME: Create it instead?
reporter.printFatalErrorAndExit(
"Output location '" + root + "' does not exist.");
root + ": Output location does not exist.");
}
if (!Files.isDirectory(root)) {
reporter.printFatalErrorAndExit(
"Output location '" + root + "' is not a directory.");
root + ": Output location is not a directory.");
}
}

Expand Down Expand Up @@ -247,4 +251,5 @@ public Resource getResource(Path path) {
return null;
}
}

}
Loading