diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a029037f4f..c93c4539fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,6 +49,24 @@ jobs: verbose: true # optional (default = false) if: runner.os == 'Linux' + standalone-lfc-tests: + strategy: + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + steps: + # Setup Build dependencies + - name: Setup Java JDK + uses: actions/setup-java@v1.4.3 + with: + # The Java version to make available on the path. Takes a whole or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x). Early access versions can be specified in the form of e.g. 14-ea, 14.0.0-ea, or 14.0.0-ea.28 + java-version: 11 + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Run standalone LFC tests + run: | + ./gradlew :org.lflang.lfc:test --stacktrace + c-tests: strategy: matrix: diff --git a/build.gradle b/build.gradle index 45bdaae265..9fca3c366e 100644 --- a/build.gradle +++ b/build.gradle @@ -66,3 +66,18 @@ subprojects { delete "${projectDir}/src-gen/" } } + +gradle.projectsEvaluated { + // Our CI uses --tests filters, which fails if some + // subprojects have no matching test. + // + // https://stackoverflow.com/questions/26147480/how-to-make-gradle-not-to-mark-build-failed-if-no-tests-are-found + subprojects { + test { + filter { + setFailOnNoMatchingTests(false) + } + } + } +} + diff --git a/org.lflang.lfc/build.gradle b/org.lflang.lfc/build.gradle index f96d08da49..ad4b8b1a36 100644 --- a/org.lflang.lfc/build.gradle +++ b/org.lflang.lfc/build.gradle @@ -1,7 +1,28 @@ + +sourceSets { + test { + java.srcDirs = [] + kotlin.srcDirs = ['test/kotlin'] + resources.srcDir('test/resources') + resources.include '**/*' + } +} + +compileTestKotlin { + destinationDir = compileTestJava.destinationDir + kotlinOptions { + jvmTarget = "1.8" + } +} + + + dependencies { implementation project(':org.lflang') implementation "org.eclipse.xtext:org.eclipse.xtext.ide:${xtextVersion}" implementation "org.eclipse.xtext:org.eclipse.xtext.xbase.ide:${xtextVersion}" + + testImplementation "junit:junit:4.12" } apply plugin: 'application' diff --git a/org.lflang.lfc/src/org/lflang/lfc/LFStandaloneModule.java b/org.lflang.lfc/src/org/lflang/lfc/LFStandaloneModule.java index 01215fef69..a26878335d 100644 --- a/org.lflang.lfc/src/org/lflang/lfc/LFStandaloneModule.java +++ b/org.lflang.lfc/src/org/lflang/lfc/LFStandaloneModule.java @@ -26,15 +26,16 @@ * generated by Xtext 2.23.0 */ -package org.lflang; +package org.lflang.lfc; import java.util.Objects; +import org.eclipse.emf.ecore.EValidator; +import org.eclipse.emf.ecore.impl.EValidatorRegistryImpl; import org.eclipse.xtext.validation.ValidationMessageAcceptor; -import org.lflang.lfc.ReportingBackend; -import org.lflang.lfc.StandaloneErrorReporter; -import org.lflang.lfc.StandaloneIssueAcceptor; +import org.lflang.ErrorReporter; +import org.lflang.LFRuntimeModule; import com.google.inject.Binder; import com.google.inject.Module; @@ -60,5 +61,13 @@ public void configure(Binder binder) { binder.bind(ErrorReporter.class).to(StandaloneErrorReporter.class); binder.bind(ReportingBackend.class).toInstance(helper); binder.bind(ValidationMessageAcceptor.class).to(StandaloneIssueAcceptor.class); + // This is required to force the ResourceValidator to + // use a new registry instance (which is reused by the injector as a singleton). + // Otherwise, it uses the static EValidator.Registry.INSTANCE which is bad + // as the first validator to be created would persist in that static instance. + // New injectors would reuse the existing instance, but + // its fields would have been injected by an older injector + // and be out of sync with the rest of the application. + binder.bind(EValidator.Registry.class).to(EValidatorRegistryImpl.class).asEagerSingleton(); } } diff --git a/org.lflang.lfc/src/org/lflang/lfc/Main.java b/org.lflang.lfc/src/org/lflang/lfc/Main.java index 9be18191e7..307c75a396 100644 --- a/org.lflang.lfc/src/org/lflang/lfc/Main.java +++ b/org.lflang.lfc/src/org/lflang/lfc/Main.java @@ -39,9 +39,9 @@ import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.LFRuntimeModule; -import org.lflang.LFStandaloneModule; import org.lflang.LFStandaloneSetup; import org.lflang.generator.StandaloneContext; +import org.lflang.lfc.LFStandaloneModule; import com.google.inject.Inject; import com.google.inject.Injector; @@ -61,7 +61,7 @@ public class Main { private static String MAIN_PATH_IN_JAR = String.join("/", new String[] {"!", "org", "lflang", "lfc", "Main.class"}); - + /** * Object for interpreting command line arguments. */ @@ -87,13 +87,13 @@ public class Main { */ @Inject private Provider resourceSetProvider; - + /** * Injected resource validator. */ @Inject private IResourceValidator validator; - + /** * Injected code generator. */ @@ -140,20 +140,20 @@ enum CLIOption { OUTPUT_PATH("o", "output-path", true, false, "Specify the root output directory.", false), RUNTIME_VERSION(null, "runtime-version", true, false, "Specify the version of the runtime library used for compiling LF programs.", true), EXTERNAL_RUNTIME_PATH(null, "external-runtime-path", true, false, "Specify an external runtime library to be used by the compiled binary.", true); - + /** * The corresponding Apache CLI Option object. */ public final Option option; - + /** * Whether or not to pass this option to the code generator. */ public final boolean passOn; - + /** * Construct a new CLIOption. - * + * * @param opt The short option name. E.g.: "f" denotes a flag * "-f". * @param longOpt The long option name. E.g.: "foo" denotes a flag @@ -171,10 +171,10 @@ enum CLIOption { option.setRequired(isReq); this.passOn = passOn; } - + /** * Create an Apache Commons CLI Options object and add all the options. - * + * * @return Options object that includes all the options in this enum. */ public static Options getOptions() { @@ -182,11 +182,11 @@ public static Options getOptions() { Arrays.asList(CLIOption.values()).forEach(o -> opts.addOption(o.option)); return opts; } - + /** * Return a list of options that are to be passed on to the code * generator. - * + * * @return List of options that must be passed on to the code gen stage. */ public static List