diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 49f3dfba4..839dd232c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,14 +21,23 @@ jobs: with: distribution: 'temurin' java-version: '21' + cache: gradle + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - name: Run Gradle Build working-directory: ./Src/java - run: ./gradlew check publish + run: ./gradlew check publish sonar env: OSSRH_USERNAME: ${{ vars.OSSRH_USERNAME }} OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - name: Publish Test Report uses: mikepenz/action-junit-report@v4 if: success() || failure() # always run even if the previous step fails diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 226811f8a..0efe07790 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -23,11 +23,20 @@ jobs: distribution: 'temurin' java-version: '21' cache: gradle + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - name: Run Gradle Build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} working-directory: ./Src/java - run: ./gradlew check -x spotlessCheck + run: ./gradlew check sonar -x spotlessCheck - name: Publish Test Report uses: mikepenz/action-junit-report@v4 if: (success() || failure()) && matrix.os == 'ubuntu-latest' diff --git a/Src/java/.sonarlint/connectedMode.json b/Src/java/.sonarlint/connectedMode.json new file mode 100644 index 000000000..4d4b19ca3 --- /dev/null +++ b/Src/java/.sonarlint/connectedMode.json @@ -0,0 +1,4 @@ +{ + "sonarCloudOrganization": "cqframework", + "projectKey": "cqframework_clinical_quality_language" +} \ No newline at end of file diff --git a/Src/java/.vscode/settings.json b/Src/java/.vscode/settings.json index 286f12b12..9b946f6bc 100644 --- a/Src/java/.vscode/settings.json +++ b/Src/java/.vscode/settings.json @@ -79,5 +79,9 @@ ".git", "**/*.gradle", "**/test/resources/**" - ] + ], + "sonarlint.connectedMode.project": { + "connectionId": "cqframework", + "projectKey": "cqframework_clinical_quality_language" + } } \ No newline at end of file diff --git a/Src/java/build.gradle b/Src/java/build.gradle index 5cc640c3a..938708ac2 100644 --- a/Src/java/build.gradle +++ b/Src/java/build.gradle @@ -1,6 +1,15 @@ plugins { id 'idea' id 'eclipse' + id "org.sonarqube" version "4.4.1.3373" +} + +sonar { + properties { + property "sonar.projectKey", "cqframework_clinical_quality_language" + property "sonar.organization", "cqframework" + property "sonar.host.url", "https://sonarcloud.io" + } } idea { diff --git a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmLibrarySourceProvider.java b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmLibrarySourceProvider.java index adb2fbfaa..bf35eb5cc 100644 --- a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmLibrarySourceProvider.java +++ b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmLibrarySourceProvider.java @@ -6,7 +6,7 @@ import java.util.List; import org.cqframework.cql.cql2elm.LibrarySourceProvider; import org.hl7.elm.r1.VersionedIdentifier; -import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.ILoggingService; import org.hl7.fhir.r5.model.Library; import org.hl7.fhir.utilities.npm.NpmPackage; @@ -15,8 +15,7 @@ */ public class NpmLibrarySourceProvider implements LibrarySourceProvider { - public NpmLibrarySourceProvider( - List packages, ILibraryReader reader, IWorkerContext.ILoggingService logger) { + public NpmLibrarySourceProvider(List packages, ILibraryReader reader, ILoggingService logger) { this.packages = packages; this.reader = reader; this.logger = logger; @@ -24,7 +23,7 @@ public NpmLibrarySourceProvider( private List packages; private ILibraryReader reader; - private IWorkerContext.ILoggingService logger; + private ILoggingService logger; @Override public InputStream getLibrarySource(VersionedIdentifier identifier) { @@ -59,7 +58,7 @@ public InputStream getLibrarySource(VersionedIdentifier identifier) { } } catch (IOException e) { logger.logDebugMessage( - IWorkerContext.ILoggingService.LogCategory.PROGRESS, + ILoggingService.LogCategory.PROGRESS, String.format( "Exceptions occurred attempting to load npm library source for %s", identifier.toString())); diff --git a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java index ef3ef3876..861deffd0 100644 --- a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java +++ b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java @@ -8,7 +8,7 @@ import org.hl7.cql.model.ModelIdentifier; import org.hl7.cql.model.ModelInfoProvider; import org.hl7.elm_modelinfo.r1.ModelInfo; -import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.ILoggingService; import org.hl7.fhir.r5.model.Library; import org.hl7.fhir.utilities.npm.NpmPackage; @@ -17,8 +17,7 @@ */ public class NpmModelInfoProvider implements ModelInfoProvider { - public NpmModelInfoProvider( - List packages, ILibraryReader reader, IWorkerContext.ILoggingService logger) { + public NpmModelInfoProvider(List packages, ILibraryReader reader, ILoggingService logger) { this.packages = packages; this.reader = reader; this.logger = logger; @@ -26,7 +25,7 @@ public NpmModelInfoProvider( private List packages; private ILibraryReader reader; - private IWorkerContext.ILoggingService logger; + private ILoggingService logger; public ModelInfo load(ModelIdentifier modelIdentifier) { // VersionedIdentifier.id: Name of the model @@ -60,7 +59,7 @@ public ModelInfo load(ModelIdentifier modelIdentifier) { } } catch (IOException e) { logger.logDebugMessage( - IWorkerContext.ILoggingService.LogCategory.PROGRESS, + ILoggingService.LogCategory.PROGRESS, String.format( "Exceptions occurred attempting to load npm library for model %s", modelIdentifier.toString())); diff --git a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmPackageManager.java b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmPackageManager.java index de0534c56..b813eadae 100644 --- a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmPackageManager.java +++ b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmPackageManager.java @@ -4,7 +4,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.ILoggingService; import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; @@ -12,7 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class NpmPackageManager implements IWorkerContext.ILoggingService { +public class NpmPackageManager implements ILoggingService { private static final Logger logger = LoggerFactory.getLogger(NpmPackageManager.class); private final ImplementationGuide sourceIg; private final FilesystemPackageCacheManager fspcm; @@ -29,23 +29,10 @@ public NpmPackageManager(ImplementationGuide sourceIg, FilesystemPackageCacheMan public NpmPackageManager( ImplementationGuide sourceIg, FilesystemPackageCacheManager fspcm, List npmList) { this.sourceIg = sourceIg; - - if (fspcm == null) { - try { - // userMode indicates whether the packageCache is within the working directory or in the user home - this.fspcm = new FilesystemPackageCacheManager(true); - } catch (IOException e) { - String message = "Error creating the FilesystemPackageCacheManager: " + e.getMessage(); - logErrorMessage(message); - throw new NpmPackageManagerException(message, e); - } - } else { - this.fspcm = fspcm; - } - this.npmList = npmList == null ? new ArrayList<>() : npmList; try { + this.fspcm = fspcm != null ? fspcm : new FilesystemPackageCacheManager.Builder().build(); loadDependencies(); } catch (Exception e) { logErrorMessage(e.getMessage()); diff --git a/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java b/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java index 69e106644..eecf860a0 100644 --- a/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java +++ b/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java @@ -12,7 +12,7 @@ import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; import org.hl7.fhir.r4.model.Resource; -import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.ILoggingService; import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.utilities.npm.NpmPackage; import org.junit.jupiter.api.Disabled; @@ -20,7 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class NpmPackageManagerTests implements IWorkerContext.ILoggingService { +public class NpmPackageManagerTests implements ILoggingService { private final Logger logger = LoggerFactory.getLogger(NpmPackageManagerTests.class); private final VersionConvertor_40_50 convertor = new VersionConvertor_40_50(new BaseAdvisor_40_50()); @@ -134,7 +134,7 @@ public void logMessage(String msg) { } @Override - public void logDebugMessage(IWorkerContext.ILoggingService.LogCategory category, String msg) { + public void logDebugMessage(ILoggingService.LogCategory category, String msg) { logMessage(msg); } diff --git a/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/IGContext.java b/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/IGContext.java index b028c0c65..28bbfe48b 100644 --- a/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/IGContext.java +++ b/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/IGContext.java @@ -9,7 +9,7 @@ import org.hl7.fhir.convertors.conv30_50.VersionConvertor_30_50; import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.ILoggingService; import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.utilities.IniFile; @@ -19,9 +19,9 @@ public class IGContext { - private IWorkerContext.ILoggingService logger; + private ILoggingService logger; - public IWorkerContext.ILoggingService getLogger() { + public ILoggingService getLogger() { return logger; } @@ -65,7 +65,7 @@ protected void setBinaryPaths(List binaryPaths) { this.binaryPaths = binaryPaths; } - public IGContext(IWorkerContext.ILoggingService logger) { + public IGContext(ILoggingService logger) { this.logger = logger; } @@ -113,16 +113,17 @@ public void initializeFromIg(String rootDir, String igPath, String fhirVersion) * Initializes from an ig.ini file in the root directory */ public void initializeFromIni(String iniFile) { - IniFile ini = new IniFile(new File(iniFile).getAbsolutePath()); - String rootDir = Utilities.getDirectoryForFile(ini.getFileName()); - String igPath = ini.getStringProperty("IG", "ig"); - String specifiedFhirVersion = ini.getStringProperty("IG", "fhir-version"); - if (specifiedFhirVersion == null || specifiedFhirVersion == "") { - logMessage("fhir-version was not specified in the ini file. Trying FHIR version 4.0.1"); - specifiedFhirVersion = "4.0.1"; - } try { - initializeFromIg(rootDir, igPath, specifiedFhirVersion); + IniFile ini = new IniFile(new File(iniFile).getAbsolutePath()); + String iniDir = Utilities.getDirectoryForFile(ini.getFileName()); + String igPath = ini.getStringProperty("IG", "ig"); + String specifiedFhirVersion = ini.getStringProperty("IG", "fhir-version"); + if (specifiedFhirVersion == null || specifiedFhirVersion == "") { + logMessage("fhir-version was not specified in the ini file. Trying FHIR version 4.0.1"); + specifiedFhirVersion = "4.0.1"; + } + + initializeFromIg(iniDir, igPath, specifiedFhirVersion); } catch (Exception e) { String message = String.format( "Exceptions occurred initializing refresh from ini file '%s':%s", iniFile, e.getMessage()); diff --git a/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/LoggerAdapter.java b/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/LoggerAdapter.java index 6e856d2b9..0616041b5 100644 --- a/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/LoggerAdapter.java +++ b/Src/java/cqf-fhir/src/main/java/org/cqframework/fhir/utilities/LoggerAdapter.java @@ -1,9 +1,9 @@ package org.cqframework.fhir.utilities; -import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.ILoggingService; import org.slf4j.Logger; -public class LoggerAdapter implements IWorkerContext.ILoggingService { +public class LoggerAdapter implements ILoggingService { private Logger innerLogger; public LoggerAdapter(Logger innerLogger) { diff --git a/Src/java/cqf-fhir/src/test/java/org/cqframework/fhir/utilities/TestIGContext.java b/Src/java/cqf-fhir/src/test/java/org/cqframework/fhir/utilities/TestIGContext.java index f91782b36..ca590f9bf 100644 --- a/Src/java/cqf-fhir/src/test/java/org/cqframework/fhir/utilities/TestIGContext.java +++ b/Src/java/cqf-fhir/src/test/java/org/cqframework/fhir/utilities/TestIGContext.java @@ -6,10 +6,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.ILoggingService; import org.junit.jupiter.api.Test; -public class TestIGContext implements IWorkerContext.ILoggingService { +public class TestIGContext implements ILoggingService { @Test void typesAndValuesIG() throws URISyntaxException { diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.java index 9b4862e5a..a99fbbac6 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.java +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.java @@ -18,6 +18,7 @@ import org.antlr.v4.runtime.tree.ParseTree; import org.cqframework.cql.cql2elm.elm.ElmEdit; import org.cqframework.cql.cql2elm.elm.ElmEditor; +import org.cqframework.cql.cql2elm.elm.IElmEdit; import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.cql2elm.preprocessor.CqlPreprocessor; import org.cqframework.cql.elm.IdObjectFactory; @@ -234,7 +235,8 @@ public Library run(CharStream is) { var edits = allNonNull( !options.contains(EnableAnnotations) ? ElmEdit.REMOVE_ANNOTATION : null, !options.contains(EnableResultTypes) ? ElmEdit.REMOVE_RESULT_TYPE : null, - !options.contains(EnableLocators) ? ElmEdit.REMOVE_LOCATOR : null); + !options.contains(EnableLocators) ? ElmEdit.REMOVE_LOCATOR : null, + ElmEdit.REMOVE_CHOICE_TYPE_SPECIFIER_TYPE_IF_EMPTY); new ElmEditor(edits).edit(library); @@ -248,7 +250,7 @@ public Library run(CharStream is) { return library; } - private List allNonNull(ElmEdit... ts) { + private List allNonNull(IElmEdit... ts) { return Arrays.stream(ts).filter(x -> x != null).collect(Collectors.toList()); } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.java index dead0ca8b..4ece6bfc9 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.java +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.java @@ -795,7 +795,7 @@ public void removeExpression(ExpressionDef expDef) { } } - public Element resolve(String identifier) { + public ResolvedIdentifierContext resolve(String identifier) { return compiledLibrary.resolve(identifier); } @@ -2370,95 +2370,99 @@ public Expression resolveIdentifier(String identifier, boolean mustResolve) { return operandRef; } - Element element = resolve(identifier); + final ResolvedIdentifierContext resolvedIdentifierContext = resolve(identifier); + final Optional optElement = resolvedIdentifierContext.getExactMatchElement(); - if (element instanceof ExpressionDef) { - checkLiteralContext(); - ExpressionRef expressionRef = of.createExpressionRef().withName(((ExpressionDef) element).getName()); - expressionRef.setResultType(getExpressionDefResultType((ExpressionDef) element)); - if (expressionRef.getResultType() == null) { - // ERROR: - throw new IllegalArgumentException(String.format( - "Could not validate reference to expression %s because its definition contains errors.", - expressionRef.getName())); + if (optElement.isPresent()) { + final Element element = optElement.get(); + if (element instanceof ExpressionDef) { + checkLiteralContext(); + ExpressionRef expressionRef = of.createExpressionRef().withName(((ExpressionDef) element).getName()); + expressionRef.setResultType(getExpressionDefResultType((ExpressionDef) element)); + if (expressionRef.getResultType() == null) { + // ERROR: + throw new IllegalArgumentException(String.format( + "Could not validate reference to expression %s because its definition contains errors.", + expressionRef.getName())); + } + return expressionRef; } - return expressionRef; - } - if (element instanceof ParameterDef) { - checkLiteralContext(); - ParameterRef parameterRef = of.createParameterRef().withName(((ParameterDef) element).getName()); - parameterRef.setResultType(element.getResultType()); - if (parameterRef.getResultType() == null) { - // ERROR: - throw new IllegalArgumentException(String.format( - "Could not validate reference to parameter %s because its definition contains errors.", - parameterRef.getName())); + if (element instanceof ParameterDef) { + checkLiteralContext(); + ParameterRef parameterRef = of.createParameterRef().withName(((ParameterDef) element).getName()); + parameterRef.setResultType(element.getResultType()); + if (parameterRef.getResultType() == null) { + // ERROR: + throw new IllegalArgumentException(String.format( + "Could not validate reference to parameter %s because its definition contains errors.", + parameterRef.getName())); + } + return parameterRef; } - return parameterRef; - } - if (element instanceof ValueSetDef) { - checkLiteralContext(); - ValueSetRef valuesetRef = of.createValueSetRef().withName(((ValueSetDef) element).getName()); - valuesetRef.setResultType(element.getResultType()); - if (valuesetRef.getResultType() == null) { - // ERROR: - throw new IllegalArgumentException(String.format( - "Could not validate reference to valueset %s because its definition contains errors.", - valuesetRef.getName())); - } - if (isCompatibleWith("1.5")) { - valuesetRef.setPreserve(true); + if (element instanceof ValueSetDef) { + checkLiteralContext(); + ValueSetRef valuesetRef = of.createValueSetRef().withName(((ValueSetDef) element).getName()); + valuesetRef.setResultType(element.getResultType()); + if (valuesetRef.getResultType() == null) { + // ERROR: + throw new IllegalArgumentException(String.format( + "Could not validate reference to valueset %s because its definition contains errors.", + valuesetRef.getName())); + } + if (isCompatibleWith("1.5")) { + valuesetRef.setPreserve(true); + } + return valuesetRef; } - return valuesetRef; - } - if (element instanceof CodeSystemDef) { - checkLiteralContext(); - CodeSystemRef codesystemRef = of.createCodeSystemRef().withName(((CodeSystemDef) element).getName()); - codesystemRef.setResultType(element.getResultType()); - if (codesystemRef.getResultType() == null) { - // ERROR: - throw new IllegalArgumentException(String.format( - "Could not validate reference to codesystem %s because its definition contains errors.", - codesystemRef.getName())); + if (element instanceof CodeSystemDef) { + checkLiteralContext(); + CodeSystemRef codesystemRef = of.createCodeSystemRef().withName(((CodeSystemDef) element).getName()); + codesystemRef.setResultType(element.getResultType()); + if (codesystemRef.getResultType() == null) { + // ERROR: + throw new IllegalArgumentException(String.format( + "Could not validate reference to codesystem %s because its definition contains errors.", + codesystemRef.getName())); + } + return codesystemRef; } - return codesystemRef; - } - if (element instanceof CodeDef) { - checkLiteralContext(); - CodeRef codeRef = of.createCodeRef().withName(((CodeDef) element).getName()); - codeRef.setResultType(element.getResultType()); - if (codeRef.getResultType() == null) { - // ERROR: - throw new IllegalArgumentException(String.format( - "Could not validate reference to code %s because its definition contains errors.", - codeRef.getName())); + if (element instanceof CodeDef) { + checkLiteralContext(); + CodeRef codeRef = of.createCodeRef().withName(((CodeDef) element).getName()); + codeRef.setResultType(element.getResultType()); + if (codeRef.getResultType() == null) { + // ERROR: + throw new IllegalArgumentException(String.format( + "Could not validate reference to code %s because its definition contains errors.", + codeRef.getName())); + } + return codeRef; } - return codeRef; - } - if (element instanceof ConceptDef) { - checkLiteralContext(); - ConceptRef conceptRef = of.createConceptRef().withName(((ConceptDef) element).getName()); - conceptRef.setResultType(element.getResultType()); - if (conceptRef.getResultType() == null) { - // ERROR: - throw new IllegalArgumentException(String.format( - "Could not validate reference to concept %s because its definition contains error.", - conceptRef.getName())); + if (element instanceof ConceptDef) { + checkLiteralContext(); + ConceptRef conceptRef = of.createConceptRef().withName(((ConceptDef) element).getName()); + conceptRef.setResultType(element.getResultType()); + if (conceptRef.getResultType() == null) { + // ERROR: + throw new IllegalArgumentException(String.format( + "Could not validate reference to concept %s because its definition contains error.", + conceptRef.getName())); + } + return conceptRef; } - return conceptRef; - } - if (element instanceof IncludeDef) { - checkLiteralContext(); - LibraryRef libraryRef = new LibraryRef(); - libraryRef.setLocalId(of.nextId()); - libraryRef.setLibraryName(((IncludeDef) element).getLocalIdentifier()); - return libraryRef; + if (element instanceof IncludeDef) { + checkLiteralContext(); + LibraryRef libraryRef = new LibraryRef(); + libraryRef.setLocalId(of.nextId()); + libraryRef.setLibraryName(((IncludeDef) element).getLocalIdentifier()); + return libraryRef; + } } // If no other resolution occurs, and we are in a specific context, and there is a parameter with the same name @@ -2477,8 +2481,11 @@ public Expression resolveIdentifier(String identifier, boolean mustResolve) { if (mustResolve) { // ERROR: - throw new IllegalArgumentException( - String.format("Could not resolve identifier %s in the current library.", identifier)); + final String exceptionMessage = resolvedIdentifierContext + .warnCaseInsensitiveIfApplicable() + .orElse(String.format("Could not resolve identifier %s in the current library.", identifier)); + + throw new IllegalArgumentException(exceptionMessage); } return null; @@ -2524,9 +2531,11 @@ private static String lookupElementWarning(Object element) { */ public ParameterRef resolveImplicitContext() { if (!inLiteralContext() && inSpecificContext()) { - Element contextElement = resolve(currentExpressionContext()); - if (contextElement instanceof ParameterDef) { - ParameterDef contextParameter = (ParameterDef) contextElement; + ResolvedIdentifierContext resolvedIdentifierContext = resolve(currentExpressionContext()); + final Optional optParameterDef = + resolvedIdentifierContext.getElementOfType(ParameterDef.class); + if (optParameterDef.isPresent()) { + final ParameterDef contextParameter = optParameterDef.get(); checkLiteralContext(); ParameterRef parameterRef = of.createParameterRef().withName(contextParameter.getName()); @@ -2903,53 +2912,65 @@ public Expression resolveAccessor(Expression left, String memberIdentifier) { String libraryName = ((LibraryRef) left).getLibraryName(); CompiledLibrary referencedLibrary = resolveLibrary(libraryName); - Element element = referencedLibrary.resolve(memberIdentifier); + ResolvedIdentifierContext resolvedIdentifierContext = referencedLibrary.resolve(memberIdentifier); - if (element instanceof ExpressionDef) { - checkAccessLevel(libraryName, memberIdentifier, ((ExpressionDef) element).getAccessLevel()); - Expression result = - of.createExpressionRef().withLibraryName(libraryName).withName(memberIdentifier); - result.setResultType(getExpressionDefResultType((ExpressionDef) element)); - return result; - } + final Optional optElement = resolvedIdentifierContext.getExactMatchElement(); - if (element instanceof ParameterDef) { - checkAccessLevel(libraryName, memberIdentifier, ((ParameterDef) element).getAccessLevel()); - Expression result = - of.createParameterRef().withLibraryName(libraryName).withName(memberIdentifier); - result.setResultType(element.getResultType()); - return result; - } + if (optElement.isPresent()) { + final Element element = optElement.get(); - if (element instanceof ValueSetDef) { - checkAccessLevel(libraryName, memberIdentifier, ((ValueSetDef) element).getAccessLevel()); - ValueSetRef result = - of.createValueSetRef().withLibraryName(libraryName).withName(memberIdentifier); - result.setResultType(element.getResultType()); - return result; - } + if (element instanceof ExpressionDef) { + checkAccessLevel(libraryName, memberIdentifier, ((ExpressionDef) element).getAccessLevel()); + Expression result = of.createExpressionRef() + .withLibraryName(libraryName) + .withName(memberIdentifier); + result.setResultType(getExpressionDefResultType((ExpressionDef) element)); + return result; + } - if (element instanceof CodeSystemDef) { - checkAccessLevel(libraryName, memberIdentifier, ((CodeSystemDef) element).getAccessLevel()); - CodeSystemRef result = - of.createCodeSystemRef().withLibraryName(libraryName).withName(memberIdentifier); - result.setResultType(element.getResultType()); - return result; - } + if (element instanceof ParameterDef) { + checkAccessLevel(libraryName, memberIdentifier, ((ParameterDef) element).getAccessLevel()); + Expression result = + of.createParameterRef().withLibraryName(libraryName).withName(memberIdentifier); + result.setResultType(element.getResultType()); + return result; + } - if (element instanceof CodeDef) { - checkAccessLevel(libraryName, memberIdentifier, ((CodeDef) element).getAccessLevel()); - CodeRef result = of.createCodeRef().withLibraryName(libraryName).withName(memberIdentifier); - result.setResultType(element.getResultType()); - return result; - } + if (element instanceof ValueSetDef) { + checkAccessLevel(libraryName, memberIdentifier, ((ValueSetDef) element).getAccessLevel()); + ValueSetRef result = + of.createValueSetRef().withLibraryName(libraryName).withName(memberIdentifier); + result.setResultType(element.getResultType()); + if (isCompatibleWith("1.5")) { + result.setPreserve(true); + } + return result; + } - if (element instanceof ConceptDef) { - checkAccessLevel(libraryName, memberIdentifier, ((ConceptDef) element).getAccessLevel()); - ConceptRef result = - of.createConceptRef().withLibraryName(libraryName).withName(memberIdentifier); - result.setResultType(element.getResultType()); - return result; + if (element instanceof CodeSystemDef) { + checkAccessLevel(libraryName, memberIdentifier, ((CodeSystemDef) element).getAccessLevel()); + CodeSystemRef result = of.createCodeSystemRef() + .withLibraryName(libraryName) + .withName(memberIdentifier); + result.setResultType(element.getResultType()); + return result; + } + + if (element instanceof CodeDef) { + checkAccessLevel(libraryName, memberIdentifier, ((CodeDef) element).getAccessLevel()); + CodeRef result = + of.createCodeRef().withLibraryName(libraryName).withName(memberIdentifier); + result.setResultType(element.getResultType()); + return result; + } + + if (element instanceof ConceptDef) { + checkAccessLevel(libraryName, memberIdentifier, ((ConceptDef) element).getAccessLevel()); + ConceptRef result = + of.createConceptRef().withLibraryName(libraryName).withName(memberIdentifier); + result.setResultType(element.getResultType()); + return result; + } } // ERROR: diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEdit.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEdit.java index 912b97f16..81212314d 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEdit.java +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEdit.java @@ -1,9 +1,10 @@ package org.cqframework.cql.cql2elm.elm; import org.hl7.cql_annotations.r1.Annotation; +import org.hl7.elm.r1.ChoiceTypeSpecifier; import org.hl7.elm.r1.Element; -public enum ElmEdit { +public enum ElmEdit implements IElmEdit { REMOVE_LOCATOR { @Override public void edit(Element element) { @@ -38,7 +39,24 @@ public void edit(Element element) { element.setResultTypeName(null); element.setResultTypeSpecifier(null); } - }; + }, + REMOVE_CHOICE_TYPE_SPECIFIER_TYPE_IF_EMPTY { + // The ChoiceTypeSpecifier ELM node has a deprecated `type` element which, if not null, clashes with the + // `"type" : "ChoiceTypeSpecifier"` field in the JSON serialization of the node. This does not happen in the XML + // serialization which nests tags inside . + // Because the `type` element is deprecated, it is not normally populated by the compiler anymore and + // stays null in the ChoiceTypeSpecifier instance. It is however set to an empty list if you just call + // ChoiceTypeSpecifier.getType() (which we do during the ELM optimization stage in the compiler), so + // this edit is needed to "protect" the downstream JSON serialization if it can be done without data loss. - public abstract void edit(Element element); + @Override + public void edit(Element element) { + if (element instanceof ChoiceTypeSpecifier) { + var choice = (ChoiceTypeSpecifier) element; + if (choice.getType().isEmpty()) { + choice.setType(null); + } + } + } + }; } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEditor.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEditor.java index 28dd8e23d..4ecd00113 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEditor.java +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/ElmEditor.java @@ -10,27 +10,31 @@ public class ElmEditor { - private final List edits; - private final FunctionalElmVisitor> visitor; + private final List edits; + private final FunctionalElmVisitor visitor; - public ElmEditor(List edits) { + public ElmEditor(List edits) { this.edits = Objects.requireNonNull(edits); - this.visitor = Visitors.from(ElmEditor::applyEdits); + this.visitor = Visitors.from((elm, context) -> elm, this::aggregateResults); } public void edit(Library library) { - this.visitor.visitLibrary(library, edits); + this.visitor.visitLibrary(library, null); + + // This is needed because aggregateResults is not called on the library itself. + this.applyEdits(library); } - protected static Void applyEdits(Trackable trackable, List edits) { - if (!(trackable instanceof Element)) { - return null; - } + protected Trackable aggregateResults(Trackable aggregate, Trackable nextResult) { + applyEdits(nextResult); + return aggregate; + } - for (ElmEdit edit : edits) { - edit.edit((Element) trackable); + protected void applyEdits(Trackable trackable) { + if (trackable instanceof Element) { + for (IElmEdit edit : edits) { + edit.edit((Element) trackable); + } } - - return null; } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/IElmEdit.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/IElmEdit.java new file mode 100644 index 000000000..ee6ba3d5d --- /dev/null +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/elm/IElmEdit.java @@ -0,0 +1,7 @@ +package org.cqframework.cql.cql2elm.elm; + +import org.hl7.elm.r1.Element; + +public interface IElmEdit { + void edit(Element element); +} diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/CompiledLibrary.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/CompiledLibrary.java index 72785eabc..1fa44d700 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/CompiledLibrary.java +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/CompiledLibrary.java @@ -33,11 +33,11 @@ public void setLibrary(Library library) { } private void checkNamespace(String identifier) { - Element existingElement = resolve(identifier); - if (existingElement != null) { + final ResolvedIdentifierContext existingResolvedIdentifierContext = resolve(identifier); + existingResolvedIdentifierContext.getExactMatchElement().ifPresent(element -> { throw new IllegalArgumentException( String.format("Identifier %s is already in use in this library.", identifier)); - } + }); } public void add(UsingDef using) { @@ -138,26 +138,25 @@ public void add(Conversion conversion) { conversions.add(conversion); } - public Element resolve(String identifier) { - return namespace.get(identifier); + public ResolvedIdentifierContext resolve(String identifier) { + if (namespace.containsKey(identifier)) { + return ResolvedIdentifierContext.exactMatch(identifier, namespace.get(identifier)); + } + + return namespace.entrySet().stream() + .filter(entry -> entry.getKey().equalsIgnoreCase(identifier)) + .map(Map.Entry::getValue) + .map(element -> ResolvedIdentifierContext.caseInsensitiveMatch(identifier, element)) + .findFirst() + .orElse(ResolvedIdentifierContext.caseInsensitiveMatch(identifier, null)); } public UsingDef resolveUsingRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof UsingDef) { - return (UsingDef) element; - } - - return null; + return resolveIdentifier(identifier, UsingDef.class); } public IncludeDef resolveIncludeRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof IncludeDef) { - return (IncludeDef) element; - } - - return null; + return resolveIdentifier(identifier, IncludeDef.class); } public String resolveIncludeAlias(VersionedIdentifier identifier) { @@ -177,57 +176,31 @@ public String resolveIncludeAlias(VersionedIdentifier identifier) { } public CodeSystemDef resolveCodeSystemRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof CodeSystemDef) { - return (CodeSystemDef) element; - } - - return null; + return resolveIdentifier(identifier, CodeSystemDef.class); } public ValueSetDef resolveValueSetRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof ValueSetDef) { - return (ValueSetDef) element; - } - - return null; + return resolveIdentifier(identifier, ValueSetDef.class); } public CodeDef resolveCodeRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof CodeDef) { - return (CodeDef) element; - } - - return null; + return resolveIdentifier(identifier, CodeDef.class); } public ConceptDef resolveConceptRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof ConceptDef) { - return (ConceptDef) element; - } - - return null; + return resolveIdentifier(identifier, ConceptDef.class); } public ParameterDef resolveParameterRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof ParameterDef) { - return (ParameterDef) element; - } - - return null; + return resolveIdentifier(identifier, ParameterDef.class); } public ExpressionDef resolveExpressionRef(String identifier) { - Element element = resolve(identifier); - if (element instanceof ExpressionDef) { - return (ExpressionDef) element; - } + return resolveIdentifier(identifier, ExpressionDef.class); + } - return null; + private T resolveIdentifier(String identifier, Class clazz) { + return resolve(identifier).resolveIdentifier(clazz); } public Iterable resolveFunctionRef(String identifier) { diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ResolvedIdentifierContext.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ResolvedIdentifierContext.java new file mode 100644 index 000000000..02ca5c1b2 --- /dev/null +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ResolvedIdentifierContext.java @@ -0,0 +1,150 @@ +package org.cqframework.cql.cql2elm.model; + +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; +import org.hl7.elm.r1.CodeDef; +import org.hl7.elm.r1.CodeSystemDef; +import org.hl7.elm.r1.ConceptDef; +import org.hl7.elm.r1.ContextDef; +import org.hl7.elm.r1.Element; +import org.hl7.elm.r1.ExpressionDef; +import org.hl7.elm.r1.OperandDef; +import org.hl7.elm.r1.ParameterDef; +import org.hl7.elm.r1.TupleElementDefinition; +import org.hl7.elm.r1.ValueSetDef; + +/** + * Context for resolved identifiers containing the identifier, the resolved element (if non-null) as well as the type + * of matching done to retrieve the element, whether case-sensitive or case-insensitive. + */ +public class ResolvedIdentifierContext { + private final String identifier; + private final Element nullableElement; + + private enum ResolvedIdentifierMatchType { + EXACT, + CASE_INSENSITIVE + } + + // TODO: enum instead? + private final ResolvedIdentifierMatchType matchType; + + public static ResolvedIdentifierContext exactMatch(String identifier, Element nullableElement) { + return new ResolvedIdentifierContext(identifier, nullableElement, ResolvedIdentifierMatchType.EXACT); + } + + public static ResolvedIdentifierContext caseInsensitiveMatch(String identifier, Element nullableElement) { + return new ResolvedIdentifierContext(identifier, nullableElement, ResolvedIdentifierMatchType.CASE_INSENSITIVE); + } + + private ResolvedIdentifierContext( + String identifier, Element nullableElement, ResolvedIdentifierMatchType matchType) { + this.identifier = identifier; + this.nullableElement = nullableElement; + this.matchType = matchType; + } + + public Optional getExactMatchElement() { + if (isExactMatch()) { + return Optional.ofNullable(nullableElement); + } + + return Optional.empty(); + } + + private boolean isExactMatch() { + return ResolvedIdentifierMatchType.EXACT == matchType; + } + + public Optional warnCaseInsensitiveIfApplicable() { + if (nullableElement != null && !isExactMatch()) { + return getName(nullableElement) + .map(name -> + String.format("Could not find identifier: [%s]. Did you mean [%s]?", identifier, name)); + } + + return Optional.empty(); + } + + public T resolveIdentifier(Class clazz) { + return getExactMatchElement().filter(clazz::isInstance).map(clazz::cast).orElse(null); + } + + public Optional getElementOfType(Class clazz) { + if (clazz.isInstance(nullableElement)) { + return Optional.of(clazz.cast(nullableElement)); + } + + return Optional.empty(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ResolvedIdentifierContext that = (ResolvedIdentifierContext) other; + return Objects.equals(identifier, that.identifier) + && Objects.equals(nullableElement, that.nullableElement) + && matchType == that.matchType; + } + + @Override + public int hashCode() { + return Objects.hash(identifier, nullableElement, matchType); + } + + @Override + public String toString() { + return new StringJoiner(", ", ResolvedIdentifierContext.class.getSimpleName() + "[", "]") + .add("identifier='" + identifier + "'") + .add("nullableElement=" + nullableElement) + .add("matchType=" + matchType) + .toString(); + } + + private static Optional getName(Element element) { + // TODO: consider other Elements that don't have getName() + if (element instanceof ExpressionDef) { + return Optional.of(((ExpressionDef) element).getName()); + } + + if (element instanceof ValueSetDef) { + return Optional.of(((ValueSetDef) element).getName()); + } + + if (element instanceof OperandDef) { + return Optional.of(((OperandDef) element).getName()); + } + + if (element instanceof TupleElementDefinition) { + return Optional.of(((TupleElementDefinition) element).getName()); + } + + if (element instanceof CodeDef) { + return Optional.of(((CodeDef) element).getName()); + } + + if (element instanceof ConceptDef) { + return Optional.of(((ConceptDef) element).getName()); + } + + if (element instanceof ParameterDef) { + return Optional.of(((ParameterDef) element).getName()); + } + + if (element instanceof CodeSystemDef) { + return Optional.of(((CodeSystemDef) element).getName()); + } + + if (element instanceof ContextDef) { + return Optional.of(((ContextDef) element).getName()); + } + + return Optional.empty(); + } +} diff --git a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/ArchitectureTest.java b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/ArchitectureTest.java index 3671456e1..6b8195e77 100644 --- a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/ArchitectureTest.java +++ b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/ArchitectureTest.java @@ -4,6 +4,7 @@ import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption; import org.cqframework.cql.cql2elm.model.LibraryRef; import org.cqframework.cql.elm.IdObjectFactory; import org.hl7.elm.r1.Element; @@ -14,7 +15,9 @@ class ArchitectureTest { @Test void ensureNoDirectElmConstruction() { - JavaClasses importedClasses = new ClassFileImporter().importPackages("org.cqframework.cql"); + JavaClasses importedClasses = new ClassFileImporter() + .withImportOption(new ImportOption.DoNotIncludeTests()) + .importPackages("org.cqframework.cql"); constructors() .that() diff --git a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/SemanticTests.java b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/SemanticTests.java index f950eda26..5d9ddf396 100644 --- a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/SemanticTests.java +++ b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/SemanticTests.java @@ -2,9 +2,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; import java.util.HashMap; @@ -755,8 +753,41 @@ void ifConditionalReturnTypes() throws IOException { } @Test - void issue863() throws IOException { - TestUtils.runSemanticTest("Issue863.cql", 0); + public void testIdentifierDoesNotResolveCaseMismatchExistIdentifier() throws IOException { + final CqlTranslator translator = + runSemanticTest("IdentifierDoesNotResolveCaseMismatchExistIdentifier_Issue598.cql", 2); + + final List errorMessages = + translator.getErrors().stream().map(Throwable::getMessage).collect(Collectors.toList()); + assertThat( + errorMessages, + contains( + "Could not resolve identifier NonExistent in the current library.", + "Could not find identifier: [IaMaDiFeReNtCaSe]. Did you mean [iAmAdIfErEnTcAsE]?")); + + final List warnings = + translator.getWarnings().stream().map(Throwable::getMessage).collect(Collectors.toList()); + assertThat(warnings, hasSize(0)); + } + + @Test + void issue1407() throws IOException { + assertNull(issue1407GetIsPreserve("1.4")); + assertTrue(issue1407GetIsPreserve("1.5")); + } + + private Boolean issue1407GetIsPreserve(String compatibilityLevel) throws IOException { + CqlTranslator translator = runSemanticTest( + "LibraryTests/Issue1407.cql", 0, new CqlCompilerOptions().withCompatibilityLevel(compatibilityLevel)); + var library = translator.toELM(); + var testExpression = library.getStatements().getDef().stream() + .filter(def -> def.getName().equals("TestStatement")) + .findFirst() + .orElseThrow() + .getExpression(); + + assertThat(testExpression, instanceOf(ValueSetRef.class)); + return ((ValueSetRef) testExpression).isPreserve(); } private CqlTranslator runSemanticTest(String testFileName) throws IOException { diff --git a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/TranslationTests.java b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/TranslationTests.java index c996384fe..afcfa35f5 100644 --- a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/TranslationTests.java +++ b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/TranslationTests.java @@ -380,4 +380,14 @@ void hidingVariousUseCases() throws IOException { hidingLetFhir, hidingAliasLet)); } + + @Test + void abstractClassNotRetrievable() throws IOException { + // See: https://github.com/cqframework/clinical_quality_language/issues/1392 + final CqlTranslator translator = TestUtils.runSemanticTest("abstractClassNotRetrievable.cql", 1); + final List errors = translator.getErrors(); + final List errorMessages = + errors.stream().map(Throwable::getMessage).toList(); + assertThat(errorMessages, contains("Specified data type DomainResource does not support retrieval.")); + } } diff --git a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/elm/ElmEditTest.java b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/elm/ElmEditTest.java new file mode 100644 index 000000000..b86a8b878 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/elm/ElmEditTest.java @@ -0,0 +1,32 @@ +package org.cqframework.cql.cql2elm.elm; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.hl7.elm.r1.ChoiceTypeSpecifier; +import org.hl7.elm.r1.NamedTypeSpecifier; +import org.hl7.elm.r1.TypeSpecifier; +import org.junit.jupiter.api.Test; + +class ElmEditTest { + + @Test + void removeChoiceTypeSpecifierTypeIfEmpty() { + var extChoiceTypeSpecifier = new ExtChoiceTypeSpecifier(); + + extChoiceTypeSpecifier.setType(List.of()); + ElmEdit.REMOVE_CHOICE_TYPE_SPECIFIER_TYPE_IF_EMPTY.edit(extChoiceTypeSpecifier); + assertNull(extChoiceTypeSpecifier.getType()); + + var typeSpecifiers = List.of((TypeSpecifier) new NamedTypeSpecifier()); + extChoiceTypeSpecifier.setType(typeSpecifiers); + ElmEdit.REMOVE_CHOICE_TYPE_SPECIFIER_TYPE_IF_EMPTY.edit(extChoiceTypeSpecifier); + assertSame(typeSpecifiers, extChoiceTypeSpecifier.getType()); + } + + private static class ExtChoiceTypeSpecifier extends ChoiceTypeSpecifier { + public List getType() { + return type; + } + } +} diff --git a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/elm/ElmEditorTest.java b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/elm/ElmEditorTest.java new file mode 100644 index 000000000..6209127ae --- /dev/null +++ b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/elm/ElmEditorTest.java @@ -0,0 +1,37 @@ +package org.cqframework.cql.cql2elm.elm; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.hl7.elm.r1.ChoiceTypeSpecifier; +import org.hl7.elm.r1.Library; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ElmEditorTest { + private int editCount = 0; + private final ElmEditor editor = new ElmEditor(List.of(element -> editCount++)); + + @BeforeEach + void beforeEach() { + editCount = 0; + } + + @Test + void edit() { + editor.edit(new Library()); + assertEquals(editCount, 1); + } + + @Test + void applyEdits1() { + editor.applyEdits(null); + assertEquals(editCount, 0); + } + + @Test + void applyEdits2() { + editor.applyEdits(new ChoiceTypeSpecifier()); + assertEquals(editCount, 1); + } +} diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/IdentifierDoesNotResolveCaseMismatchExistIdentifier_Issue598.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/IdentifierDoesNotResolveCaseMismatchExistIdentifier_Issue598.cql new file mode 100644 index 000000000..294264c57 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/IdentifierDoesNotResolveCaseMismatchExistIdentifier_Issue598.cql @@ -0,0 +1,10 @@ +library TestIdentifierDoesNotResolveCaseMismatchExistIdentifier + +define "iAmAdIfErEnTcAsE" : 1 + +define doesNotResolveExact : + "NonExistent" + 1 + +define doesNotResolveCaseSensitive : +// "iAmAdIfErEnTcAsE" + 1 make sure comment isn't considered + "IaMaDiFeReNtCaSe" + 1 diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/LibraryTests/Issue1407.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/LibraryTests/Issue1407.cql new file mode 100644 index 000000000..a6a63395a --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/LibraryTests/Issue1407.cql @@ -0,0 +1,11 @@ +library Issue1407 + +using FHIR version '4.0.1' + +include FHIRHelpers version '4.0.1' called FHIRHelpers + +include Issue1407ValueSets + +context Patient + +define TestStatement: Issue1407ValueSets.TestValueSet \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/LibraryTests/Issue1407ValueSets.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/LibraryTests/Issue1407ValueSets.cql new file mode 100644 index 000000000..98f5506af --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/LibraryTests/Issue1407ValueSets.cql @@ -0,0 +1,5 @@ +library Issue1407ValueSets + +using FHIR version '4.0.1' + +valueset TestValueSet: 'garb' \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/OperatorTests/CqlComparisonOperators.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/OperatorTests/CqlComparisonOperators.cql index d5fbc9571..3292d155c 100644 --- a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/OperatorTests/CqlComparisonOperators.cql +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/OperatorTests/CqlComparisonOperators.cql @@ -28,7 +28,7 @@ define TupleEqJohnJane: Tuple { Id : 1, Name : 'John' } = Tuple { Id : 2, Name : define TupleEqJohn1John2: Tuple { Id : 1, Name : 'John' } = Tuple { Id : 2, Name : 'John' } define TupleEqDateTimeTrue: Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 0, 0, 0, 0) } = Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 0, 0, 0, 0) } define TupleEqDateTimeFalse: Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 0, 0, 0, 0) } = Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 5, 0, 0, 0) } -define TupleEqDateTimeNull: Tuple { dateId: 12, Date: DateTime(2012, 1, 1) } = Tuple { dateId: 12, Date: DateTime(2012, 1, 1) } +define TupleEqDateTimeTrue2: Tuple { dateId: 12, Date: DateTime(2012, 1, 1) } = Tuple { dateId: 12, Date: DateTime(2012, 1, 1) } define TupleEqTimeTrue: Tuple { timeId: 55, TheTime: @T05:15:15.541 } = Tuple { timeId: 55, TheTime: @T05:15:15.541 } define TupleEqTimeFalse: Tuple { timeId: 55, TheTime: @T05:15:15.541 } = Tuple { timeId: 55, TheTime: @T05:15:15.540 } define ListEqEmptyEmpty : {} = {} @@ -45,7 +45,7 @@ define DateTimeEqJanJan: DateTime(2014, 1, 5, 5, 0, 0, 0, 0) = DateTime(2014, 1, define DateTimeEqJanJuly: DateTime(2014, 1, 5, 5, 0, 0, 0, 0) = DateTime(2014, 7, 5, 5, 0, 0, 0, 0) define DateTimeEqMissingArg: DateTime(2015, 1, 5, 5, 0, 0) = DateTime(2015, 1, 5, 5, 0, 0) define DateTimeEqNull: DateTime(null) = DateTime(null) -define DateTimeEqNull2: DateTime(2001, 1, 1, null) = DateTime(2001, 1, 1, null, null) +define DateTimeEqTrue: DateTime(2001, 1, 1, null) = DateTime(2001, 1, 1, null, null) define DateTimeUTC: @2014-01-25T14:30:14.559+01:00 = @2014-01-25T14:30:14.559+01:00 define TimeEq10A10A: @T10:00:00.000 = @T10:00:00.000 define TimeEq10A10P: @T10:00:00.000 = @T22:00:00.000 diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/abstractClassNotRetrievable.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/abstractClassNotRetrievable.cql new file mode 100644 index 000000000..eaa2b1c8c --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/abstractClassNotRetrievable.cql @@ -0,0 +1,10 @@ +library abstractClassNotRetrievable + +using FHIR version '4.0.1' + +include FHIRHelpers version '4.0.1' + +context Patient + +define "Bar": + [DomainResource] // should be marked non-retrievable, should give error/warning in translator diff --git a/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEvaluator.java b/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEvaluator.java index d1a838c33..4d9294069 100644 --- a/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEvaluator.java +++ b/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEvaluator.java @@ -1,6 +1,8 @@ package org.cqframework.cql.elm.evaluating; +import javax.xml.namespace.QName; import org.hl7.elm.r1.Expression; +import org.hl7.elm.r1.TypeSpecifier; public class SimpleElmEvaluator { @@ -41,4 +43,12 @@ public static boolean dateRangesEqual(Expression left, Expression right) { public static boolean codesEqual(Expression left, Expression right) { return engine.codesEqual(left, right); } + + public static boolean typeSpecifiersEqual(TypeSpecifier left, TypeSpecifier right) { + return engine.typeSpecifiersEqual(left, right); + } + + public static boolean qnamesEqual(QName left, QName right) { + return engine.qnamesEqual(left, right); + } } diff --git a/Src/java/engine-fhir/build.gradle b/Src/java/engine-fhir/build.gradle index 178c6ba77..3072caf70 100644 --- a/Src/java/engine-fhir/build.gradle +++ b/Src/java/engine-fhir/build.gradle @@ -26,17 +26,15 @@ generateSources { } jacocoTestReport { - dependsOn ':cql-to-elm:test' - dependsOn ':engine:test' - dependsOn ':engine-fhir:test' - sourceDirectories.setFrom(files( + "${projectDir}/../elm/src/main/java", "${projectDir}/../cql-to-elm/src/main/java", "${projectDir}/../engine/src/main/java", "${projectDir}/../engine-fhir/src/main/java", )) classDirectories.setFrom(files( + "${projectDir}/../elm/build/classes/java/main", "${projectDir}/../cql-to-elm/build/classes/java/main", "${projectDir}/../engine/build/classes/java/main", "${projectDir}/../engine-fhir/build/classes/java/main", diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java index 4002d9d2b..abc3a1828 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java @@ -42,7 +42,7 @@ public boolean isFhirType(Object value) { } @Override - public Iterable toFhirTypes(Iterable values) { + public List toFhirTypes(Iterable values) { List converted = new ArrayList<>(); for (Object value : values) { if (value == null) { diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java index 1b1846617..b5813fbea 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java @@ -221,13 +221,86 @@ public ICompositeType toFhirRange(Interval value) { return range; } + private static BooleanType emptyBooleanWithExtension(String url, Type value) { + var result = new BooleanType((String) null); + result.addExtension().setUrl(url).setValue(value); + return result; + } + + private static void addPartWithNameAndValue( + Parameters.ParametersParameterComponent param, String key, Object value) { + if (value instanceof Parameters.ParametersParameterComponent) { + var part = (Parameters.ParametersParameterComponent) value; + part.setName(key); + param.addPart(part); + } else { + var part = param.addPart().setName(key); + if (value instanceof Resource) { + part.setResource((Resource) value); + } else if (value instanceof Type) { + part.setValue((Type) value); + } else { + throw new IllegalArgumentException( + "Unsupported FHIR type: " + value.getClass().getName()); + } + } + } + + private static Iterable asIterable(Object value) { + if (value instanceof Iterable) { + return (Iterable) value; + } else { + return null; + } + } + + private void addElementToParameter(Parameters.ParametersParameterComponent param, String key, Object value) { + if (value == null) { + // Null value, add a single empty value with an extension indicating the reason + var dataAbsentValue = emptyBooleanWithExtension( + DATA_ABSENT_REASON_EXT_URL, new CodeType(DATA_ABSENT_REASON_UNKNOWN_CODE)); + addPartWithNameAndValue(param, key, dataAbsentValue); + return; + } + + var iterable = asIterable(value); + if (iterable == null) { + // Single, non-null value + addPartWithNameAndValue(param, key, toFhirType(value)); + return; + } + + if (!iterable.iterator().hasNext()) { + // Empty list + var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true)); + addPartWithNameAndValue(param, key, emptyListValue); + } else { + // Non-empty list, one part per value + var fhirTypes = this.toFhirTypes(iterable); + for (var fhirType : fhirTypes) { + addPartWithNameAndValue(param, key, fhirType); + } + } + } + @Override public IBase toFhirTuple(Tuple value) { if (value == null) { return null; } - throw new NotImplementedException("can't convert Tuples"); + var parameters = new Parameters(); + var param = parameters.addParameter(); + + if (value.getElements().isEmpty()) { + param.setValue(emptyBooleanWithExtension(EMPTY_TUPLE_EXT_URL, new BooleanType(true))); + } + + for (String key : value.getElements().keySet()) { + addElementToParameter(param, key, value.getElements().get(key)); + } + + return param; } @Override diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java index c24dee528..d58927482 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java @@ -220,13 +220,86 @@ public ICompositeType toFhirRange(Interval value) { return range; } + private static BooleanType emptyBooleanWithExtension(String url, Type value) { + var result = new BooleanType((String) null); + result.addExtension().setUrl(url).setValue(value); + return result; + } + + private static void addPartWithNameAndValue( + Parameters.ParametersParameterComponent param, String key, Object value) { + if (value instanceof Parameters.ParametersParameterComponent) { + var part = (Parameters.ParametersParameterComponent) value; + part.setName(key); + param.addPart(part); + } else { + var part = param.addPart().setName(key); + if (value instanceof Resource) { + part.setResource((Resource) value); + } else if (value instanceof Type) { + part.setValue((Type) value); + } else { + throw new IllegalArgumentException( + "Unsupported FHIR type: " + value.getClass().getName()); + } + } + } + + private static Iterable asIterable(Object value) { + if (value instanceof Iterable) { + return (Iterable) value; + } else { + return null; + } + } + + private void addElementToParameter(Parameters.ParametersParameterComponent param, String key, Object value) { + if (value == null) { + // Null value, add a single empty value with an extension indicating the reason + var dataAbsentValue = emptyBooleanWithExtension( + DATA_ABSENT_REASON_EXT_URL, new CodeType(DATA_ABSENT_REASON_UNKNOWN_CODE)); + addPartWithNameAndValue(param, key, dataAbsentValue); + return; + } + + var iterable = asIterable(value); + if (iterable == null) { + // Single, non-null value + addPartWithNameAndValue(param, key, toFhirType(value)); + return; + } + + if (!iterable.iterator().hasNext()) { + // Empty list + var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true)); + addPartWithNameAndValue(param, key, emptyListValue); + } else { + // Non-empty list, one part per value + var fhirTypes = this.toFhirTypes(iterable); + for (var fhirType : fhirTypes) { + addPartWithNameAndValue(param, key, fhirType); + } + } + } + @Override public IBase toFhirTuple(Tuple value) { if (value == null) { return null; } - throw new NotImplementedException("can't convert Tuples"); + var parameters = new Parameters(); + var param = parameters.addParameter(); + + if (value.getElements().isEmpty()) { + param.setValue(emptyBooleanWithExtension(EMPTY_TUPLE_EXT_URL, new BooleanType(true))); + } + + for (String key : value.getElements().keySet()) { + addElementToParameter(param, key, value.getElements().get(key)); + } + + return param; } @Override diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java index 835479e1c..c81c3e48f 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java @@ -1,6 +1,7 @@ package org.opencds.cqf.cql.engine.fhir.converter; import java.math.BigDecimal; +import java.util.List; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -28,6 +29,12 @@ */ public interface FhirTypeConverter { + static final String EMPTY_LIST_EXT_URL = "http://hl7.org/fhir/StructureDefinition/cqf-isEmptyList"; + static final String EMPTY_TUPLE_EXT_URL = "http://hl7.org/fhir/StructureDefinition/cqf-isEmptyTuple"; + static final String DATA_ABSENT_REASON_EXT_URL = "http://hl7.org/fhir/StructureDefinition/data-absent-reason"; + static final String DATA_ABSENT_REASON_UNKNOWN_CODE = "unknown"; + static final String CQL_TYPE_EXT_URL = "http://hl7.org/fhir/StructureDefinition/cqf-cqlType"; + // CQL-to-FHIR conversions /** @@ -53,9 +60,9 @@ public interface FhirTypeConverter { * nulls, and sublist hierarchy * * @param values an Iterable containing CQL structures, nulls, or sublists - * @return an Iterable containing FHIR types, nulls, and sublists + * @return an List containing FHIR types, nulls, and sublists */ - public Iterable toFhirTypes(Iterable values); + public List toFhirTypes(Iterable values); /** * Converts a String to a FHIR Id diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java index c24ef73b0..b69f1f8d6 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java @@ -179,7 +179,8 @@ public ICompositeType toFhirPeriod(Interval value) { return period; } else if (getSimpleName(value.getPointType().getTypeName()).equals("Date")) { - // TODO: This will construct DateTimeType values in FHIR with the system timezone id, not the + // TODO: This will construct DateTimeType values in FHIR with the system + // timezone id, not the // timezoneoffset of the evaluation request..... this is a bug waiting to happen if (value.getStart() != null) { period.setStart(toFhirDate((Date) value.getStart()).getValue()); @@ -212,13 +213,86 @@ public ICompositeType toFhirRange(Interval value) { return range; } + private static BooleanType emptyBooleanWithExtension(String url, Type value) { + var result = new BooleanType((String) null); + result.addExtension().setUrl(url).setValue(value); + return result; + } + + private static void addPartWithNameAndValue( + Parameters.ParametersParameterComponent param, String key, Object value) { + if (value instanceof Parameters.ParametersParameterComponent) { + var part = (Parameters.ParametersParameterComponent) value; + part.setName(key); + param.addPart(part); + } else { + var part = param.addPart().setName(key); + if (value instanceof Resource) { + part.setResource((Resource) value); + } else if (value instanceof Type) { + part.setValue((Type) value); + } else { + throw new IllegalArgumentException( + "Unsupported FHIR type: " + value.getClass().getName()); + } + } + } + + private static Iterable asIterable(Object value) { + if (value instanceof Iterable) { + return (Iterable) value; + } else { + return null; + } + } + + private void addElementToParameter(Parameters.ParametersParameterComponent param, String key, Object value) { + if (value == null) { + // Null value, add a single empty value with an extension indicating the reason + var dataAbsentValue = emptyBooleanWithExtension( + DATA_ABSENT_REASON_EXT_URL, new CodeType(DATA_ABSENT_REASON_UNKNOWN_CODE)); + addPartWithNameAndValue(param, key, dataAbsentValue); + return; + } + + var iterable = asIterable(value); + if (iterable == null) { + // Single, non-null value + addPartWithNameAndValue(param, key, toFhirType(value)); + return; + } + + if (!iterable.iterator().hasNext()) { + // Empty list + var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true)); + addPartWithNameAndValue(param, key, emptyListValue); + } else { + // Non-empty list, one part per value + var fhirTypes = this.toFhirTypes(iterable); + for (var fhirType : fhirTypes) { + addPartWithNameAndValue(param, key, fhirType); + } + } + } + @Override public IBase toFhirTuple(Tuple value) { if (value == null) { return null; } - throw new NotImplementedException("can't convert Tuples"); + var parameters = new Parameters(); + var param = parameters.addParameter(); + + if (value.getElements().isEmpty()) { + param.setValue(emptyBooleanWithExtension(EMPTY_TUPLE_EXT_URL, new BooleanType(true))); + } + + for (String key : value.getElements().keySet()) { + addElementToParameter(param, key, value.getElements().get(key)); + } + + return param; } @Override diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java index c53b694e2..c8c53194e 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java @@ -212,13 +212,86 @@ public ICompositeType toFhirRange(Interval value) { return range; } + private static BooleanType emptyBooleanWithExtension(String url, DataType value) { + var result = new BooleanType((String) null); + result.addExtension().setUrl(url).setValue(value); + return result; + } + + private static void addPartWithNameAndValue( + Parameters.ParametersParameterComponent param, String key, Object value) { + if (value instanceof Parameters.ParametersParameterComponent) { + var part = (Parameters.ParametersParameterComponent) value; + part.setName(key); + param.addPart(part); + } else { + var part = param.addPart().setName(key); + if (value instanceof Resource) { + part.setResource((Resource) value); + } else if (value instanceof DataType) { + part.setValue((DataType) value); + } else { + throw new IllegalArgumentException( + "Unsupported FHIR type: " + value.getClass().getName()); + } + } + } + + private static Iterable asIterable(Object value) { + if (value instanceof Iterable) { + return (Iterable) value; + } else { + return null; + } + } + + private void addElementToParameter(Parameters.ParametersParameterComponent param, String key, Object value) { + if (value == null) { + // Null value, add a single empty value with an extension indicating the reason + var dataAbsentValue = emptyBooleanWithExtension( + DATA_ABSENT_REASON_EXT_URL, new CodeType(DATA_ABSENT_REASON_UNKNOWN_CODE)); + addPartWithNameAndValue(param, key, dataAbsentValue); + return; + } + + var iterable = asIterable(value); + if (iterable == null) { + // Single, non-null value + addPartWithNameAndValue(param, key, toFhirType(value)); + return; + } + + if (!iterable.iterator().hasNext()) { + // Empty list + var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true)); + addPartWithNameAndValue(param, key, emptyListValue); + } else { + // Non-empty list, one part per value + var fhirTypes = this.toFhirTypes(iterable); + for (var fhirType : fhirTypes) { + addPartWithNameAndValue(param, key, fhirType); + } + } + } + @Override public IBase toFhirTuple(Tuple value) { if (value == null) { return null; } - throw new NotImplementedException("can't convert Tuples"); + var parameters = new Parameters(); + var param = parameters.addParameter(); + + if (value.getElements().isEmpty()) { + param.setValue(emptyBooleanWithExtension(EMPTY_TUPLE_EXT_URL, new BooleanType(true))); + } + + for (String key : value.getElements().keySet()) { + addElementToParameter(param, key, value.getElements().get(key)); + } + + return param; } @Override diff --git a/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsDstu3Test.java b/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsDstu3Test.java index 33a5d2157..5eeace57e 100644 --- a/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsDstu3Test.java +++ b/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsDstu3Test.java @@ -39,7 +39,8 @@ public static Object[][] dataMethod() { for (Group group : loadTestsFile(file).getGroup()) { for (org.hl7.fhirpath.tests.Test test : group.getTest()) { if (!"2.1.0".equals(test.getVersion())) { // unsupported version - testsToRun.add(new Object[] {file, group, test}); + var name = getTestName(file, group, test); + testsToRun.add(new Object[] {name, test}); } } } @@ -146,17 +147,16 @@ public static Object[][] dataMethod() { "stu3/tests-fhir-r3/testWhere(Patient.name.where(given = 'Jim').count() = 1)", "stu3/tests-fhir-r3/testWhere(Patient.name.where(given = 'X').count() = 0)"); - public String getTestName(String file, Group group, org.hl7.fhirpath.tests.Test test) { + public static String getTestName(String file, Group group, org.hl7.fhirpath.tests.Test test) { return file.replaceAll(".xml", "") + "/" + group.getName() + (test.getName() != null ? "/" + test.getName() : "") + "(" + test.getExpression().getValue() + ")"; } - @ParameterizedTest + @ParameterizedTest(name = "{0}") @MethodSource("dataMethod") - void test(String file, Group group, org.hl7.fhirpath.tests.Test test) throws UcumException { - var name = getTestName(file, group, test); + void test(String name, org.hl7.fhirpath.tests.Test test) throws UcumException { Assumptions.assumeFalse(SKIP.contains(name), "Skipping " + name); runTest(test, "stu3/input/", fhirContext, provider, fhirModelResolver); } diff --git a/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsR4Test.java b/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsR4Test.java index 2f733ca0f..8468e7cb5 100644 --- a/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsR4Test.java +++ b/Src/java/engine-fhir/src/test/java/org/hl7/fhirpath/CQLOperationsR4Test.java @@ -57,7 +57,8 @@ public static Object[][] dataMethod() { for (Group group : loadTestsFile(file).getGroup()) { for (org.hl7.fhirpath.tests.Test test : group.getTest()) { if (!"2.1.0".equals(test.getVersion())) { // unsupported version - testsToRun.add(new Object[] {file, group, test}); + var name = getTestName(file, group, test); + testsToRun.add(new Object[] {name, test}); } } } @@ -83,7 +84,6 @@ public static Object[][] dataMethod() { "cql/CqlComparisonOperatorsTest/Less Or Equal/LessOrEqualM1CM1", "cql/CqlComparisonOperatorsTest/Less Or Equal/LessOrEqualM1CM10", "cql/CqlComparisonOperatorsTest/Not Equal/QuantityNotEqCM1M01", - "cql/CqlDateTimeOperatorsTest/DateTimeComponentFrom/DateTimeComponentFromTimezone", "cql/CqlDateTimeOperatorsTest/Duration/DateTimeDurationBetweenYear", "cql/CqlDateTimeOperatorsTest/Uncertainty tests/DateTimeDurationBetweenUncertainAdd", "cql/CqlDateTimeOperatorsTest/Uncertainty tests/DateTimeDurationBetweenUncertainInterval", @@ -92,7 +92,6 @@ public static Object[][] dataMethod() { "cql/CqlDateTimeOperatorsTest/Uncertainty tests/DateTimeDurationBetweenUncertainSubtract", "cql/CqlDateTimeOperatorsTest/Uncertainty tests/DurationInDaysA", "cql/CqlDateTimeOperatorsTest/Uncertainty tests/DurationInDaysAA", - "cql/CqlDateTimeOperatorsTest/Uncertainty tests/TimeDurationBetweenHourDiffPrecision", "cql/CqlIntervalOperatorsTest/Intersect/TestIntersectNull", "cql/CqlIntervalOperatorsTest/Intersect/TestIntersectNull1", "cql/CqlIntervalOperatorsTest/Intersect/TestIntersectNull2", @@ -103,6 +102,9 @@ public static Object[][] dataMethod() { "cql/CqlIntervalOperatorsTest/Expand/ExpandPer1", "cql/CqlIntervalOperatorsTest/Expand/ExpandPer2Days", "cql/CqlIntervalOperatorsTest/Expand/ExpandPerMinute", + "cql/CqlListOperatorsTest/Equal/EqualABCAnd123", + "cql/CqlListOperatorsTest/Equal/Equal123AndABC", + "cql/CqlListOperatorsTest/Equal/Equal123AndString123", "cql/CqlListOperatorsTest/Equivalent/Equivalent123AndABC", "cql/CqlListOperatorsTest/Equivalent/Equivalent123AndString123", "cql/CqlListOperatorsTest/Equivalent/EquivalentABCAnd123", @@ -278,7 +280,7 @@ public static Object[][] dataMethod() { "r4/tests-fhir-r4/testWhere/testWhere3", "r4/tests-fhir-r4/testWhere/testWhere4"); - public String getTestName(String file, Group group, org.hl7.fhirpath.tests.Test test) { + public static String getTestName(String file, Group group, org.hl7.fhirpath.tests.Test test) { return file.replaceAll(".xml", "") + "/" + group.getName() + "/" + (test.getName() != null @@ -286,10 +288,9 @@ public String getTestName(String file, Group group, org.hl7.fhirpath.tests.Test : test.getExpression().getValue()); } - @ParameterizedTest + @ParameterizedTest(name = "{0}") @MethodSource("dataMethod") - void test(String file, Group group, org.hl7.fhirpath.tests.Test test) throws UcumException { - var name = getTestName(file, group, test); + void test(String name, org.hl7.fhirpath.tests.Test test) throws UcumException { Assumptions.assumeFalse(SKIP.contains(name), "Skipping " + name); runTest(test, "r4/input/", fhirContext, provider, fhirModelResolver); } diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java index 44c468525..e35315c88 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java @@ -18,6 +18,7 @@ import java.util.Iterator; import java.util.List; import java.util.TimeZone; +import java.util.stream.Collectors; import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.dstu2.model.Attachment; import org.hl7.fhir.dstu2.model.Base; @@ -27,9 +28,12 @@ import org.hl7.fhir.dstu2.model.DateTimeType; import org.hl7.fhir.dstu2.model.DateType; import org.hl7.fhir.dstu2.model.DecimalType; +import org.hl7.fhir.dstu2.model.Encounter; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.InstantType; import org.hl7.fhir.dstu2.model.IntegerType; +import org.hl7.fhir.dstu2.model.Parameters; +import org.hl7.fhir.dstu2.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.dstu2.model.Patient; import org.hl7.fhir.dstu2.model.Period; import org.hl7.fhir.dstu2.model.Range; @@ -40,6 +44,7 @@ import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -67,7 +72,7 @@ protected Boolean compareIterables(Iterable left, Iterable right return !leftIterator.hasNext() && !rightIterator.hasNext(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "null"}) protected Boolean compareObjects(Object left, Object right) { if (left == null ^ right == null) { return false; @@ -522,15 +527,102 @@ void invalidIntervalToFhirInterval() { }); } + private static List getPartsByName(ParametersParameterComponent ppc, String name) { + return ppc.getPart().stream().filter(p -> p.getName().equals(name)).collect(Collectors.toList()); + } + @Test void tupleToFhirTuple() { - IBase expected = typeConverter.toFhirTuple(null); - assertNull(expected); + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(null); + assertNull(actual); var tuple = new Tuple(); - assertThrows(NotImplementedException.class, () -> { - typeConverter.toFhirTuple(tuple); - }); + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + Assertions.assertNotNull(actual); + assertEquals( + FhirTypeConverter.EMPTY_TUPLE_EXT_URL, + actual.getValue().getExtension().get(0).getUrl()); + + var ints = new ArrayList(); + for (int i = 0; i < 5; i++) { + ints.add(i); + } + + tuple.getElements().put("V", ints); + tuple.getElements().put("W", null); + tuple.getElements().put("X", 5); + tuple.getElements().put("Y", new Encounter().setId("123")); + tuple.getElements().put("Z", new ArrayList<>()); + + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + var first = actual; + assertEquals(9, first.getPart().size()); + + var v = getPartsByName(first, "V"); + assertEquals(5, v.size()); + assertEquals(0, ((IntegerType) v.get(0).getValue()).getValue()); + + var w = getPartsByName(first, "W").get(0); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + w.getValue().getExtension().get(0).getUrl()); + + var x = getPartsByName(first, "X").get(0); + assertEquals(5, ((IntegerType) x.getValue()).getValue()); + + var y = getPartsByName(first, "Y").get(0); + assertEquals("123", y.getResource().getId()); + + var z = getPartsByName(first, "Z").get(0); + assertEquals( + FhirTypeConverter.EMPTY_LIST_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + } + + @Test + void complexTupleToFhirTuple() { + var innerTuple = new Tuple(); + innerTuple.getElements().put("X", 1); + innerTuple.getElements().put("Y", 2); + innerTuple.getElements().put("Z", null); + var outerTuple = new Tuple(); + outerTuple.getElements().put("A", innerTuple); + var tupleList = new ArrayList(); + for (int i = 0; i < 3; i++) { + var elementTuple = new Tuple(); + elementTuple.getElements().put("P", i); + elementTuple.getElements().put("Q", i + 1); + tupleList.add(elementTuple); + } + outerTuple.getElements().put("B", tupleList); + + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(outerTuple); + var first = actual; + assertEquals(4, first.getPart().size()); + + var a = getPartsByName(first, "A"); + assertEquals(1, a.size()); + assertEquals(3, a.get(0).getPart().size()); + var x = a.get(0).getPart().get(0); + assertEquals(1, ((IntegerType) x.getValue()).getValue()); + var y = a.get(0).getPart().get(1); + assertEquals(2, ((IntegerType) y.getValue()).getValue()); + var z = a.get(0).getPart().get(2); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + + var b = getPartsByName(first, "B"); + assertEquals(3, b.size()); + var b1 = b.get(0); + var p = getPartsByName(b1, "P"); + assertEquals(1, p.size()); + assertEquals(0, ((IntegerType) p.get(0).getValue()).getValue()); + var q = getPartsByName(b1, "Q"); + assertEquals(1, q.size()); + assertEquals(1, ((IntegerType) q.get(0).getValue()).getValue()); } // FHIR-to-CQL diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java index 61cdc6519..38a75bce3 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -18,6 +19,7 @@ import java.util.Iterator; import java.util.List; import java.util.TimeZone; +import java.util.stream.Collectors; import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.dstu3.model.Attachment; import org.hl7.fhir.dstu3.model.Base; @@ -27,9 +29,12 @@ import org.hl7.fhir.dstu3.model.DateTimeType; import org.hl7.fhir.dstu3.model.DateType; import org.hl7.fhir.dstu3.model.DecimalType; +import org.hl7.fhir.dstu3.model.Encounter; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.InstantType; import org.hl7.fhir.dstu3.model.IntegerType; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Period; import org.hl7.fhir.dstu3.model.Range; @@ -67,7 +72,7 @@ protected Boolean compareIterables(Iterable left, Iterable right return !leftIterator.hasNext() && !rightIterator.hasNext(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "null"}) protected Boolean compareObjects(Object left, Object right) { if (left == null ^ right == null) { return false; @@ -526,15 +531,102 @@ void invalidIntervalToFhirInterval() { }); } + private static List getPartsByName(ParametersParameterComponent ppc, String name) { + return ppc.getPart().stream().filter(p -> p.getName().equals(name)).collect(Collectors.toList()); + } + @Test void tupleToFhirTuple() { - IBase expected = typeConverter.toFhirTuple(null); - assertNull(expected); + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(null); + assertNull(actual); var tuple = new Tuple(); - assertThrows(NotImplementedException.class, () -> { - typeConverter.toFhirTuple(tuple); - }); + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + assertNotNull(actual); + assertEquals( + FhirTypeConverter.EMPTY_TUPLE_EXT_URL, + actual.getValue().getExtension().get(0).getUrl()); + + var ints = new ArrayList(); + for (int i = 0; i < 5; i++) { + ints.add(i); + } + + tuple.getElements().put("V", ints); + tuple.getElements().put("W", null); + tuple.getElements().put("X", 5); + tuple.getElements().put("Y", new Encounter().setId("123")); + tuple.getElements().put("Z", new ArrayList<>()); + + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + var first = actual; + assertEquals(9, first.getPart().size()); + + var v = getPartsByName(first, "V"); + assertEquals(5, v.size()); + assertEquals(0, ((IntegerType) v.get(0).getValue()).getValue()); + + var w = getPartsByName(first, "W").get(0); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + w.getValue().getExtension().get(0).getUrl()); + + var x = getPartsByName(first, "X").get(0); + assertEquals(5, ((IntegerType) x.getValue()).getValue()); + + var y = getPartsByName(first, "Y").get(0); + assertEquals("123", y.getResource().getId()); + + var z = getPartsByName(first, "Z").get(0); + assertEquals( + FhirTypeConverter.EMPTY_LIST_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + } + + @Test + void complexTupleToFhirTuple() { + var innerTuple = new Tuple(); + innerTuple.getElements().put("X", 1); + innerTuple.getElements().put("Y", 2); + innerTuple.getElements().put("Z", null); + var outerTuple = new Tuple(); + outerTuple.getElements().put("A", innerTuple); + var tupleList = new ArrayList(); + for (int i = 0; i < 3; i++) { + var elementTuple = new Tuple(); + elementTuple.getElements().put("P", i); + elementTuple.getElements().put("Q", i + 1); + tupleList.add(elementTuple); + } + outerTuple.getElements().put("B", tupleList); + + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(outerTuple); + var first = actual; + assertEquals(4, first.getPart().size()); + + var a = getPartsByName(first, "A"); + assertEquals(1, a.size()); + assertEquals(3, a.get(0).getPart().size()); + var x = a.get(0).getPart().get(0); + assertEquals(1, ((IntegerType) x.getValue()).getValue()); + var y = a.get(0).getPart().get(1); + assertEquals(2, ((IntegerType) y.getValue()).getValue()); + var z = a.get(0).getPart().get(2); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + + var b = getPartsByName(first, "B"); + assertEquals(3, b.size()); + var b1 = b.get(0); + var p = getPartsByName(b1, "P"); + assertEquals(1, p.size()); + assertEquals(0, ((IntegerType) p.get(0).getValue()).getValue()); + var q = getPartsByName(b1, "Q"); + assertEquals(1, q.size()); + assertEquals(1, ((IntegerType) q.get(0).getValue()).getValue()); } // FHIR-to-CQL diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java index a040bddf7..0e446722c 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -18,6 +19,7 @@ import java.util.Iterator; import java.util.List; import java.util.TimeZone; +import java.util.stream.Collectors; import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -31,9 +33,12 @@ import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Encounter; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.InstantType; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.Range; @@ -67,7 +72,7 @@ protected Boolean compareIterables(Iterable left, Iterable right return !leftIterator.hasNext() && !rightIterator.hasNext(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "null"}) protected Boolean compareObjects(Object left, Object right) { if (left == null ^ right == null) { return false; @@ -526,15 +531,102 @@ void invalidIntervalToFhirInterval() { }); } + private static List getPartsByName(ParametersParameterComponent ppc, String name) { + return ppc.getPart().stream().filter(p -> p.getName().equals(name)).collect(Collectors.toList()); + } + @Test void tupleToFhirTuple() { - IBase expected = typeConverter.toFhirTuple(null); - assertNull(expected); + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(null); + assertNull(actual); var tuple = new Tuple(); - assertThrows(NotImplementedException.class, () -> { - typeConverter.toFhirTuple(tuple); - }); + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + assertNotNull(actual); + assertEquals( + FhirTypeConverter.EMPTY_TUPLE_EXT_URL, + actual.getValue().getExtension().get(0).getUrl()); + + var ints = new ArrayList(); + for (int i = 0; i < 5; i++) { + ints.add(i); + } + + tuple.getElements().put("V", ints); + tuple.getElements().put("W", null); + tuple.getElements().put("X", 5); + tuple.getElements().put("Y", new Encounter().setId("123")); + tuple.getElements().put("Z", new ArrayList<>()); + + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + var first = actual; + assertEquals(9, first.getPart().size()); + + var v = getPartsByName(first, "V"); + assertEquals(5, v.size()); + assertEquals(0, ((IntegerType) v.get(0).getValue()).getValue()); + + var w = getPartsByName(first, "W").get(0); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + w.getValue().getExtension().get(0).getUrl()); + + var x = getPartsByName(first, "X").get(0); + assertEquals(5, ((IntegerType) x.getValue()).getValue()); + + var y = getPartsByName(first, "Y").get(0); + assertEquals("123", y.getResource().getId()); + + var z = getPartsByName(first, "Z").get(0); + assertEquals( + FhirTypeConverter.EMPTY_LIST_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + } + + @Test + void complexTupleToFhirTuple() { + var innerTuple = new Tuple(); + innerTuple.getElements().put("X", 1); + innerTuple.getElements().put("Y", 2); + innerTuple.getElements().put("Z", null); + var outerTuple = new Tuple(); + outerTuple.getElements().put("A", innerTuple); + var tupleList = new ArrayList(); + for (int i = 0; i < 3; i++) { + var elementTuple = new Tuple(); + elementTuple.getElements().put("P", i); + elementTuple.getElements().put("Q", i + 1); + tupleList.add(elementTuple); + } + outerTuple.getElements().put("B", tupleList); + + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(outerTuple); + var first = actual; + assertEquals(4, first.getPart().size()); + + var a = getPartsByName(first, "A"); + assertEquals(1, a.size()); + assertEquals(3, a.get(0).getPart().size()); + var x = a.get(0).getPart().get(0); + assertEquals(1, ((IntegerType) x.getValue()).getValue()); + var y = a.get(0).getPart().get(1); + assertEquals(2, ((IntegerType) y.getValue()).getValue()); + var z = a.get(0).getPart().get(2); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + + var b = getPartsByName(first, "B"); + assertEquals(3, b.size()); + var b1 = b.get(0); + var p = getPartsByName(b1, "P"); + assertEquals(1, p.size()); + assertEquals(0, ((IntegerType) p.get(0).getValue()).getValue()); + var q = getPartsByName(b1, "Q"); + assertEquals(1, q.size()); + assertEquals(1, ((IntegerType) q.get(0).getValue()).getValue()); } // FHIR-to-CQL diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java index ac055c092..b80bfb695 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -18,6 +19,7 @@ import java.util.Iterator; import java.util.List; import java.util.TimeZone; +import java.util.stream.Collectors; import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -31,9 +33,12 @@ import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.DateType; import org.hl7.fhir.r5.model.DecimalType; +import org.hl7.fhir.r5.model.Encounter; import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.InstantType; import org.hl7.fhir.r5.model.IntegerType; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r5.model.Patient; import org.hl7.fhir.r5.model.Period; import org.hl7.fhir.r5.model.Range; @@ -67,7 +72,7 @@ protected Boolean compareIterables(Iterable left, Iterable right return !leftIterator.hasNext() && !rightIterator.hasNext(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "null"}) protected Boolean compareObjects(Object left, Object right) { if (left == null ^ right == null) { return false; @@ -526,15 +531,102 @@ void invalidIntervalToFhirInterval() { }); } + private static List getPartsByName(ParametersParameterComponent ppc, String name) { + return ppc.getPart().stream().filter(p -> p.getName().equals(name)).collect(Collectors.toList()); + } + @Test void tupleToFhirTuple() { - IBase expected = typeConverter.toFhirTuple(null); - assertNull(expected); + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(null); + assertNull(actual); var tuple = new Tuple(); - assertThrows(NotImplementedException.class, () -> { - typeConverter.toFhirTuple(tuple); - }); + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + assertNotNull(actual); + assertEquals( + FhirTypeConverter.EMPTY_TUPLE_EXT_URL, + actual.getValue().getExtension().get(0).getUrl()); + + var ints = new ArrayList(); + for (int i = 0; i < 5; i++) { + ints.add(i); + } + + tuple.getElements().put("V", ints); + tuple.getElements().put("W", null); + tuple.getElements().put("X", 5); + tuple.getElements().put("Y", new Encounter().setId("123")); + tuple.getElements().put("Z", new ArrayList<>()); + + actual = (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(tuple); + var first = actual; + assertEquals(9, first.getPart().size()); + + var v = getPartsByName(first, "V"); + assertEquals(5, v.size()); + assertEquals(0, ((IntegerType) v.get(0).getValue()).getValue()); + + var w = getPartsByName(first, "W").get(0); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + w.getValue().getExtension().get(0).getUrl()); + + var x = getPartsByName(first, "X").get(0); + assertEquals(5, ((IntegerType) x.getValue()).getValue()); + + var y = getPartsByName(first, "Y").get(0); + assertEquals("123", y.getResource().getId()); + + var z = getPartsByName(first, "Z").get(0); + assertEquals( + FhirTypeConverter.EMPTY_LIST_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + } + + @Test + void complexTupleToFhirTuple() { + var innerTuple = new Tuple(); + innerTuple.getElements().put("X", 1); + innerTuple.getElements().put("Y", 2); + innerTuple.getElements().put("Z", null); + var outerTuple = new Tuple(); + outerTuple.getElements().put("A", innerTuple); + var tupleList = new ArrayList(); + for (int i = 0; i < 3; i++) { + var elementTuple = new Tuple(); + elementTuple.getElements().put("P", i); + elementTuple.getElements().put("Q", i + 1); + tupleList.add(elementTuple); + } + outerTuple.getElements().put("B", tupleList); + + Parameters.ParametersParameterComponent actual = + (Parameters.ParametersParameterComponent) typeConverter.toFhirTuple(outerTuple); + var first = actual; + assertEquals(4, first.getPart().size()); + + var a = getPartsByName(first, "A"); + assertEquals(1, a.size()); + assertEquals(3, a.get(0).getPart().size()); + var x = a.get(0).getPart().get(0); + assertEquals(1, ((IntegerType) x.getValue()).getValue()); + var y = a.get(0).getPart().get(1); + assertEquals(2, ((IntegerType) y.getValue()).getValue()); + var z = a.get(0).getPart().get(2); + assertEquals( + FhirTypeConverter.DATA_ABSENT_REASON_EXT_URL, + z.getValue().getExtension().get(0).getUrl()); + + var b = getPartsByName(first, "B"); + assertEquals(3, b.size()); + var b1 = b.get(0); + var p = getPartsByName(b1, "P"); + assertEquals(1, p.size()); + assertEquals(0, ((IntegerType) p.get(0).getValue()).getValue()); + var q = getPartsByName(b1, "Q"); + assertEquals(1, q.size()); + assertEquals(1, ((IntegerType) q.get(0).getValue()).getValue()); } // FHIR-to-CQL diff --git a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateFunctionsTest.xml b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateFunctionsTest.xml index f1489afd8..7298d6b13 100644 --- a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateFunctionsTest.xml +++ b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateFunctionsTest.xml @@ -30,6 +30,10 @@ AllTrue({}) true + + AllTrue(null) + true + @@ -68,6 +72,10 @@ AnyTrue({}) false + + AnyTrue(null) + false + @@ -75,6 +83,12 @@ 3.0 + + + Product({5L, 4L, 5L}) + 100L + + Count({ 15, 5, 99, null, 1 }) @@ -98,6 +112,10 @@ Max({ 5, 12, 1, 15, 0, 4, 90, 44 }) 90 + + Max({ 5L, 12L, 1L, 15L, 0L, 4L, 90L, 44L }) + 90L + Max({ 'hi', 'bye', 'zebra' }) 'zebra' @@ -122,6 +140,10 @@ Min({5, 12, 1, 15, 0, 4, 90, 44}) 0 + + Min({5L, 12L, 1L, 15L, 0L, 4L, 90L, 44L}) + 0L + Min({'hi', 'bye', 'zebra'}) 'bye' @@ -144,6 +166,10 @@ Mode({ DateTime(2012, 10, 5), DateTime(2012, 9, 5), DateTime(2012, 10, 6), DateTime(2012, 9, 5) }) DateTime(2012, 9, 5) + + Mode({ DateTime(2012, 10, 5), DateTime(2012, 10, 5), DateTime(2012, 10, 6), DateTime(2012, 9, 5) }) + DateTime(2012, 10, 5) + Mode({ @T15:59:59.999, @T05:59:59.999, @T20:59:59.999, @T05:59:59.999 }) @T05:59:59.999 @@ -155,12 +181,20 @@ 1.41421356 + + PopulationStdDev({ null as Quantity, null as Quantity, null as Quantity }) + null + PopulationVariance({ 1.0, 2.0, 3.0, 4.0, 5.0 }) 2.0 + + PopulationVariance({ null as Quantity, null as Quantity, null as Quantity }) + null + @@ -168,12 +202,24 @@ 1.58113883 + + StdDev({ null as Quantity, null as Quantity, null as Quantity }) + null + Sum({ 6.0, 2.0, 3.0, 4.0, 5.0 }) 20.0 + + Sum({ 6L, 2L, 3L, 4L, 5L }) + 20L + + + Sum({1 'ml',2 'ml',3 'ml',4 'ml',5 'ml'}) + 15 'ml' + Sum({ null, 1, null }) 1 diff --git a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateTest.xml b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateTest.xml index 2a6ee6738..ac51c750e 100644 --- a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateTest.xml +++ b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlAggregateTest.xml @@ -1,29 +1,88 @@ + name="CqlAggregateTest" reference="http://build.fhir.org/ig/HL7/cql/03-developersguide.html#aggregate-queries"> ({ 1, 2, 3, 4, 5 }) Num aggregate Result starting 1: Result * Num 120 - - ({ - Interval[@2012-01-01, @2012-02-28], - Interval[@2012-02-01, @2012-03-31], - Interval[@2012-03-01, @2012-04-30] -}) M - aggregate R starting (null as List<Interval<DateTime>>): R union ({ - M X - let S: Max({ end of Last(R) + 1 day, start of X }), - E: S + duration in days of X - return Interval[S, E] - }) - { - Interval[@2012-01-01, @2012-02-28], - Interval[@2012-02-29, @2012-04-28], - Interval[@2012-04-29, @2012-06-28] -} - - - + + + ({ + Interval[@2012-01-01, @2012-02-28], + Interval[@2012-02-01, @2012-03-31], + Interval[@2012-03-01, @2012-04-30] + }) M + aggregate R starting (null as List<Interval<DateTime>>): R union ({ + M X + let S: Max({ end of Last(R) + 1 day, start of X }), + E: S + duration in days of X + return Interval[S, E] + }) + + + { + Interval[@2012-01-01, @2012-02-28], + Interval[@2012-02-29, @2012-04-28], + Interval[@2012-04-29, @2012-06-28] + } + + + + + + ({ 1, 2, 3, 4, 5 }) Num + aggregate Result starting 1: Result + Num + + 16 + + + + + ({ 1, 2, 3, 4, 5 }) Num + aggregate Result: Coalesce(Result, 0) + Num + + 15 + + + + + ({ 1, 1, 2, 2, 2, 3, 4, 4, 5 }) Num + aggregate all Result: Coalesce(Result, 0) + Num + + 24 + + + + + ({ 1, 1, 2, 2, 2, 3, 4, 4, 5 }) Num + aggregate distinct Result: Coalesce(Result, 0) + Num + + 15 + + + + + from ({1}) X, ({2}) Y, ({3}) Z + aggregate Agg: Coalesce(Agg, 0) + X + Y + Z + + 6 + + + + from ({1, 2}) X, ({1, 2}) Y, ({1, 2}) Z + aggregate Agg starting 0: Agg + X + Y + Z + + 36 + + + + + from ({1, 2, 2, 1}) X, ({1, 2, 1, 2}) Y, ({2, 1, 2, 1}) Z + aggregate distinct Agg starting 1: Agg + X + Y + Z + + 37 + + + diff --git a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml index a667d780e..e609a4d7e 100644 --- a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml +++ b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml @@ -40,6 +40,10 @@ 1 + 1 2 + + 1L + 2L + 3L + 1.0 + 1.0 2.0 @@ -104,6 +108,10 @@ 1 / 1 1.0 + + 1L / 1L + 1.0 + 1.0 / 1.0 1.0 @@ -195,6 +203,10 @@ Round(Exp(1), 8) 2.71828183 + + Round(Exp(1L), 8) + 2.71828183 + Round(Exp(-1), 8) 0.36787944 @@ -225,6 +237,14 @@ HighBoundary(@T10:30, 9) @T10:30:59.999 + + HighBoundary(null as Decimal, 8) + null + + + HighBoundary(1.58888, null) + 1.58888999 + @@ -251,6 +271,10 @@ Log(1, 100) 0.0 + + Log(1L, 100L) + 0.0 + Log(16, 2) 4.0 @@ -277,6 +301,14 @@ LowBoundary(@T10:30, 9) @T10:30:00.000 + + LowBoundary(null as Decimal, 8) + null + + + LowBoundary(1.58888, null) + 1.58888000 + @@ -295,6 +327,10 @@ Ln(1) 0.0 + + Ln(1L) + 0.0 + Ln(-1) null @@ -404,6 +440,14 @@ 3.5 'cm' mod 3 'cm' 0.5 'cm' + + 10.0 'g' mod 3.0 'g' + 1.0 'g' + + + 10.0 'g' mod 0.0 'g' + null + @@ -414,6 +458,10 @@ 1 * 1 1 + + 2L * 3L + 6L + 1.0 * 2.0 2.0 @@ -449,6 +497,14 @@ -1 -1 + + -1L + -1L + + + -9223372036854775807L + -9223372036854775807L + -(-1) 1 @@ -595,6 +651,10 @@ 2^4 16 + + 2L^3L + 8L + 2.0^4.0 16.0 @@ -833,5 +893,17 @@ 10.1 'cm' div -3.1 'cm' -3.0 'cm' + + 10.0 'g' div 5.0 'g' + 2.0 'g' + + + 4.14 'm' div 2.06 'm' + 2.0 'm' + + + 10.0 'g' div 0.0 'g' + null + diff --git a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlComparisonOperatorsTest.xml b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlComparisonOperatorsTest.xml index 8f73b6fb5..929691c55 100644 --- a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlComparisonOperatorsTest.xml +++ b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlComparisonOperatorsTest.xml @@ -44,6 +44,10 @@ 1 = 2 false + + 10L = 20L + false + 'a' = 'a' true @@ -60,6 +64,14 @@ 1.0 = 2.0 false + + 1.0 = 1.00 + true + + + 1.50 = 1.55 + false + 1.0 = 1 true @@ -80,10 +92,26 @@ 2.0'cm' = 2.00'cm' true + + 1'cm':2'cm' = 1'cm':2'cm' + true + + + 1'cm':2'cm' = 1.1'cm':2'cm' + false + Tuple { Id : 1, Name : 'John' } = Tuple { Id : 1, Name : 'John' } true + + Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } = Tuple { Id : 1, Name : 'John' } + + + + Tuple { Id : 1, Name : 'John' } = Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } + + Tuple { Id : 1, Name : 'John' } = Tuple { Id : 2, Name : 'Jane' } false @@ -100,6 +128,10 @@ Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 0, 0, 0, 0) } = Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 5, 0, 0, 0) } false + + Tuple { dateId: 12, Date: DateTime(2012, 1, 1) } = Tuple { dateId: 12, Date: DateTime(2012, 1, 1) } + true + Tuple { timeId: 55, TheTime: @T05:15:15.541 } = Tuple { timeId: 55, TheTime: @T05:15:15.541 } true @@ -124,10 +156,18 @@ DateTime(2014, 1, 5, 5, 0, 0, 0, 0) = DateTime(2014, 7, 5, 5, 0, 0, 0, 0) false + + DateTime(2015, 1, 5, 5, 0, 0) = DateTime(2015, 1, 5, 5, 0, 0) + true + DateTime(null) = DateTime(null) null + + DateTime(2001, 1, 1, null) = DateTime(2001, 1, 1, null, null) + true + @2014-01-25T14:30:14.559+01:00 = @2014-01-25T14:30:14.559+01:00 true @@ -154,6 +194,10 @@ 0 > 1 false + + 00L > 10L + false + 0 > -1 true @@ -256,6 +300,10 @@ 0 >= 1 false + + 00L >= 10L + false + 0 >= -1 true @@ -366,6 +414,14 @@ 0 < 1 true + + 00L < 10L + true + + + -30L < -20L + true + 0 < -1 false @@ -468,6 +524,10 @@ 0 <= 1 true + + 00L <= 10L + true + 0 <= -1 false @@ -614,6 +674,10 @@ 'a' ~ 'b' false + + 'Abel' ~ 'abel' + true + 1.0 ~ 1.0 true @@ -622,6 +686,22 @@ 1.0 ~ 2.0 false + + 1.0 ~ 1.00 + true + + + 1.5 ~ 1.55 + true + + + 1.50 ~ 1.55 + true + + + 1.001 ~ 1.000 + true + 1.0 ~ 1 true @@ -638,6 +718,14 @@ 1'cm' ~ 0.01'm' true + + 1'cm':2'cm' ~ 1'cm':2'cm' + true + + + 1'cm':2'cm' ~ 1'cm':3'cm' + false + Tuple { Id : 1, Name : 'John' } ~ Tuple { Id : 1, Name : 'John' } true @@ -646,6 +734,14 @@ Tuple { Id : 1, Name : 'John', Position: null } ~ Tuple { Id : 1, Name : 'John', Position: null } true + + Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } ~ Tuple { Id : 1, Name : 'John' } + + + + Tuple { Id : 1, Name : 'John' } ~ Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } + + Tuple { Id : 1, Name : 'John' } ~ Tuple { Id : 2, Name : 'Jane' } false @@ -708,6 +804,10 @@ 1 != 2 true + + 10L != 20L + true + 'a' != 'a' false diff --git a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlDateTimeOperatorsTest.xml b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlDateTimeOperatorsTest.xml index 20fccccfc..0d1f016e8 100644 --- a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlDateTimeOperatorsTest.xml +++ b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlDateTimeOperatorsTest.xml @@ -18,6 +18,14 @@ DateTime(2005, 5, 10) + 10 months @2006-03-10T + + DateTime(2018, 5, 2) + 3 weeks = DateTime(2018, 5, 23) + true + + + DateTime(2018, 5, 23) + 52 weeks = DateTime(2019, 5, 23) + true + DateTime(2005, 5, 10) + 5 days @2005-05-15T @@ -30,6 +38,34 @@ DateTime(2005, 5, 10, 5) + 5 hours @2005-05-10T10 + + DateTime(2005, 5, 10, 5, 20, 30) + 5 hours + @2005-05-10T10:20:30 + + + DateTime(2005, 5, 10) + 5 hours = DateTime(2005, 5, 10) + true + + + DateTime(2005, 5, 10) + 25 hours = DateTime(2005, 5, 11) + true + + + Date(2014) + 24 months + @2016 + + + Date(2014) + 25 months + @2016 + + + Date(2014,6) + 33 days + @2014-07 + + + Date(2014,6) + 1 year + @2015-06 + DateTime(2016, 6, 10, 5) + 19 hours @2016-06-11T00 @@ -377,9 +413,12 @@ 955 - timezone from DateTime(2003, 10, 29, 20, 50, 33, 955, 1) + timezone from DateTime(2003, 10, 29, 20, 50, 33, 955, 1) + + + + timezoneoffset from DateTime(2003, 10, 29, 20, 50, 33, 955, 1) 1.00 - date from DateTime(2003, 10, 29, 20, 50, 33, 955, 1) @@ -670,9 +709,12 @@ 2 - hours between @T06Z and @T07:00:00Z + hours between @T06Z and @T07:00:00Z + + + + hours between @T06 and @T07:00:00 1 - minutes between @T23:20:16.555 and @T23:25:15.555 @@ -1141,6 +1183,14 @@ DateTime(2005, 5, 10) - 6 months @2004-11-10T + + DateTime(2018, 5, 23) - 3 weeks = DateTime(2018, 5, 2) + true + + + DateTime(2018, 5, 23) - 52 weeks = DateTime(2017, 5, 23) + true + DateTime(2005, 5, 10) - 5 days @2005-05-05T @@ -1169,6 +1219,14 @@ DateTime(2005, 5, 10, 5, 5, 10) - 5 seconds @2005-05-10T05:05:05 + + DateTime(2016,5) - 31535999 seconds = DateTime(2015, 5) + true + + + DateTime(2016, 10, 1, 10, 20, 30) - 15 hours + @2016-09-30T19:20:30 + DateTime(2016, 6, 10, 5, 5, 5) - 6 seconds @2016-06-10T05:04:59 @@ -1189,6 +1247,22 @@ DateTime(2014) - 25 months @2012T + + Date(2014) - 24 months + @2012 + + + Date(2014) - 25 months + @2012 + + + Date(2014,6) - 33 days + @2014-05 + + + Date(2014,6) - 1 year + @2013-06 + @T15:59:59.999 - 5 hours @T10:59:59.999 diff --git a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlListOperatorsTest.xml b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlListOperatorsTest.xml index 9b577abf4..6f4092fbe 100644 --- a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlListOperatorsTest.xml +++ b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlListOperatorsTest.xml @@ -147,6 +147,29 @@ {} = {} true + + { 'a', 'b', 'c' } = { 'a', 'b', 'c' } + true + + + { 'a', 'b', 'c' } = { 'a', 'b' } + false + + + { 'a', 'b', 'c' } = { 1, 2, 3 } + false + + + + { 1, 2, 3 } = { 'a', 'b', 'c' } + false + + + + { 1, 2, 3 } = { '1', '2', '3' } + false + + { 1, 2 } = { 1, 2, 3 } false diff --git a/Src/java/engine/build.gradle b/Src/java/engine/build.gradle index 03871059e..03212da9a 100644 --- a/Src/java/engine/build.gradle +++ b/Src/java/engine/build.gradle @@ -12,15 +12,14 @@ dependencies { } jacocoTestReport { - dependsOn ':cql-to-elm:test' - dependsOn ':engine:test' - sourceDirectories.setFrom(files( + "${projectDir}/../elm/src/main/java", "${projectDir}/../cql-to-elm/src/main/java", "${projectDir}/../engine/src/main/java", )) classDirectories.setFrom(files( + "${projectDir}/../elm/build/classes/java/main", "${projectDir}/../cql-to-elm/build/classes/java/main", "${projectDir}/../engine/build/classes/java/main", )) diff --git a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/executing/FunctionRefEvaluator.java b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/executing/FunctionRefEvaluator.java index afca5d3ec..3708d7e0d 100644 --- a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/executing/FunctionRefEvaluator.java +++ b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/executing/FunctionRefEvaluator.java @@ -3,11 +3,10 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.cqframework.cql.elm.evaluating.SimpleElmEvaluator; import org.cqframework.cql.elm.visiting.ElmLibraryVisitor; -import org.hl7.elm.r1.Expression; -import org.hl7.elm.r1.FunctionDef; -import org.hl7.elm.r1.FunctionRef; -import org.hl7.elm.r1.TypeSpecifier; +import org.hl7.elm.r1.*; import org.opencds.cqf.cql.engine.exception.CqlException; import org.opencds.cqf.cql.engine.execution.Libraries; import org.opencds.cqf.cql.engine.execution.State; @@ -65,7 +64,7 @@ protected static FunctionDef resolveOrCacheFunctionDef( } } - FunctionDef functionDef = resolveFunctionDef(state, functionRef, arguments); + FunctionDef functionDef = resolveFunctionRef(state, functionRef, arguments); if (eligibleForCaching) { state.getCache().getFunctionCache().put(functionRef, functionDef); @@ -74,58 +73,97 @@ protected static FunctionDef resolveOrCacheFunctionDef( return functionDef; } - protected static FunctionDef resolveFunctionDef(State state, FunctionRef functionRef, ArrayList arguments) { - return resolveFunctionRef(state, functionRef.getName(), arguments, functionRef.getSignature()); - } + protected static FunctionDef resolveFunctionRef(State state, FunctionRef functionRef, List arguments) { + var name = functionRef.getName(); + var signature = functionRef.getSignature(); - public static FunctionDef resolveFunctionRef( - State state, final String name, final List arguments, final List signature) { - FunctionDef ret; + var functionDefs = resolveFunctionRef(state, name, arguments, signature); - final List types = signature.isEmpty() ? arguments : signature; + return pickFunctionDef(state, name, arguments, signature, functionDefs); + } - ret = getResolvedFunctionDef(state, name, types, !signature.isEmpty()); + static List resolveFunctionRef( + State state, String name, List arguments, List signature) { + var namedDefs = Libraries.getFunctionDefs(name, state.getCurrentLibrary()); - if (ret != null) { - return ret; + // If the function ref includes a signature, use the signature to find the matching function defs + if (!signature.isEmpty()) { + return namedDefs.stream() + .filter(x -> functionDefOperandsSignatureEqual(x, signature)) + .collect(Collectors.toList()); } - throw new CqlException(String.format( - "Could not resolve call to operator '%s(%s)' in library '%s'.", - name, - getUnresolvedMessage(state, types, name), - state.getCurrentLibrary().getIdentifier().getId())); + logger.debug( + "Using runtime function resolution for '{}'. It's recommended to always include signatures in ELM", + name); + + return namedDefs.stream() + .filter(x -> state.getEnvironment().matchesTypes(x, arguments)) + .collect(Collectors.toList()); } - private static FunctionDef getResolvedFunctionDef( - State state, final String name, final List types, final boolean hasSignature) { - var namedDefs = Libraries.getFunctionDefs(name, state.getCurrentLibrary()); + static boolean functionDefOperandsSignatureEqual(FunctionDef functionDef, List signature) { + var operands = functionDef.getOperand(); - var candidateDefs = namedDefs.stream() - .filter(x -> x.getOperand().size() == types.size()) - .collect(Collectors.toList()); + // Check if the number of operands match and if the type specifiers match + return operands.size() == signature.size() + && IntStream.range(0, operands.size()) + .allMatch(i -> operandDefTypeSpecifierEqual(operands.get(i), signature.get(i))); + } - if (candidateDefs.size() == 1) { - return candidateDefs.get(0); + static boolean operandDefTypeSpecifierEqual(OperandDef operandDef, TypeSpecifier typeSpecifier) { + // An operand def can have an operandTypeSpecifier or operandType + + var operandDefOperandTypeSpecifier = operandDef.getOperandTypeSpecifier(); + if (operandDefOperandTypeSpecifier != null) { + return SimpleElmEvaluator.typeSpecifiersEqual(operandDefOperandTypeSpecifier, typeSpecifier); } - if (candidateDefs.size() > 1 && !hasSignature) { - logger.debug( - "Using runtime function resolution for '{}'. It's recommended to always include signatures in ELM", - name); + if (typeSpecifier instanceof NamedTypeSpecifier) { + return SimpleElmEvaluator.qnamesEqual( + operandDef.getOperandType(), ((NamedTypeSpecifier) typeSpecifier).getName()); } - return candidateDefs.stream() - .filter(x -> state.getEnvironment().matchesTypes(x, types)) - .findFirst() - .orElse(null); + return false; } - private static String getUnresolvedMessage(State state, List arguments, String name) { + static FunctionDef pickFunctionDef( + State state, + String name, + List arguments, + List signature, + List functionDefs) { + var types = signature.isEmpty() ? arguments : signature; + + if (functionDefs.isEmpty()) { + throw new CqlException(String.format( + "Could not resolve call to operator '%s(%s)' in library '%s'.", + name, + typesToString(state, types), + state.getCurrentLibrary().getIdentifier().getId())); + } + + if (functionDefs.size() == 1) { + // Normal case + return functionDefs.get(0); + } + + throw new CqlException(String.format( + "Ambiguous call to operator '%s(%s)' in library '%s'.", + name, + typesToString(state, types), + state.getCurrentLibrary().getIdentifier().getId())); + } + + static String typesToString(State state, List arguments) { StringBuilder argStr = new StringBuilder(); if (arguments != null) { - arguments.forEach(a -> argStr.append((argStr.length() > 0) ? ", " : "") - .append(state.getEnvironment().resolveType(a).getTypeName())); + arguments.forEach(a -> { + argStr.append((argStr.length() > 0) ? ", " : ""); + + Class type = state.getEnvironment().resolveType(a); + argStr.append(type == null ? "null" : type.getTypeName()); + }); } return argStr.toString(); diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/elm/executing/FunctionRefEvaluatorTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/elm/executing/FunctionRefEvaluatorTest.java new file mode 100644 index 000000000..177445e00 --- /dev/null +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/elm/executing/FunctionRefEvaluatorTest.java @@ -0,0 +1,66 @@ +package org.opencds.cqf.cql.engine.elm.executing; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; +import javax.xml.namespace.QName; +import org.hl7.elm.r1.*; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.cql.engine.exception.CqlException; +import org.opencds.cqf.cql.engine.execution.Environment; +import org.opencds.cqf.cql.engine.execution.State; + +class FunctionRefEvaluatorTest { + @Test + void pickFunctionDef() { + var env = new Environment(null); + var state = new State(env); + state.init(new Library().withIdentifier(new VersionedIdentifier().withId("lib"))); + + var cqlException = assertThrows( + CqlException.class, + () -> FunctionRefEvaluator.pickFunctionDef(state, "func", List.of(1, 2, 3), List.of(), List.of())); + assertEquals( + "Could not resolve call to operator 'func(java.lang.Integer, java.lang.Integer, java.lang.Integer)' in library 'lib'.", + cqlException.getMessage()); + } + + @Test + void functionDefOperandsSignatureEqual() { + var functionDefWithOneOperand = new FunctionDef().withOperand(new OperandDef()); + List signatureWithTwoOperands = List.of(new NamedTypeSpecifier(), new NamedTypeSpecifier()); + + assertFalse(FunctionRefEvaluator.functionDefOperandsSignatureEqual( + functionDefWithOneOperand, signatureWithTwoOperands)); + } + + @Test + void operandDefTypeSpecifierEqual() { + var integerTypeName = new QName("urn:hl7-org:elm-types:r1", "Integer"); + var integerNamedTypeSpecifier = new NamedTypeSpecifier().withName(integerTypeName); + var listTypeSpecifier = new ListTypeSpecifier().withElementType(integerNamedTypeSpecifier); + + var listOperandDef = new OperandDef().withOperandTypeSpecifier(listTypeSpecifier); + var integerOperandDef = new OperandDef().withOperandType(integerTypeName); + + assertTrue(FunctionRefEvaluator.operandDefTypeSpecifierEqual(listOperandDef, listTypeSpecifier)); + assertTrue(FunctionRefEvaluator.operandDefTypeSpecifierEqual(integerOperandDef, integerNamedTypeSpecifier)); + assertFalse(FunctionRefEvaluator.operandDefTypeSpecifierEqual(integerOperandDef, null)); + } + + @Test + void typesToString() { + var env = new Environment(null); + var state = new State(env); + + var actual = FunctionRefEvaluator.typesToString(state, List.of("a", "b", "c")); + assertEquals("java.lang.String, java.lang.String, java.lang.String", actual); + + actual = FunctionRefEvaluator.typesToString(state, Arrays.asList(1, 2, null)); + assertEquals("java.lang.Integer, java.lang.Integer, null", actual); + + actual = FunctionRefEvaluator.typesToString(state, null); + assertEquals("", actual); + } +} diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlAggregateFunctionsTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlAggregateFunctionsTest.java index 233a60b32..0b8156688 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlAggregateFunctionsTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlAggregateFunctionsTest.java @@ -1,81 +1,18 @@ package org.opencds.cqf.cql.engine.execution; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.*; -import java.math.BigDecimal; import java.util.Arrays; import org.junit.jupiter.api.Test; import org.opencds.cqf.cql.engine.elm.executing.AnyTrueEvaluator; import org.opencds.cqf.cql.engine.elm.executing.AvgEvaluator; -import org.opencds.cqf.cql.engine.elm.executing.EquivalentEvaluator; import org.opencds.cqf.cql.engine.exception.InvalidOperatorArgument; -import org.opencds.cqf.cql.engine.runtime.DateTime; -import org.opencds.cqf.cql.engine.runtime.Quantity; -import org.opencds.cqf.cql.engine.runtime.Time; class CqlAggregateFunctionsTest extends CqlTestBase { @Test void all_aggregate_function_tests() { - final BigDecimal bigDecimalZoneOffset = getBigDecimalZoneOffset(); - - var results = engine.evaluate(toElmIdentifier("CqlAggregateFunctionsTest")); - Object value = results.forExpression("AllTrueAllTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("AllTrueTrueFirst").value(); - assertThat(value, is(false)); - - value = results.forExpression("AllTrueFalseFirst").value(); - assertThat(value, is(false)); - - value = results.forExpression("AllTrueAllTrueFalseTrue").value(); - assertThat(value, is(false)); - - value = results.forExpression("AllTrueAllFalseTrueFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("AllTrueNullFirst").value(); - assertThat(value, is(true)); - - value = results.forExpression("AllTrueEmptyList").value(); - assertThat(value, is(true)); - - value = results.forExpression("AllTrueIsTrueWhenNull").value(); - assertThat(value, is(true)); - - value = results.forExpression("AnyTrueAllTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("AnyTrueAllFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("AnyTrueAllTrueFalseTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("AnyTrueAllFalseTrueFalse").value(); - assertThat(value, is(true)); - - value = results.forExpression("AnyTrueTrueFirst").value(); - assertThat(value, is(true)); - - value = results.forExpression("AnyTrueFalseFirst").value(); - assertThat(value, is(true)); - - value = results.forExpression("AnyTrueNullFirstThenTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("AnyTrueNullFirstThenFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("AnyTrueEmptyList").value(); - assertThat(value, is(false)); - - value = results.forExpression("AnyTrueIsFalseWhenNull").value(); - assertThat(value, is(false)); + Object value; try { value = AnyTrueEvaluator.anyTrue(Arrays.asList("this", "is", "error")); @@ -90,107 +27,5 @@ void all_aggregate_function_tests() { } catch (InvalidOperatorArgument e) { // pass } - - value = results.forExpression("AvgTest1").value(); - assertThat(value, is(new BigDecimal("3.0"))); - - value = results.expressionResults.get("Product_Long").value(); - assertThat(value, is(100L)); - - value = results.forExpression("CountTest1").value(); - assertThat(value, is(4)); - - value = results.forExpression("CountTestDateTime").value(); - assertThat(value, is(3)); - - value = results.forExpression("CountTestTime").value(); - assertThat(value, is(3)); - - value = results.forExpression("CountTestNull").value(); - assertThat(value, is(0)); - - value = results.forExpression("MaxTestInteger").value(); - assertThat(value, is(90)); - - value = results.forExpression("MaxTestLong").value(); - assertThat(value, is(90L)); - - value = results.forExpression("MaxTestString").value(); - assertThat(value, is("zebra")); - - value = results.forExpression("MaxTestDateTime").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2012, 10, 6))); - - value = results.forExpression("MaxTestTime").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(20, 59, 59, 999))); - - value = results.forExpression("MedianTestDecimal").value(); - assertThat(value, is(new BigDecimal("3.5"))); - - value = results.forExpression("MinTestInteger").value(); - assertThat(value, is(0)); - - value = results.forExpression("MinTestLong").value(); - assertThat(value, is(0L)); - - value = results.forExpression("MinTestString").value(); - assertThat(value, is("bye")); - - value = results.forExpression("MinTestDateTime").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2012, 9, 5))); - - value = results.forExpression("MinTestTime").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(5, 59, 59, 999))); - - value = results.forExpression("ModeTestInteger").value(); - assertThat(value, is(9)); - value = results.forExpression("ModeTestDateTime2").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2012, 10, 5))); - - value = results.forExpression("ModeTestTime").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(5, 59, 59, 999))); - - value = results.forExpression("ModeTestDateTime").value(); - assertTrue(((DateTime) value).equal(new DateTime(bigDecimalZoneOffset, 2012, 9, 5))); - - value = results.forExpression("PopStdDevTest1").value(); - assertEquals( - 0, - ((BigDecimal) value) - .compareTo(new BigDecimal("1.41421356"))); // 23730951454746218587388284504413604736328125 - - value = results.forExpression("PopulationStdDevIsNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("PopVarianceTest1").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("2.0"))); - - value = results.forExpression("PopVarianceIsNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("StdDevTest1").value(); - assertEquals( - 0, - ((BigDecimal) value) - .compareTo(new BigDecimal("1.58113883"))); // 00841897613935316257993690669536590576171875 - - value = results.forExpression("StdDevIsNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("SumTest1").value(); - assertThat(value, is(new BigDecimal("20.0"))); - - value = results.forExpression("SumTestLong").value(); - assertThat(value, is(20L)); - - value = results.forExpression("SumTestQuantity").value(); - assertTrue(((Quantity) value) - .equal(new Quantity().withValue(new BigDecimal("15.0")).withUnit("ml"))); - - value = results.forExpression("SumTestNull").value(); - assertThat(value, is(1)); - - value = results.forExpression("VarianceTest1").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("2.5"))); } } diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlAggregateQueryTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlAggregateQueryTest.java deleted file mode 100644 index f1cc2b6f9..000000000 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlAggregateQueryTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.opencds.cqf.cql.engine.execution; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -import org.junit.jupiter.api.Test; - -class CqlAggregateQueryTest extends CqlTestBase { - @Test - void all_aggregate_clause_tests() { - var results = engine.evaluate(toElmIdentifier("CqlAggregateQueryTest")); - var value = results.forExpression("AggregateSumWithStart").value(); - assertThat(value, is(16)); - - value = results.forExpression("AggregateSumWithNull").value(); - assertThat(value, is(15)); - - value = results.forExpression("AggregateSumAll").value(); - assertThat(value, is(24)); - - value = results.forExpression("AggregateSumDistinct").value(); - assertThat(value, is(15)); - - value = results.forExpression("Multi").value(); - assertThat(value, is(6)); - - value = results.forExpression("MegaMulti").value(); - assertThat(value, is(36)); - - value = results.forExpression("MegaMultiDistinct").value(); - assertThat(value, is(37)); - } -} diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java index e156bc907..5d16c58c0 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java @@ -1,53 +1,20 @@ package org.opencds.cqf.cql.engine.execution; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; -import java.math.BigDecimal; -import org.hl7.elm.r1.VersionedIdentifier; import org.junit.jupiter.api.Test; import org.opencds.cqf.cql.engine.elm.executing.*; import org.opencds.cqf.cql.engine.exception.CqlException; -import org.opencds.cqf.cql.engine.exception.UndefinedResult; -import org.opencds.cqf.cql.engine.runtime.*; @SuppressWarnings("removal") class CqlArithmeticFunctionsTest extends CqlTestBase { - private static final VersionedIdentifier library = new VersionedIdentifier().withId("CqlArithmeticFunctionsTest"); - @Test void abs() { - var value = engine.expression(library, "AbsNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Abs0").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "AbsNeg1").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "AbsNeg1Long").value(); - assertThat(value, is(1L)); - - value = engine.expression(library, "AbsNeg1Dec").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(1.0))); - - value = engine.expression(library, "Abs0Dec").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0.0))); - - value = engine.expression(library, "Abs1cm").value(); - assertEquals( - 0, - ((Quantity) value) - .compareTo( - new Quantity().withValue(new BigDecimal("1.0")).withUnit("cm"))); - // error testing try { - value = AbsEvaluator.abs("This is an error"); + var value = AbsEvaluator.abs("This is an error"); fail(); } catch (CqlException e) { // pass @@ -60,787 +27,12 @@ void abs() { @Test void add() { - var value = engine.expression(library, "AddNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Add11").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "Add12Long").value(); - assertThat(value, is(3L)); - - value = engine.expression(library, "Add1D1D").value(); - assertThat(value, is(new BigDecimal("2.0"))); - - value = engine.expression(library, "Add1Q1Q").value(); - assertEquals(((Quantity) value).getValue(), new BigDecimal("2")); - assertEquals("g/cm3", ((Quantity) value).getUnit()); - - value = engine.expression(library, "AddIAndD").value(); - assertThat(value, is(new BigDecimal("3.0"))); - // error testing try { - value = AddEvaluator.add("This is an error", 404); + var value = AddEvaluator.add("This is an error", 404); fail(); } catch (CqlException e) { // pass } } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.CeilingEvaluator#evaluate(Context)} - */ - @Test - void ceiling() { - - var value = engine.expression(library, "CeilingNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Ceiling1D").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "Ceiling1D1").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "CeilingNegD1").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "CeilingNeg1").value(); - assertThat(value, is(-1)); - - value = engine.expression(library, "CeilingNeg1D1").value(); - assertThat(value, is(-1)); - - value = engine.expression(library, "Ceiling1I").value(); - assertThat(value, is(1)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.DivideEvaluator#evaluate(Context)} - */ - @Test - void divide() { - - var value = engine.expression(library, "DivideNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Divide10").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Divide01").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("0.0"))); - - value = engine.expression(library, "Divide11").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("1.0"))); - - value = engine.expression(library, "Divide11Long").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("1.0"))); - - value = engine.expression(library, "Divide1d1d").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("1.0"))); - - value = engine.expression(library, "Divide103").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("3.33333333"))); - - value = engine.expression(library, "Divide1Q1").value(); - assertEquals(((Quantity) value).getValue(), new BigDecimal("1")); - assertEquals("g/cm3", ((Quantity) value).getUnit()); - - // TODO: The asserted "correct" answer 1.0'g/cm3' is wrong; - // the true correct answer is just 1.0 with no units or empty string unit. - // value = engine.expression(arithmetic, "Divide1Q1Q").value(); - // Assertions.assertEquals(((Quantity) value).getValue(), new BigDecimal("1.0")); - // Assertions.assertEquals("g/cm3", ((Quantity) value).getUnit()); - - value = engine.expression(library, "Divide10I5D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("2.0"))); - - value = engine.expression(library, "Divide10I5I").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("2.0"))); - - value = engine.expression(library, "Divide10Q5I").value(); - assertEquals(new BigDecimal("2.0"), ((Quantity) value).getValue()); - assertEquals("g", ((Quantity) value).getUnit()); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.FloorEvaluator#evaluate(Context)} - */ - @Test - void floor() { - - var value = engine.expression(library, "FloorNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Floor1").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "Floor1D").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "Floor1D1").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "FloorNegD1").value(); - assertThat(value, is(-1)); - - value = engine.expression(library, "FloorNeg1").value(); - assertThat(value, is(-1)); - - value = engine.expression(library, "FloorNeg1D1").value(); - assertThat(value, is(-2)); - - value = engine.expression(library, "Floor2I").value(); - assertThat(value, is(2)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.ExpEvaluator#evaluate(Context)} - */ - @Test - void exp() { - - var value = engine.expression(library, "ExpNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Exp0").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(1.0))); - - value = engine.expression(library, "ExpNeg0").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(1.0))); - - value = engine.expression(library, "Exp1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(Math.exp(1d)))); - - value = engine.expression(library, "Exp1Long").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(Math.exp(1d)))); - - value = engine.expression(library, "ExpNeg1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(Math.exp((double) -1)))); - - try { - value = engine.expression(library, "Exp1000").value(); - fail(); - } catch (UndefinedResult ae) { - assertThat(ae.getMessage(), is("Results in positive infinity")); - } - - try { - value = engine.expression(library, "Exp1000D").value(); - fail(); - } catch (UndefinedResult ae) { - assertThat(ae.getMessage(), is("Results in positive infinity")); - } - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.HighBoundaryEvaluator#evaluate(Context)} - */ - @Test - void highBoundary() { - - var value = engine.expression(library, "HighBoundaryDec").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("1.58799999"))); - - value = engine.expression(library, "HighBoundaryDate").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2014, 12))); - - value = engine.expression(library, "HighBoundaryDateTime").value(); - final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 2014, 1, 1, 8, 59, 59, 999); - assertTrue(EquivalentEvaluator.equivalent(value, expectedDateTime)); - - value = engine.expression(library, "HighBoundaryTime").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(10, 30, 59, 999))); - - value = engine.expression(library, "HighBoundaryNull").value(); - assertNull(value); - - value = engine.expression(library, "HighBoundaryNullPrecision").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("1.58888999"))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.LogEvaluator#evaluate(Context)} - */ - @Test - void log() { - var value = engine.expression(library, "LogNullNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Log1BaseNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Log1Base1").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Log1Base2").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0d))); - - value = engine.expression(library, "Log1Base100").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0d))); - - value = engine.expression(library, "Log1Base100Long").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0d))); - - value = engine.expression(library, "Log16Base2").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(4d))); - - value = engine.expression(library, "LogD125Base2").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf((double) -3))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.LnEvaluator#evaluate(Context)} - */ - @Test - void ln() { - var value = engine.expression(library, "LnNull").value(); - assertThat(value, is(nullValue())); - - try { - value = engine.expression(library, "Ln0").value(); - fail(); - } catch (UndefinedResult ae) { - assertThat(ae.getMessage(), is("Results in negative infinity")); - } - - try { - value = engine.expression(library, "LnNeg0").value(); - fail(); - } catch (UndefinedResult ae) { - assertThat(ae.getMessage(), is("Results in negative infinity")); - } - - value = engine.expression(library, "Ln1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0))); - - value = engine.expression(library, "Ln1Long").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0))); - - value = engine.expression(library, "LnNeg1").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Ln1000").value(); - assertThat((BigDecimal) value, comparesEqualTo(Value.verifyPrecision(new BigDecimal("6.90775527"), null))); - - value = engine.expression(library, "Ln1000D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("6.90775527"))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.LowBoundaryEvaluator#evaluate(Context)} - */ - @Test - void lowBoundary() { - - var value = engine.expression(library, "LowBoundaryDec").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("1.58700000"))); - - value = engine.expression(library, "LowBoundaryDate").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2014, 1))); - - value = engine.expression(library, "LowBoundaryDateTime").value(); - final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 2014, 1, 1, 8, 0, 0, 0); - assertTrue(EquivalentEvaluator.equivalent(value, expectedDateTime)); - - value = engine.expression(library, "LowBoundaryTime").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(10, 30, 0, 0))); - - value = engine.expression(library, "LowBoundaryNull").value(); - assertNull(value); - - value = engine.expression(library, "LowBoundaryNullPrecision").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("1.58888000"))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.MaxEvaluator#evaluate(Context)} - */ - @Test - void maximum() { - - var value = engine.expression(library, "IntegerMaxValue").value(); - assertThat(value, is(Integer.MAX_VALUE)); - - value = engine.expression(library, "LongMaxValue").value(); - assertThat(value, is(Long.MAX_VALUE)); - - value = engine.expression(library, "DecimalMaxValue").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("99999999999999999999.99999999"))); - - value = engine.expression(library, "DateTimeMaxValue").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(BigDecimal.ZERO, 9999, 12, 31, 23, 59, 59, 999))); - - value = engine.expression(library, "TimeMaxValue").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(23, 59, 59, 999))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.MinEvaluator#evaluate(Context)} - */ - @Test - void minimum() { - - var value = engine.expression(library, "IntegerMinValue").value(); - assertThat(value, is(Integer.MIN_VALUE)); - - value = engine.expression(library, "LongMinValue").value(); - assertThat(value, is(Long.MIN_VALUE)); - - value = engine.expression(library, "DecimalMinValue").value(); - assertEquals(0, ((BigDecimal) value).compareTo(new BigDecimal("-99999999999999999999.99999999"))); - - value = engine.expression(library, "DateTimeMinValue").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(BigDecimal.ZERO, 1, 1, 1, 0, 0, 0, 0))); - - value = engine.expression(library, "TimeMinValue").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(0, 0, 0, 0))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.ModuloEvaluator#evaluate(Context)} - */ - @Test - void modulo() { - - var value = engine.expression(library, "ModuloNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Modulo0By0").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Modulo4By2").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "Modulo4By2Long").value(); - assertThat(value, is(0L)); - - value = engine.expression(library, "Modulo4DBy2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0d))); - - value = engine.expression(library, "Modulo10By3").value(); - assertThat(value, is((1))); - - value = engine.expression(library, "Modulo10DBy3D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(1.0))); - - value = engine.expression(library, "Modulo10IBy3D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(1.0))); - - value = engine.expression(library, "ModuloDResult").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0.5))); - - value = engine.expression(library, "Modulo10By3Quantity").value(); - assertThat(((Quantity) value).getValue(), comparesEqualTo(BigDecimal.valueOf(1.0))); - assertThat(((Quantity) value).getUnit(), is("g")); - - value = engine.expression(library, "Modulo10By0Quantity").value(); - assertThat(value, is(nullValue())); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.MultiplyEvaluator#evaluate(Context)} - */ - @Test - void multiply() { - - var value = engine.expression(library, "MultiplyNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Multiply1By1").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "Multiply2By3Long").value(); - assertThat(value, is(6L)); - - value = engine.expression(library, "Multiply1DBy2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(2.0))); - - value = engine.expression(library, "Multiply1IBy2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(2.0))); - - // TODO: should return multiplied units i.e. cm2 - // value = engine.expression(arithmetic, "Multiply1CMBy2CM").value(); - // Assertions.assertTrue(new BigDecimal("2.0").compareTo(((Quantity) value).getValue()) == 0); - // Assertions.assertEquals("cm", ((Quantity) value).getUnit()); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.NegateEvaluator#evaluate(Context)} - */ - @Test - void negate() { - var value = engine.expression(library, "NegateNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Negate0").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "NegateNeg0").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "Negate1").value(); - assertThat(value, is(-1)); - - value = engine.expression(library, "Negate1Long").value(); - assertThat(value, is(-1L)); - - value = engine.expression(library, "NegateMaxLong").value(); - assertThat(value, is(-9223372036854775808L)); - - value = engine.expression(library, "NegateNeg1").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "Negate0D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-(0d)))); - - value = engine.expression(library, "NegateNeg0D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0d))); - - value = engine.expression(library, "Negate1D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf((double) -1))); - - value = engine.expression(library, "NegateNeg1D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("1.0"))); - - value = engine.expression(library, "Negate1CM").value(); - assertEquals(0, new BigDecimal("-1.0").compareTo(((Quantity) value).getValue())); - assertEquals("cm", ((Quantity) value).getUnit()); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.PredecessorEvaluator#evaluate(Context)} - */ - @Test - void predecessor() { - var value = engine.expression(library, "PredecessorNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "PredecessorOf0").value(); - assertThat(value, is(-1)); - - value = engine.expression(library, "PredecessorOf1").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "PredecessorOf1Long").value(); - assertThat(value, is(0L)); - - value = engine.expression(library, "PredecessorOf1D").value(); - assertThat((BigDecimal) value, comparesEqualTo((new BigDecimal("0.99999999")))); - - value = engine.expression(library, "PredecessorOf101D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("1.00999999"))); - - // value = engine.expression(arithmetic, "PredecessorOf1QCM").value(); - // Assertions.assertTrue(new BigDecimal("0.99999999").compareTo(((Quantity) value).getValue()) == 0); - // Assertions.assertEquals("cm", ((Quantity) value).getUnit()); - - value = engine.expression(library, "PredecessorOfJan12000").value(); - final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 1999, 12, 31); - assertTrue(EquivalentEvaluator.equivalent(value, expectedDateTime)); - - value = engine.expression(library, "PredecessorOfNoon").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(11, 59, 59, 999))); - - try { - value = engine.expression(library, "PredecessorUnderflowDt").value(); - fail(); - } catch (RuntimeException re) { - assertThat(re.getMessage(), is("The year: 0 falls below the accepted bounds of 0001-9999.")); - } - - try { - value = engine.expression(library, "PredecessorUnderflowT").value(); - fail(); - } catch (RuntimeException re) { - assertThat( - re.getMessage(), - is("The result of the successor operation precedes the minimum value allowed for the Time type")); - } - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.PrecisionEvaluator#evaluate(Context)} - */ - @Test - void precision() { - var value = engine.expression(library, "PrecisionDecimal5").value(); - assertEquals(5, value); - - value = engine.expression(library, "PrecisionDateYear").value(); - assertEquals(4, value); - - value = engine.expression(library, "PrecisionDateTimeMs").value(); - assertEquals(17, value); - - value = engine.expression(library, "PrecisionTimeMinute").value(); - assertEquals(4, value); - - value = engine.expression(library, "PrecisionTimeMs").value(); - assertEquals(9, value); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.PowerEvaluator#evaluate(Context)} - */ - @Test - void power() { - var value = engine.expression(library, "PowerNullToNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Power0To0").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "Power2To2").value(); - assertThat(value, is(4)); - - value = engine.expression(library, "Power2To2Long").value(); - assertThat(value, is(4L)); - - value = engine.expression(library, "PowerNeg2To2").value(); - assertThat(value, is(4)); - - value = engine.expression(library, "Power2ToNeg2").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("0.25"))); - - value = engine.expression(library, "Power2DTo2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(4d))); - - value = engine.expression(library, "PowerNeg2DTo2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(4d))); - - value = engine.expression(library, "Power2DToNeg2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0.25))); - - value = engine.expression(library, "Power2DTo2").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(4d))); - - value = engine.expression(library, "Power2To2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(4d))); - - value = engine.expression(library, "Power2To4").value(); - assertThat(value, is(16)); - - value = engine.expression(library, "Power2To3Long").value(); - assertThat(value, is(8L)); - - value = engine.expression(library, "Power2DTo4D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("16.0"))); - - value = engine.expression(library, "Power2DToNeg2DEquivalence").value(); - assertThat(value, is(true)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.RoundEvaluator#evaluate(Context)} - */ - @Test - void round() { - var value = engine.expression(library, "RoundNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Round1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(1.0))); - - value = engine.expression(library, "Round0D5").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(1.0))); - - value = engine.expression(library, "Round0D4").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0.0))); - - value = engine.expression(library, "Round3D14159").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("3.14"))); - - value = engine.expression(library, "RoundNeg0D5").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0.0))); - - value = engine.expression(library, "RoundNeg0D4").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(0.0))); - - value = engine.expression(library, "RoundNeg0D6").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-1.0))); - - value = engine.expression(library, "RoundNeg1D1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-1.0))); - - value = engine.expression(library, "RoundNeg1D5").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-1.0))); - - value = engine.expression(library, "RoundNeg1D6").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-2.0))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.SubtractEvaluator#evaluate(Context)} - */ - @Test - void subtract() { - var value = engine.expression(library, "SubtractNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Subtract1And1").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "Subtract1And1Long").value(); - assertThat(value, is(0L)); - - value = engine.expression(library, "Subtract1DAnd2D").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-1.0))); - - value = engine.expression(library, "Subtract1CMAnd2CM").value(); - assertEquals(0, new BigDecimal("-1.0").compareTo(((Quantity) value).getValue())); - assertEquals("cm", ((Quantity) value).getUnit()); - - value = engine.expression(library, "Subtract2And11D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("0.9"))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.SuccessorEvaluator#evaluate(Context)} - */ - @Test - void successor() { - var value = engine.expression(library, "SuccessorNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "SuccessorOf0").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "SuccessorOf1").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "SuccessorOf1Long").value(); - assertThat(value, is(2L)); - - value = engine.expression(library, "SuccessorOf1D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("1.00000001"))); - - value = engine.expression(library, "SuccessorOf101D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("1.01000001"))); - - value = engine.expression(library, "SuccessorOfJan12000").value(); - final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 2000, 1, 2); - assertTrue(EquivalentEvaluator.equivalent(value, expectedDateTime)); - - value = engine.expression(library, "SuccessorOfNoon").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(12, 0, 0, 1))); - - try { - value = engine.expression(library, "SuccessorOverflowDt").value(); - fail(); - } catch (RuntimeException re) { - assertThat(re.getMessage(), is("The year: 10000 falls above the accepted bounds of 0001-9999.")); - } - - try { - value = engine.expression(library, "SuccessorOverflowT").value(); - fail(); - } catch (RuntimeException re) { - assertThat( - re.getMessage(), - is("The result of the successor operation exceeds the maximum value allowed for the Time type")); - } - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.TruncateEvaluator#evaluate(Context)} - */ - @Test - void truncate() { - var value = engine.expression(library, "TruncateNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "Truncate0").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "Truncate0D0").value(); - assertThat(value, is((0))); - - value = engine.expression(library, "Truncate0D1").value(); - assertThat(value, is((0))); - - value = engine.expression(library, "Truncate1").value(); - assertThat(value, is((1))); - - value = engine.expression(library, "Truncate1D0").value(); - assertThat(value, is((1))); - - value = engine.expression(library, "Truncate1D1").value(); - assertThat(value, is((1))); - - value = engine.expression(library, "Truncate1D9").value(); - assertThat(value, is((1))); - - value = engine.expression(library, "TruncateNeg1").value(); - assertThat(value, is((-1))); - - value = engine.expression(library, "TruncateNeg1D0").value(); - assertThat(value, is((-1))); - - value = engine.expression(library, "TruncateNeg1D1").value(); - assertThat(value, is((-1))); - - value = engine.expression(library, "TruncateNeg1D9").value(); - assertThat(value, is((-1))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.TruncatedDivideEvaluator#evaluate(Context)} - */ - @Test - void truncatedDivide() { - var value = engine.expression(library, "TruncatedDivideNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "TruncatedDivide2By1").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "TruncatedDivide10By3").value(); - assertThat(value, is(3)); - - value = engine.expression(library, "TruncatedDivide10d1By3D1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(3.0))); - - value = engine.expression(library, "TruncatedDivideNeg2ByNeg1").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "TruncatedDivideNeg10ByNeg3").value(); - assertThat(value, is(3)); - - value = engine.expression(library, "TruncatedDivideNeg10d1ByNeg3D1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(3.0))); - - value = engine.expression(library, "TruncatedDivideNeg2By1").value(); - assertThat(value, is(-2)); - - value = engine.expression(library, "TruncatedDivideNeg10By3").value(); - assertThat(value, is(-3)); - - value = engine.expression(library, "TruncatedDivideNeg10d1By3D1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-3.0))); - - value = engine.expression(library, "TruncatedDivide2ByNeg1").value(); - assertThat(value, is((-2))); - - value = engine.expression(library, "TruncatedDivide10ByNeg3").value(); - assertThat(value, is(-3)); - - value = engine.expression(library, "TruncatedDivide10d1ByNeg3D1").value(); - assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(-3.0))); - - value = engine.expression(library, "TruncatedDivide10By5D").value(); - assertThat((BigDecimal) value, comparesEqualTo(new BigDecimal("2.0"))); - - value = engine.expression(library, "TruncatedDivide10By5DQuantity").value(); - assertThat(((Quantity) value).getValue(), comparesEqualTo(new BigDecimal("2.0"))); - assertThat(((Quantity) value).getUnit(), is("g")); - - value = engine.expression(library, "TruncatedDivide414By206DQuantity").value(); - assertThat(((Quantity) value).getValue(), comparesEqualTo(new BigDecimal("2.0"))); - assertThat(((Quantity) value).getUnit(), is("m")); - - value = engine.expression(library, "TruncatedDivide10By0DQuantity").value(); - assertThat(value, nullValue()); - } } diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlComparisonOperatorsTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlComparisonOperatorsTest.java index 2c71fa782..ed3825281 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlComparisonOperatorsTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlComparisonOperatorsTest.java @@ -1,695 +1,17 @@ package org.opencds.cqf.cql.engine.execution; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.ArrayList; -import java.util.List; -import org.cqframework.cql.cql2elm.CqlCompilerException; -import org.cqframework.cql.cql2elm.CqlCompilerOptions; import org.junit.jupiter.api.Test; import org.opencds.cqf.cql.engine.elm.executing.GreaterEvaluator; import org.opencds.cqf.cql.engine.exception.CqlException; class CqlComparisonOperatorsTest extends CqlTestBase { - @Test - void cql_comparison_test_suite_compiles() { - var errors = new ArrayList(); - this.getLibrary(toElmIdentifier("CqlComparisonOperatorsTest"), errors, testCompilerOptions()); - assertFalse( - CqlCompilerException.hasErrors(errors), - String.format("Test library compiled with the following errors : %s", this.toString(errors))); - } - @Test void all_comparison_operators_tests() { - var eng = getEngine(testCompilerOptions()); - var results = eng.evaluate(toElmIdentifier("CqlComparisonOperatorsTest")); - - Object value = results.forExpression("BetweenIntTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqTrueTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqTrueFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleEqFalseFalse").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqFalseTrue").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleEqNullNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("SimpleEqTrueNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("SimpleEqNullTrue").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("SimpleEqInt1Int1").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqInt1Int2").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleEqInt1Int2Long").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleEqStringAStringA").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqStringAStringB").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleEqFloat1Float1").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqFloat1Float1WithZ").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqFloat1Float1WithPrecisionAndZ").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleEqFloat1Float2").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleEqFloat1Int1").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleEqFloat1Int2").value(); - assertThat(value, is(false)); - - value = results.forExpression("QuantityEqCM1CM1").value(); - assertThat(value, is(true)); - - value = results.forExpression("QuantityEqDiffPrecision").value(); - assertThat(value, is(true)); - - value = results.forExpression("RatioEqual").value(); - assertThat(value, is(true)); - - value = results.forExpression("RatioNotEqual").value(); - assertThat(value, is(false)); - - // TODO: Quantity unit comparison is not implemented yet - // value = results.forExpression("QuantityEqCM1M01").value(); - // assertThat(value, is(true)); - - value = results.forExpression("TupleEqJohnJohn").value(); - assertThat(value, is(true)); - - // TODO - this test should actually throw a translation error, but due to a bug in the translator it isn't - - // remove once bug is resolved - // value = results.forExpression("TupleEqJohnJohnFalse").value(); - // assertThat(value, is(false)); - - // TODO - this test should actually throw a translation error, but due to a bug in the translator it isn't - - // remove once bug is resolved - // value = results.forExpression("TupleEqJohnJohnFalse2").value(); - // assertThat(value, is(false)); - - value = results.forExpression("TupleEqJohnJane").value(); - assertThat(value, is(false)); - - value = results.forExpression("TupleEqJohn1John2").value(); - assertThat(value, is(false)); - - value = results.forExpression("TupleEqDateTimeTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("TupleEqDateTimeFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("TupleEqTimeTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("TupleEqTimeFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("DateTimeEqTodayToday").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeEqJanJan").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeEqJanJuly").value(); - assertThat(value, is(false)); - - value = results.forExpression("DateTimeEqNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("DateTimeUTC").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeEqTodayYesterday").value(); - assertThat(value, is(false)); - - value = results.forExpression("TimeEq10A10A").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeEq10A10P").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterZZ").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterLong").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterZ1").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterZNeg1").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterDecZZ").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterDecZ1").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterDecZNeg1").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterCM0CM0").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterCM0CM1").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterCM0NegCM1").value(); - assertThat(value, is(true)); - - // TODO: Quantity unit comparison is not implemented yet - // value = results.forExpression("GreaterM1CM1").value(); - // assertThat(value, is(true)); - - // TODO: Quantity unit comparison is not implemented yet - // value = results.forExpression("GreaterM1CM10").value(); - // assertThat(value, is(true)); - - value = results.forExpression("GreaterAA").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterAB").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterBA").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterAThanAA").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterAAThanA").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterJackJill").value(); - assertThat(value, is(false)); - - value = results.forExpression("DateTimeGreaterTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeGreaterFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("TimeGreaterTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeGreaterFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("UncertaintyGreaterNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("UncertaintyGreaterTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("UncertaintyGreaterFalse").value(); - assertThat(value, is(false)); - - try { + assertThrows(CqlException.class, () -> { GreaterEvaluator.greater(1, "one", engine.getState()); - fail(); - } catch (CqlException e) { - // pass - } - - value = results.forExpression("GreaterOrEqualZZ").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualZ1").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterOrEqualZ1Long").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterOrEqualZNeg1").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualDecZZ").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualDecZ1").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterOrEqualDecZNeg1").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualCM0CM0").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualCM0CM1").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterOrEqualCM0NegCM1").value(); - assertThat(value, is(true)); - - // TODO: Quantity unit comparison is not implemented yet - // value = results.forExpression("GreaterOrEqualM1CM1").value(); - // assertThat(value, is(true)); - // - // value = results.forExpression("GreaterOrEqualM1CM10").value(); - // assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualAA").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualAB").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterOrEqualBA").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualAThanAA").value(); - assertThat(value, is(false)); - - value = results.forExpression("GreaterOrEqualAAThanA").value(); - assertThat(value, is(true)); - - value = results.forExpression("GreaterOrEqualJackJill").value(); - assertThat(value, is(false)); - - value = results.forExpression("DateTimeGreaterEqTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeGreaterEqTrue2").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeGreaterEqFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("TimeGreaterEqTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeGreaterEqTrue2").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeGreaterEqFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("UncertaintyGreaterEqualNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("UncertaintyGreaterEqualTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("UncertaintyGreaterEqualFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessZZ").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessZ1").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessLong").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessLongNeg").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessZNeg1").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessDecZZ").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessDecZ1").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessDecZNeg1").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessCM0CM0").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessCM0CM1").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessCM0NegCM1").value(); - assertThat(value, is(false)); - - // TODO: Quantity unit comparison is not implemented yet - // value = results.forExpression("LessM1CM1").value(); - // assertThat(value, is(false)); - - // value = results.forExpression("LessM1CM10").value(); - // assertThat(value, is(false)); - - value = results.forExpression("LessAA").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessAB").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessBA").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessAThanAA").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessAAThanA").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessJackJill").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeLessTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeLessFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("TimeLessTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeLessFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("UncertaintyLessNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("UncertaintyLessTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("UncertaintyLessFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessOrEqualZZ").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualZ1").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualZ1Long").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualZNeg1").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessOrEqualDecZZ").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualDecZ1").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualDecZNeg1").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessOrEqualCM0CM0").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualCM0CM1").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualCM0NegCM1").value(); - assertThat(value, is(false)); - - // TODO: uncomment once quantity unit comparison is implemented - // value = results.forExpression("LessOrEqualM1CM1").value(); - // assertThat(value, is(false)); - // - // value = results.forExpression("LessOrEqualM1CM10").value(); - // assertThat(value, is(false)); - - value = results.forExpression("LessOrEqualAA").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualAB").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualBA").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessOrEqualAThanAA").value(); - assertThat(value, is(true)); - - value = results.forExpression("LessOrEqualAAThanA").value(); - assertThat(value, is(false)); - - value = results.forExpression("LessOrEqualJackJill").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeLessEqTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeLessEqTrue2").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeLessEqFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("TimeLessEqTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeLessEqTrue2").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeLessEqFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("UncertaintyLessEqualNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("UncertaintyLessEqualTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("UncertaintyLessEqualFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivTrueTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivTrueFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivFalseFalse").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFalseTrue").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivNullNull").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivTrueNull").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivNullTrue").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivInt1Int1").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivInt1Int2").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivStringAStringA").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivStringAStringB").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivStringIgnoreCase").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFloat1Float1").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFloat1Float2").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivFloat1Float1WithZ").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFloat1Float1WithPrecision").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFloat1Float1WithPrecisionAndZ").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFloatTrailingZero").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFloat1Int1").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivFloat1Int2").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivEqCM1CM1").value(); - assertThat(value, is(true)); - - value = results.forExpression("RatioEquivalent").value(); - assertThat(value, is(true)); - - value = results.forExpression("RatioNotEquivalent").value(); - assertThat(value, is(false)); - - // TODO: Quantity unit comparison is not implemented yet - // value = results.forExpression("EquivEqCM1M01").value(); - // assertThat(value, is(true)); - - value = results.forExpression("EquivTupleJohnJohn").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivTupleJohnJohnWithNulls").value(); - assertThat(value, is(true)); - - // TODO - this test should actually throw a translation error, but due to a bug in the translator it isn't - - // remove once bug is resolved - // value = results.forExpression("EquivTupleJohnJohnFalse").value(); - // assertThat(value, is(false)); - - // TODO - this test should actually throw a translation error, but due to a bug in the translator it isn't - - // remove once bug is resolved - // value = results.forExpression("EquivTupleJohnJohnFalse2").value(); - // assertThat(value, is(false)); - - value = results.forExpression("EquivTupleJohnJane").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivTupleJohn1John2").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivDateTimeTodayToday").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivDateTimeTodayYesterday").value(); - assertThat(value, is(false)); - - value = results.forExpression("EquivTime10A10A").value(); - assertThat(value, is(true)); - - value = results.forExpression("EquivTime10A10P").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleNotEqTrueTrue").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleNotEqTrueFalse").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleNotEqFalseFalse").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleNotEqFalseTrue").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleNotEqNullNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("SimpleNotEqTrueNull").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("SimpleNotEqNullTrue").value(); - assertThat(value, is(nullValue())); - - value = results.forExpression("SimpleNotEqInt1Int1").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleNotEqInt1Int2").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleNotEqInt1Int2Long").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleNotEqStringAStringA").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleNotEqStringAStringB").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleNotEqFloat1Float1").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleNotEqFloat1Float2").value(); - assertThat(value, is(true)); - - value = results.forExpression("SimpleNotEqFloat1Int1").value(); - assertThat(value, is(false)); - - value = results.forExpression("SimpleNotEqFloat1Int2").value(); - assertThat(value, is(true)); - - value = results.forExpression("QuantityNotEqCM1CM1").value(); - assertThat(value, is(false)); - - // TODO: Quantity unit comparison is not implemented yet - // value = results.forExpression("QuantityNotEqCM1M01").value(); - // assertThat(value, is(false)); - - value = results.forExpression("TupleNotEqJohnJohn").value(); - assertThat(value, is(false)); - - value = results.forExpression("TupleNotEqJohnJane").value(); - assertThat(value, is(true)); - - value = results.forExpression("TupleNotEqJohn1John2").value(); - assertThat(value, is(true)); - - value = results.forExpression("DateTimeNotEqTodayToday").value(); - assertThat(value, is(false)); - - value = results.forExpression("DateTimeNotEqTodayYesterday").value(); - assertThat(value, is(true)); - - value = results.forExpression("TimeNotEq10A10A").value(); - assertThat(value, is(false)); - - value = results.forExpression("TimeNotEq10A10P").value(); - assertThat(value, is(true)); - } - - protected CqlCompilerOptions testCompilerOptions() { - var options = CqlCompilerOptions.defaultOptions(); - // This test suite contains some definitions that use features that are usually - // turned off for CQL. - options.getOptions().remove(CqlCompilerOptions.Options.DisableListDemotion); - options.getOptions().remove(CqlCompilerOptions.Options.DisableListPromotion); - return options; - } - - String toString(List errors) { - StringBuilder builder = new StringBuilder(); - - for (var e : errors) { - builder.append(e.toString() + System.lineSeparator()); - if (e.getLocator() != null) { - builder.append("at" + System.lineSeparator()); - builder.append(e.getLocator().toLocator() + System.lineSeparator()); - } - builder.append(System.lineSeparator()); - } - - return builder.toString(); + }); } } diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlConditionalOperatorsTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlConditionalOperatorsTest.java deleted file mode 100644 index 344246947..000000000 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlConditionalOperatorsTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.opencds.cqf.cql.engine.execution; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -import java.io.IOException; -import org.hl7.elm.r1.VersionedIdentifier; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("removal") -class CqlConditionalOperatorsTest extends CqlTestBase { - - private static final VersionedIdentifier library = new VersionedIdentifier().withId("CqlConditionalOperatorsTest"); - - @Test - void all_conditional_operators_tests() throws IOException { - var value = engine.expression(library, "IfTrue1").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "IfFalse1").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "IfNull1").value(); - assertThat(value, is(10)); - value = engine.expression(library, "StandardCase1").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "StandardCase2").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "StandardCase3").value(); - assertThat(value, is(15)); - } -} diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.java index 8a9b5e3f9..a19fd52a1 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.java @@ -2,16 +2,17 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.*; -import java.math.BigDecimal; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.EnumSet; +import java.util.List; import org.hl7.elm.r1.VersionedIdentifier; import org.junit.jupiter.api.Test; import org.opencds.cqf.cql.engine.elm.executing.AfterEvaluator; import org.opencds.cqf.cql.engine.elm.executing.EquivalentEvaluator; import org.opencds.cqf.cql.engine.exception.CqlException; -import org.opencds.cqf.cql.engine.exception.InvalidDateTime; import org.opencds.cqf.cql.engine.runtime.*; @SuppressWarnings("removal") @@ -19,446 +20,12 @@ class CqlDateTimeOperatorsTest extends CqlTestBase { private static final VersionedIdentifier library = new VersionedIdentifier().withId("CqlDateTimeOperatorsTest"); - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.AddEvaluator#evaluate(Context)} - */ - @Test - void add() { - final BigDecimal bigDecimalZoneOffset = getBigDecimalZoneOffset(); - - var value = engine.expression(library, "DateTimeAdd5Years").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2010, 10, 10))); - - try { - engine.expression(library, "DateTimeAddInvalidYears").value(); - fail(); - } catch (InvalidDateTime ae) { - // pass - } - - value = engine.expression(library, "DateTimeAdd5Months").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 10, 10))); - - value = engine.expression(library, "DateTimeAddMonthsOverflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2006, 3, 10))); - - value = engine.expression(library, "DateTimeAddThreeWeeks").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2018, 5, 23))); - - value = engine.expression(library, "DateTimeAddYearInWeeks").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2019, 5, 23))); - - value = engine.expression(library, "DateTimeAdd5Days").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 15))); - - value = engine.expression(library, "DateTimeAddDaysOverflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 7, 1))); - - value = engine.expression(library, "DateTimeAdd5Hours").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 10))); - - value = engine.expression(library, "DateTimeAdd5HoursWithLeftMinPrecisionSecond") - .value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 10, 20, 30))); - - value = engine.expression(library, "DateTimeAdd5HoursWithLeftMinPrecisionDay") - .value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10))); - - value = engine.expression(library, "DateTimeAdd5HoursWithLeftMinPrecisionDayOverflow") - .value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 11))); - - value = engine.expression(library, "DateAdd2YearsAsMonths").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2016))); - - value = engine.expression(library, "DateAdd2YearsAsMonthsRem1").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2016))); - - value = engine.expression(library, "DateAdd33Days").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2014, 7))); - - value = engine.expression(library, "DateAdd1Year").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2015, 6))); - - value = engine.expression(library, "DateTimeAddHoursOverflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 11, 0))); - - value = engine.expression(library, "DateTimeAdd5Minutes").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 5, 10))); - - value = engine.expression(library, "DateTimeAddMinutesOverflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 10, 6, 0))); - - value = engine.expression(library, "DateTimeAdd5Seconds").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 5, 5, 10))); - - value = engine.expression(library, "DateTimeAddSecondsOverflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 10, 5, 6, 0))); - - value = engine.expression(library, "DateTimeAdd5Milliseconds").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 5, 5, 5, 10))); - - value = engine.expression(library, "DateTimeAddMillisecondsOverflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 10, 5, 5, 6, 0))); - - value = engine.expression(library, "DateTimeAddLeapYear").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2013, 2, 28))); - - value = engine.expression(library, "DateTimeAdd2YearsByMonths").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016))); - - value = engine.expression(library, "DateTimeAdd2YearsByDays").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016))); - - value = engine.expression(library, "DateTimeAdd2YearsByDaysRem5Days").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016))); - - value = engine.expression(library, "TimeAdd5Hours").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(20, 59, 59, 999))); - - value = engine.expression(library, "TimeAdd1Minute").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(16, 0, 59, 999))); - - value = engine.expression(library, "TimeAdd1Second").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(16, 0, 0, 999))); - - value = engine.expression(library, "TimeAdd1Millisecond").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(16, 0, 0, 0))); - - value = engine.expression(library, "TimeAdd5Hours1Minute").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(21, 0, 59, 999))); - - // checking access ordering and returning correct result - value = engine.expression(library, "TimeAdd1Second").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(16, 0, 0, 999))); - - value = engine.expression(library, "TimeAdd5hoursByMinute").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(20, 59, 59, 999))); - } - /** * {@link org.opencds.cqf.cql.engine.elm.execution.AfterEvaluator#evaluate(Context)} */ @Test void after() { - final BigDecimal bigDecimalZoneOffset = getBigDecimalZoneOffset(); - - var value = engine.expression(library, "DateTimeAfterYearTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAfterMonthTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterMonthFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAfterDayTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterDayTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterDayFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAfterHourTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAfterMinuteTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAfterSecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAfterMillisecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeAfterMillisecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAfterUncertain").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeAfterHourTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeAfterHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeAfterMinuteTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeAfterMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeAfterSecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeAfterSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeAfterMillisecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeAfterMillisecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeAfterTimeCstor").value(); - assertThat(value, is(true)); - try { - AfterEvaluator.after(12, "This is an error", null, engine.getState()); - fail(); - } catch (CqlException e) { - // pass - } - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.BeforeEvaluator#evaluate(Context)} - */ - @Test - void before() { - - var value = engine.expression(library, "DateTimeBeforeYearTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeBeforeMonthTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeMonthFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeBeforeDayTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeDayTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeDayFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeBeforeHourTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeBeforeMinuteTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeBeforeSecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeBeforeMillisecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeBeforeMillisecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "BeforeTimezoneTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "BeforeTimezoneFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeBeforeHourTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeBeforeHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeBeforeMinuteTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeBeforeMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeBeforeSecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeBeforeSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeBeforeMillisecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeBeforeMillisecondFalse").value(); - assertThat(value, is(false)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.DateTimeEvaluator#evaluate(Context)} - */ - @Test - void dateTime() { - final BigDecimal bigDecimalZoneOffset = getBigDecimalZoneOffset(); - - var value = engine.expression(library, "DateTimeYear").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2003))); - - value = engine.expression(library, "DateTimeMonth").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2003, 10))); - - value = engine.expression(library, "DateTimeDay").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2003, 10, 29))); - - value = engine.expression(library, "DateTimeHour").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2003, 10, 29, 20))); - - value = engine.expression(library, "DateTimeMinute").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2003, 10, 29, 20, 50))); - - value = engine.expression(library, "DateTimeSecond").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2003, 10, 29, 20, 50, 33))); - - value = engine.expression(library, "DateTimeMillisecond").value(); - assertTrue(EquivalentEvaluator.equivalent( - value, new DateTime(bigDecimalZoneOffset, 2003, 10, 29, 20, 50, 33, 955))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.DateTimeComponentFromEvaluator#evaluate(Context)} - */ - @Test - void dateTimeComponentFrom() { - - var value = engine.expression(library, "DateTimeComponentFromYear").value(); - assertThat(value, is(2003)); - - value = engine.expression(library, "DateTimeComponentFromMonth").value(); - assertThat(value, is(10)); - - value = engine.expression(library, "DateTimeComponentFromMonthMinBoundary") - .value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DateTimeComponentFromDay").value(); - assertThat(value, is(29)); - - value = engine.expression(library, "DateTimeComponentFromHour").value(); - assertThat(value, is(20)); - - value = engine.expression(library, "DateTimeComponentFromMinute").value(); - assertThat(value, is(50)); - - value = engine.expression(library, "DateTimeComponentFromSecond").value(); - assertThat(value, is(33)); - - value = engine.expression(library, "DateTimeComponentFromMillisecond").value(); - assertThat(value, is(955)); - - value = engine.expression(library, "DateTimeComponentFromTimezone").value(); - assertThat(value, is(new BigDecimal("1.0"))); - - value = engine.expression(library, "DateTimeComponentFromDate").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2003, 10, 29))); - - value = engine.expression(library, "TimeComponentFromHour").value(); - assertThat(value, is(23)); - - value = engine.expression(library, "TimeComponentFromMinute").value(); - assertThat(value, is(20)); - - value = engine.expression(library, "TimeComponentFromSecond").value(); - assertThat(value, is(15)); - - value = engine.expression(library, "TimeComponentFromMilli").value(); - assertThat(value, is(555)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.DifferenceBetweenEvaluator#evaluate(Context)} - */ - @Test - void difference() { - - var value = engine.expression(library, "DateTimeDifferenceYear").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "DateTimeDifferenceMonth").value(); - assertThat(value, is(8)); - - value = engine.expression(library, "DateTimeDifferenceDay").value(); - assertThat(value, is(10)); - - value = engine.expression(library, "DateTimeDifferenceHour").value(); - assertThat(value, is(8)); - - value = engine.expression(library, "DateTimeDifferenceMinute").value(); - assertThat(value, is(9)); - - value = engine.expression(library, "DateTimeDifferenceSecond").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "DateTimeDifferenceMillisecond").value(); - assertThat(value, is(3600400)); - - value = engine.expression(library, "DateTimeDifferenceWeeks").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DateTimeDifferenceWeeks2").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "DateTimeDifferenceWeeks2").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "DateTimeDifferenceNegative").value(); - assertThat(value, is(-18)); - - value = engine.expression(library, "DateTimeDifferenceUncertain").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeDifferenceHour").value(); - assertThat(value, is(3)); - - value = engine.expression(library, "TimeDifferenceMinute").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "TimeDifferenceSecond").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "TimeDifferenceMillis").value(); - assertThat(value, is(-5)); - - value = engine.expression(library, "DifferenceInHoursA").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DifferenceInMinutesA").value(); - assertThat(value, is(45)); - - value = engine.expression(library, "DifferenceInDaysA").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DifferenceInHoursAA").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DifferenceInMinutesAA").value(); - assertThat(value, is(45)); - - value = engine.expression(library, "DifferenceInDaysAA").value(); - assertThat(value, is(1)); + assertThrows(CqlException.class, () -> AfterEvaluator.after(12, "This is an error", null, engine.getState())); } /** @@ -470,25 +37,6 @@ void duration() { var value = engine.expression(library, "DateTimeDurationBetweenYear").value(); assertThat(value, is(5)); - value = engine.expression(library, "DurationInWeeks").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DurationInWeeks2").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DurationInWeeks3").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "DateTimeDurationBetweenYearOffset").value(); - assertThat(value, is(4)); - - value = engine.expression(library, "DateTimeDurationBetweenMonth").value(); - assertThat(value, is(0)); - - value = engine.expression(library, "DateTimeDurationBetweenDaysDiffYears") - .value(); - assertThat(value, is(-788)); - value = engine.expression(library, "DateTimeDurationBetweenUncertainInterval") .value(); assertEquals(17, ((Interval) value).getStart()); @@ -515,76 +63,9 @@ void duration() { assertEquals(289, ((Interval) value).getStart()); assertEquals(1936, ((Interval) value).getEnd()); - try { - value = engine.expression(library, "DateTimeDurationBetweenUncertainDiv") - .value(); - fail(); - } catch (RuntimeException re) { - // pass - } - - value = engine.expression(library, "DateTimeDurationBetweenMonthUncertain") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeDurationBetweenMonthUncertain2") - .value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "DateTimeDurationBetweenMonthUncertain3") - .value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeDurationBetweenMonthUncertain4") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeDurationBetweenMonthUncertain5") - .value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeDurationBetweenMonthUncertain6") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeDurationBetweenMonthUncertain7") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DurationInYears").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "TimeDurationBetweenHour").value(); - assertThat(value, is(2)); - - value = engine.expression(library, "TimeDurationBetweenHourDiffPrecision") - .value(); - assertThat(value, is(1)); - - value = engine.expression(library, "TimeDurationBetweenMinute").value(); - assertThat(value, is(4)); - - value = engine.expression(library, "TimeDurationBetweenSecond").value(); - assertThat(value, is(4)); - - value = engine.expression(library, "TimeDurationBetweenMillis").value(); - assertThat(value, is(5)); - - value = engine.expression(library, "DurationInHoursA").value(); - assertThat(value, is(1)); - - value = engine.expression(library, "DurationInMinutesA").value(); - assertThat(value, is(45)); - - // value = engine.expression(library, "DurationInDaysA").value(); - // assertThat(value, is(1)); - - value = engine.expression(library, "DurationInHoursAA").value(); + value = engine.expression(library, "DurationInDaysA").value(); assertThat(value, is(1)); - value = engine.expression(library, "DurationInMinutesAA").value(); - assertThat(value, is(45)); - value = engine.expression(library, "DurationInDaysAA").value(); assertThat(value, is(1)); } @@ -594,11 +75,8 @@ void duration() { */ @Test void now() { - var value = engine.expression(library, "DateTimeNow").value(); - assertThat(value, is(true)); - DateTime evaluationDateTime = new DateTime(getBigDecimalZoneOffset(), 2016, 6, 10, 5, 5, 4, 999); - value = engine.expression( + var value = engine.expression( library, "Issue34A", evaluationDateTime.getDateTime().toZonedDateTime()) .value(); assertTrue(EquivalentEvaluator.equivalent(value, evaluationDateTime)); @@ -607,443 +85,6 @@ void now() { evaluationDateTime.getDateTime().getOffset()); } - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.SameAsEvaluator#evaluate(Context)} - */ - @Test - void sameAs() { - - var value = engine.expression(library, "DateTimeSameAsYearTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameAsYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameAsMonthTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameAsMonthFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameAsDayTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameAsDayFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameAsHourTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameAsHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameAsMinuteTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameAsMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameAsSecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameAsSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameAsMillisecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameAsMillisecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameAsNull").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "SameAsTimezoneTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "SameAsTimezoneFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameAsHourTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameAsHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameAsMinuteTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameAsMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameAsSecondTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameAsSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameAsMillisTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameAsMillisFalse").value(); - assertThat(value, is(false)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.SameOrAfterEvaluator#evaluate(Context)} - */ - @Test - void sameOrAfter() { - var value = engine.expression(library, "DateTimeSameOrAfterYearTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterYearTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrAfterMonthTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterMonthTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterMonthFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrAfterDayTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterDayTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterDayFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrAfterHourTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterHourTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrAfterMinuteTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterMinuteTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrAfterSecondTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterSecondTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrAfterMillisecondTrue1") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterMillisecondTrue2") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrAfterMillisecondFalse") - .value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrAfterNull1").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "SameOrAfterTimezoneTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "SameOrAfterTimezoneFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameOrAfterHourTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterHourTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameOrAfterMinuteTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterMinuteTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameOrAfterSecondTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterSecondTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "TimeSameOrAfterMillisTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterMillisTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "TimeSameOrAfterMillisFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "OnOrAfterTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "Issue32DateTime").value(); - assertThat(value, is(true)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.SameOrBeforeEvaluator#evaluate(Context)} - */ - @Test - void sameOrBefore() { - var value = engine.expression(library, "DateTimeSameOrBeforeYearTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeMonthTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeMonthTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeMonthFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeDayTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeDayTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeDayFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeHourTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeHourTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeHourFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeMinuteTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeMinuteTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeMinuteFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeSecondTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeSecondTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeSecondFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeMillisecondTrue1") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeMillisecondTrue2") - .value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeMillisecondFalse") - .value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeNull1").value(); - assertThat(value, is(nullValue())); - - value = engine.expression(library, "SameOrBeforeTimezoneTrue").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "SameOrBeforeTimezoneFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeYearFalse").value(); - assertThat(value, is(false)); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.SubtractEvaluator#evaluate(Context)} - */ - @Test - void subtract() { - final BigDecimal bigDecimalZoneOffset = getBigDecimalZoneOffset(); - - var value = engine.expression(library, "DateTimeSubtract5Years").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2000, 10, 10))); - - try { - engine.expression(library, "DateTimeSubtractInvalidYears").value(); - fail(); - } catch (InvalidDateTime ae) { - // pass - } - - value = engine.expression(library, "DateTimeSubtract5Months").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 1, 10))); - - value = engine.expression(library, "DateTimeSubtractMonthsUnderflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2004, 11, 10))); - - value = engine.expression(library, "DateTimeSubtractThreeWeeks").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2018, 5, 2))); - - value = engine.expression(library, "DateTimeSubtractYearInWeeks").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2017, 5, 23))); - - value = engine.expression(library, "DateTimeSubtract5Days").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 5))); - - value = engine.expression(library, "DateTimeSubtractDaysUnderflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 5, 30))); - - value = engine.expression(library, "DateTimeSubtract5Hours").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 5))); - - value = engine.expression(library, "DateTimeSubtractHoursUnderflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 9, 23))); - - value = engine.expression(library, "DateTimeSubtract5Minutes").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 5, 5))); - - value = engine.expression(library, "DateTimeSubtractMinutesUnderflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 10, 4, 59))); - - value = engine.expression(library, "DateTimeSubtract5Seconds").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 5, 5, 5))); - - value = engine.expression(library, "DateTimeSubtract1YearInSeconds").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2015, 5))); - - value = engine.expression(library, "DateTimeSubtract15HourPrecisionSecond") - .value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 9, 30, 19, 20, 30))); - - value = engine.expression(library, "DateTimeSubtractSecondsUnderflow").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 10, 5, 4, 59))); - - value = engine.expression(library, "DateTimeSubtract5Milliseconds").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2005, 5, 10, 5, 5, 5, 5))); - - value = engine.expression(library, "DateTimeSubtractMillisecondsUnderflow") - .value(); - assertTrue( - EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2016, 6, 10, 5, 5, 4, 999))); - - value = engine.expression(library, "DateTimeSubtract2YearsAsMonths").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2012))); - - value = engine.expression(library, "DateTimeSubtract2YearsAsMonthsRem1").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new DateTime(bigDecimalZoneOffset, 2012))); - - value = engine.expression(library, "DateSubtract2YearsAsMonthsRem1").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2012))); - - value = engine.expression(library, "DateSubtract2YearsAsMonths").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2012))); - - value = engine.expression(library, "DateSubtract33Days").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2014, 5))); - - value = engine.expression(library, "DateSubtract1Year").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Date(2013, 6))); - - value = engine.expression(library, "TimeSubtract5Hours").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(10, 59, 59, 999))); - - value = engine.expression(library, "TimeSubtract1Minute").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(15, 58, 59, 999))); - - value = engine.expression(library, "TimeSubtract1Second").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(15, 59, 58, 999))); - - value = engine.expression(library, "TimeSubtract1Millisecond").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(15, 59, 58, 999))); - - value = engine.expression(library, "TimeSubtract5Hours1Minute").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(10, 58, 59, 999))); - - value = engine.expression(library, "TimeSubtract5hoursByMinute").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(10, 59, 59, 999))); - } - - /** - * {@link org.opencds.cqf.cql.engine.elm.execution.TimeEvaluator#evaluate(Context)} - */ - @Test - void time() { - - var value = engine.expression(library, "TimeTest2").value(); - assertTrue(EquivalentEvaluator.equivalent(value, new Time(23, 59, 59, 999))); - } - /** * {@link org.opencds.cqf.cql.engine.elm.execution.TimeOfDayEvaluator#evaluate(Context)} */ @@ -1060,19 +101,6 @@ void timeOfDay() { */ @Test void today() { - - var value = engine.expression(library, "DateTimeSameOrBeforeTodayTrue1").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeTodayTrue2").value(); - assertThat(value, is(true)); - - value = engine.expression(library, "DateTimeSameOrBeforeTodayFalse").value(); - assertThat(value, is(false)); - - value = engine.expression(library, "DateTimeAddTodayTrue").value(); - assertThat(value, is(true)); - // context = new Context(library, new DateTime(TemporalHelper.getDefaultOffset(), 2016, 6, 10, 5, 5, 4, // 999)); // value = engine.expression(library, "Issue34B").value(); @@ -1080,4 +108,23 @@ void today() { // Assertions.assertTrue(((DateTime) // result).getDateTime().getOffset().equals(TemporalHelper.getDefaultZoneOffset())); } + + @Test + void defaultTimezoneOffset() { + // Disable expression caching so that we can re-evaluate the same expression in different time zones + var engine = new CqlEngine(environment, EnumSet.noneOf(CqlEngine.Options.class)); + + for (var zoneId : List.of("America/New_York", "Europe/London", "Pacific/Auckland")) { + var evaluationZonedDateTime = ZonedDateTime.of(2024, 10, 3, 15, 54, 0, 0, ZoneId.of(zoneId)); + + // Issue1420 calculates the difference in hours between `DateTime(y, m, d, h, m, s, ms)` and + // `DateTime(y, m, d, h, m, s, ms, 0)`, so it returns the timezone offset of the first DateTime. + // The first DateTime should have the timezone offset of the evaluation request as per the spec. + // CQL also has `timezoneoffset from DateTime(...)` but here we are testing a more complex scenario. + var value = engine.expression(library, "Issue1420", evaluationZonedDateTime) + .value(); + var expected = evaluationZonedDateTime.getOffset().getTotalSeconds() / 3600; + assertEquals(expected, value); + } + } } diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlFunctionTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlFunctionTest.java index 2305e176f..167e1c173 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlFunctionTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlFunctionTest.java @@ -4,12 +4,18 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; +import org.cqframework.cql.cql2elm.CqlCompilerOptions; +import org.cqframework.cql.cql2elm.LibraryBuilder; import org.junit.jupiter.api.Test; class CqlFunctionTest extends CqlTestBase { @Test void all_function_tests() { + var compilerOptions = + CqlCompilerOptions.defaultOptions().withSignatureLevel(LibraryBuilder.SignatureLevel.Overloads); + var engine = getEngine(compilerOptions); + var results = engine.evaluate(toElmIdentifier("CqlFunctionTests")); var value = results.forExpression("FunctionTestStringArg").value(); assertThat(value, is("hello")); diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloadsTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloadsTest.java index 67f86cfb9..b7859690f 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloadsTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloadsTest.java @@ -1,10 +1,13 @@ package org.opencds.cqf.cql.engine.execution; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.cqframework.cql.cql2elm.CqlCompilerOptions; +import org.cqframework.cql.cql2elm.LibraryBuilder; import org.hl7.elm.r1.VersionedIdentifier; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.opencds.cqf.cql.engine.exception.CqlException; @SuppressWarnings("removal") class CqlListDistinguishedOverloadsTest extends CqlTestBase { @@ -13,9 +16,17 @@ class CqlListDistinguishedOverloadsTest extends CqlTestBase { new VersionedIdentifier().withId("CqlListDistinguishedOverloads"); @Test - @Disabled("There's a bug in the cql engine that is causing it to select the wrong function overload at runtime") void list_overload() { - var value = engine.expression(library, "Test").value(); + var compilerOptions = CqlCompilerOptions.defaultOptions(); + + var engine1 = getEngine(compilerOptions.withSignatureLevel(LibraryBuilder.SignatureLevel.Overloads)); + var value = engine1.expression(library, "Test").value(); assertEquals("1, 2, 3, 4, 5", value); + + var engine2 = getEngine(compilerOptions.withSignatureLevel(LibraryBuilder.SignatureLevel.None)); + var cqlException = assertThrows(CqlException.class, () -> engine2.expression(library, "Test")); + assertEquals( + "Ambiguous call to operator 'toString(java.util.List)' in library 'CqlListDistinguishedOverloads'.", + cqlException.getMessage()); } } diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlMainSuiteTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlMainSuiteTest.java index 42024405e..ff3d157fe 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlMainSuiteTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlMainSuiteTest.java @@ -7,6 +7,7 @@ import java.util.*; import org.cqframework.cql.cql2elm.CqlCompilerException; import org.cqframework.cql.cql2elm.CqlCompilerOptions; +import org.cqframework.cql.cql2elm.LibraryBuilder; import org.junit.jupiter.api.Test; class CqlMainSuiteTest extends CqlTestBase { @@ -69,6 +70,11 @@ protected CqlCompilerOptions testCompilerOptions() { options.getOptions().remove(CqlCompilerOptions.Options.DisableListDemotion); options.getOptions().remove(CqlCompilerOptions.Options.DisableListPromotion); + // When called with the null argument, the toString function in the CqlTestSuite + // library can only be unambiguously resolved at runtime if the library is + // compiled with signature level set to Overloads or All. + options.withSignatureLevel(LibraryBuilder.SignatureLevel.Overloads); + return options; } diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlPerformanceIT.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlPerformanceIT.java index 1bc182602..4a447e600 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlPerformanceIT.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlPerformanceIT.java @@ -8,6 +8,7 @@ import java.time.ZonedDateTime; import java.util.TimeZone; import org.cqframework.cql.cql2elm.CqlCompilerOptions; +import org.cqframework.cql.cql2elm.LibraryBuilder; import org.fhir.ucum.UcumException; import org.hl7.elm.r1.VersionedIdentifier; import org.junit.jupiter.api.Test; @@ -98,6 +99,10 @@ protected CqlCompilerOptions testCompilerOptions() { options.getOptions().remove(CqlCompilerOptions.Options.DisableListDemotion); options.getOptions().remove(CqlCompilerOptions.Options.DisableListPromotion); + // When called with the null argument, the toString function in the CqlPerformanceTest + // library can only be unambiguously resolved at runtime if the library is + // compiled with signature level set to Overloads or All. + options.withSignatureLevel(LibraryBuilder.SignatureLevel.Overloads); return options; } } diff --git a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/IncludedValueSetRefTest.java b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/IncludedValueSetRefTest.java index e9c7a66a5..a99199d1e 100644 --- a/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/IncludedValueSetRefTest.java +++ b/Src/java/engine/src/test/java/org/opencds/cqf/cql/engine/execution/IncludedValueSetRefTest.java @@ -2,17 +2,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.opencds.cqf.cql.engine.execution.CqlConceptTest.assertEqual; -import java.util.Collections; -import java.util.List; import org.cqframework.cql.cql2elm.LibraryManager; import org.cqframework.cql.cql2elm.ModelManager; import org.junit.jupiter.api.Test; -import org.opencds.cqf.cql.engine.runtime.Code; -import org.opencds.cqf.cql.engine.terminology.CodeSystemInfo; -import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; -import org.opencds.cqf.cql.engine.terminology.ValueSetInfo; +import org.opencds.cqf.cql.engine.runtime.ValueSet; class IncludedValueSetRefTest { @@ -21,33 +15,15 @@ void all_included_valueset() { LibraryManager libraryManager = new LibraryManager(new ModelManager()); libraryManager.getLibrarySourceLoader().registerProvider(new TestLibrarySourceProvider()); - Code expected = new Code().withCode("M").withSystem("http://test.com/system"); - TerminologyProvider terminologyProvider = new TerminologyProvider() { - public boolean in(Code code, ValueSetInfo valueSet) { - return true; - } - - public Iterable expand(ValueSetInfo valueSet) { - return Collections.singletonList(expected); - } - - public Code lookup(Code code, CodeSystemInfo codeSystem) { - return null; - } - }; - - Environment environment = new Environment(libraryManager, null, terminologyProvider); + Environment environment = new Environment(libraryManager); CqlEngine engine = new CqlEngine(environment); var results = engine.evaluate(CqlTestBase.toElmIdentifier("IncludedValueSetRefTest")); - @SuppressWarnings("unchecked") - List actual = - (List) results.forExpression("IncludedValueSet").value(); - assertNotNull(actual); - assertEquals(1, actual.size()); + var actual = (ValueSet) results.forExpression("IncludedValueSet").value(); - assertEqual(expected, actual.get(0)); + assertNotNull(actual); + assertEquals("http://test/common", actual.getId()); } } diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlAggregateFunctionsTest.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlAggregateFunctionsTest.cql deleted file mode 100644 index acbed4f77..000000000 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlAggregateFunctionsTest.cql +++ /dev/null @@ -1,78 +0,0 @@ -library CqlAggregateFunctionsTest - -//AllTrue -define AllTrueAllTrue : AllTrue({true,true}) -define AllTrueTrueFirst : AllTrue({true,false}) -define AllTrueFalseFirst : AllTrue({false,true}) -define AllTrueAllTrueFalseTrue : AllTrue({true,false,true}) -define AllTrueAllFalseTrueFalse : AllTrue({false,true,false}) -define AllTrueNullFirst : AllTrue({null,true,true}) -define AllTrueEmptyList : AllTrue({}) -define AllTrueIsTrueWhenNull : AllTrue(null) -//AnyTrue -define AnyTrueAllTrue : AnyTrue({true,true}) -define AnyTrueAllFalse : AnyTrue({false,false}) -define AnyTrueAllTrueFalseTrue : AnyTrue({true,false,true}) -define AnyTrueAllFalseTrueFalse : AnyTrue({false,true,false}) -define AnyTrueTrueFirst : AnyTrue({true,false}) -define AnyTrueFalseFirst : AnyTrue({false,true}) -define AnyTrueNullFirstThenTrue : AnyTrue({null,true}) -define AnyTrueNullFirstThenFalse : AnyTrue({null,false}) -define AnyTrueEmptyList : AnyTrue({}) -define AnyTrueIsFalseWhenNull : AnyTrue(null) - -//Avg -define AvgTest1: Avg({ 1.0, 2.0, 3.0, 6.0 }) - -//Product -define Product_Long: Product({5L, 4L, 5L}) - -//Count -define CountTest1: Count({ 15, 5, 99, null, 1 }) -define CountTestDateTime: Count({ DateTime(2014), DateTime(2001), DateTime(2010) }) -define CountTestTime: Count({ @T15:59:59.999, @T05:59:59.999, @T20:59:59.999 }) -define CountTestNull: Count({}) - -//Max -define MaxTestInteger: Max({ 5, 12, 1, 15, 0, 4, 90, 44 }) -define MaxTestLong: Max({ 5L, 12L, 1L, 15L, 0L, 4L, 90L, 44L }) -define MaxTestString: Max({ 'hi', 'bye', 'zebra' }) -define MaxTestDateTime: Max({ DateTime(2012, 10, 5), DateTime(2012, 9, 5), DateTime(2012, 10, 6) }) -define MaxTestTime: Max({ @T15:59:59.999, @T05:59:59.999, @T20:59:59.999 }) - -//Median -define MedianTestDecimal: Median({6.0, 5.0, 4.0, 3.0, 2.0, 1.0}) - -//Min -define MinTestInteger: Min({5, 12, 1, 15, 0, 4, 90, 44}) -define MinTestLong: Min({5L, 12L, 1L, 15L, 0L, 4L, 90L, 44L}) -define MinTestString: Min({'hi', 'bye', 'zebra'}) -define MinTestDateTime: Min({ DateTime(2012, 10, 5), DateTime(2012, 9, 5), DateTime(2012, 10, 6) }) -define MinTestTime: Min({ @T15:59:59.999, @T05:59:59.999, @T20:59:59.999 }) - -//Mode -define ModeTestInteger: Mode({ 2, 1, 8, 2, 9, 1, 9, 9 }) -define ModeTestDateTime: Mode({ DateTime(2012, 10, 5), DateTime(2012, 9, 5), DateTime(2012, 10, 6), DateTime(2012, 9, 5) }) -define ModeTestDateTime2: Mode({ DateTime(2012, 10, 5), DateTime(2012, 10, 5), DateTime(2012, 10, 6), DateTime(2012, 9, 5) }) -define ModeTestTime: Mode({ @T15:59:59.999, @T05:59:59.999, @T20:59:59.999, @T05:59:59.999 }) - -//PopulationStdDev -define PopStdDevTest1: PopulationStdDev({ 1.0, 2.0, 3.0, 4.0, 5.0 }) -define PopulationStdDevIsNull: PopulationStdDev({ null as Quantity, null as Quantity, null as Quantity }) - -//PopulationVariance -define PopVarianceTest1: PopulationVariance({ 1.0, 2.0, 3.0, 4.0, 5.0 }) -define PopVarianceIsNull: PopulationVariance({ null as Quantity, null as Quantity, null as Quantity }) - -//StdDev -define StdDevTest1: StdDev({ 1.0, 2.0, 3.0, 4.0, 5.0 }) -define StdDevIsNull: StdDev({ null as Quantity, null as Quantity, null as Quantity }) - -//Sum -define SumTest1: Sum({ 6.0, 2.0, 3.0, 4.0, 5.0 }) -define SumTestLong: Sum({ 6L, 2L, 3L, 4L, 5L }) -define SumTestQuantity: Sum({1 'ml',2 'ml',3 'ml',4 'ml',5 'ml'}) -define SumTestNull: Sum({ null, 1, null }) - -//Variance -define VarianceTest1: Variance({ 1.0, 2.0, 3.0, 4.0, 5.0 }) \ No newline at end of file diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlAggregateQueryTest.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlAggregateQueryTest.cql deleted file mode 100644 index dd0c3763c..000000000 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlAggregateQueryTest.cql +++ /dev/null @@ -1,45 +0,0 @@ -library CqlAggregateQueryTest - -//Aggregate clause -define AggregateSumWithStart: - ({ 1, 2, 3, 4, 5 }) Num - aggregate Result starting 1: Result + Num // 15 + 1 (the initial value) - -define AggregateSumWithNull: - ({ 1, 2, 3, 4, 5 }) Num - aggregate Result: Coalesce(Result, 0) + Num // 15 + 0 (the initial value from null) - -define AggregateSumAll: - ({ 1, 1, 2, 2, 2, 3, 4, 4, 5 }) Num - aggregate all Result: Coalesce(Result, 0) + Num // 24 + 0 - -define AggregateSumDistinct: - ({ 1, 1, 2, 2, 2, 3, 4, 4, 5 }) Num - aggregate distinct Result: Coalesce(Result, 0) + Num // 15 + 0 (the initial value) - - -define First: {1} -define Second: {2} -define Third: {3} - -define Multi: - from First X, Second Y, Third Z - aggregate Agg: Coalesce(Agg, 0) + X + Y + Z // 6 - -define "A": {1, 2} -define "B": {1, 2} -define "C": {1, 2} - -define MegaMulti: - from "A" X, "B" Y, "C" Z - aggregate Agg starting 0: Agg + X + Y + Z // 36 -- (1+1+1)+(1+1+2)+(1+2+1)+(1+2+2)+(2+1+1)+(2+1+2)+(2+2+1)+(2+2+2) - - -define "1": {1, 2, 2, 1} -define "2": {1, 2, 1, 2} -define "3": {2, 1, 2, 1} - -define MegaMultiDistinct: - from "1" X, "2" Y, "3" Z - aggregate distinct Agg starting 1: Agg + X + Y + Z // 37 -- 1 + (1+1+1)+(1+1+2)+(1+2+1)+(1+2+2)+(2+1+1)+(2+1+2)+(2+2+1)+(2+2+2) - diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.cql deleted file mode 100644 index 0da7327ec..000000000 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.cql +++ /dev/null @@ -1,253 +0,0 @@ -library CqlArithmeticFunctionsTest - -//Abs -define AbsNull: Abs(null as Integer) -define Abs0: Abs(0) -define AbsNeg1: Abs(-1) -define AbsNeg1Long: Abs(-1L) -define AbsNeg1Dec: Abs(-1.0) -define Abs0Dec: Abs(0.0) -define Abs1cm: Abs(-1.0'cm') - -//Add -define AddNull: 1 + null -define Add11: 1 + 1 -define Add12Long: 1L + 2L -define Add1D1D: 1.0 + 1.0 -define Add1Q1Q: 1'g/cm3' + 1'g/cm3' -define AddIAndD: 1 + 2.0 - -//Ceiling -define CeilingNull: Ceiling(null as Decimal) -define Ceiling1D: Ceiling(1.0) -define Ceiling1D1: Ceiling(1.1) -define CeilingNegD1: Ceiling(-0.1) -define CeilingNeg1: Ceiling(-1.0) -define CeilingNeg1D1: Ceiling(-1.1) -define Ceiling1I: Ceiling(1) - -//Divide -define DivideNull: 1 / null -define Divide10: 1 / 0 -define Divide01: 0 / 1 -define Divide11: 1 / 1 -define Divide11Long: 1L / 1L -define Divide1d1d: 1.0 / 1.0 -define Divide103: 10 / 3 -define Divide1Q1: 1'g/cm3' / 1.0 -define Divide1Q1Q: 1'g/cm3' / 1'g/cm3' -define Divide10I5D: 10 / 5.0 -define Divide10I5I: 10 / 5 -define Divide10Q5I: 10.0 'g' / 5 - -//Floor -define FloorNull: Floor(null as Decimal) -define Floor1: Floor(1) -define Floor1D: Floor(1.0) -define Floor1D1: Floor(1.1) -define FloorNegD1: Floor(-0.1) -define FloorNeg1: Floor(-1.0) -define FloorNeg1D1: Floor(-1.1) -define Floor2I: Floor(2) - -//Exp -define ExpNull : Exp(null as Decimal) -define Exp0 : Exp(0) -define ExpNeg0 : Exp(-0) -define Exp1 : Exp(1) -define Exp1Long : Exp(1L) -define ExpNeg1 : Exp(-1) -define Exp1000 : Exp(1000) -define Exp1000D : Exp(1000.0) - -//HighBoundary - -define HighBoundaryDec : HighBoundary(1.587, 8) -define HighBoundaryDate :HighBoundary(@2014, 6) -define HighBoundaryDateTime : HighBoundary(@2014-01-01T08, 17) -define HighBoundaryTime : HighBoundary(@T10:30, 9) -define HighBoundaryNull : HighBoundary(null as Decimal, 8) -define HighBoundaryNullPrecision : HighBoundary(1.58888, null) - - -//Log -define LogNullNull : Log(null, null) -define Log1BaseNull : Log(1, null) -define Log1Base1 : Log(1, 1) -define Log1Base2 : Log(1, 2) -define Log1Base100 : Log(1, 100) -define Log1Base100Long : Log(1L, 100L) -define Log16Base2 : Log(16, 2) -define LogD125Base2 : Log(0.125, 2) - -//LowBoundary -define LowBoundaryDec : LowBoundary(1.587, 8) -define LowBoundaryDate :LowBoundary(@2014, 6) -define LowBoundaryDateTime : LowBoundary(@2014-01-01T08, 17) -define LowBoundaryTime : LowBoundary(@T10:30, 9) -define LowBoundaryNull : LowBoundary(null as Decimal, 8) -define LowBoundaryNullPrecision : LowBoundary(1.58888, null) - -//Ln -define LnNull : Ln(null) -define Ln0 : Ln(0) -define LnNeg0 : Ln(-0) -define Ln1 : Ln(1) -define Ln1Long : Ln(1L) -define LnNeg1 : Ln(-1) -define Ln1000 : Ln(1000) -define Ln1000D : Ln(1000.0) - -//MinValue -define IntegerMinValue: minimum Integer -define LongMinValue: minimum Long -define DecimalMinValue: minimum Decimal -define QuantityMinValue: minimum Quantity -define DateTimeMinValue: minimum DateTime -define TimeMinValue: minimum Time - -//MaxValue -define IntegerMaxValue: maximum Integer -define LongMaxValue: maximum Long -define DecimalMaxValue: maximum Decimal -define QuantityMaxValue: maximum Quantity -define DateTimeMaxValue: maximum DateTime -define TimeMaxValue: maximum Time - -//Modulo -define ModuloNull : 1 mod null -define Modulo0By0 : 0 mod 0 -define Modulo4By2 : 4 mod 2 -define Modulo4By2Long : 4L mod 2L -define Modulo4DBy2D : 4.0 mod 2.0 -define Modulo10By3 : 10 mod 3 -define Modulo10DBy3D : 10.0 mod 3.0 -define Modulo10IBy3D: 10 mod 3.0 -define ModuloDResult: 3.5 mod 3 -define Modulo10By3Quantity: 10.0 'g' mod 3.0 'g' -define Modulo10By0Quantity: 10.0 'g' mod 0.0 'g' - -//Multiply -define MultiplyNull: 1 * null -define Multiply1By1 : 1 * 1 -define Multiply2By3Long : 2L * 3L -define Multiply1DBy2D : 1.0 * 2.0 -define Multiply1IBy2D: 1 * 2.0 -define Multiply1CMBy2CM : 1.0 'cm' * 2.0 'cm' - -//Negate -define NegateNull: -(null as Integer) -define Negate0 : -0 -define NegateNeg0 : -(-0) -define Negate1 : -1 -define Negate1Long : -1L -define NegateMaxLong : -9223372036854775807L -define NegateNeg1 : -(-1) -define Negate0D : -(0.0) -define NegateNeg0D : -(-0.0) -define Negate1D : -(1.0) -define NegateNeg1D : -(-1.0) -define Negate1CM : -(1'cm') - -//Precision -define PrecisionDecimal5: Precision(1.58700) //5 -define PrecisionDateYear: Precision(@2014) // 4 -define PrecisionDateTimeMs: Precision(@2014-01-05T10:30:00.000) // 17 -define PrecisionTimeMinute: Precision(@T10:30) // 4 -define PrecisionTimeMs: Precision(@T10:30:00.000) // 9 - -//Predecessor -define PredecessorNull: predecessor of (null as Integer) -define PredecessorOf0: predecessor of 0 -define PredecessorOf1: predecessor of 1 -define PredecessorOf1Long: predecessor of 1L -define PredecessorOf1D: predecessor of 1.0 -define PredecessorOf101D: predecessor of 1.01 -define PredecessorOf1QCM: predecessor of 1.0 'cm' -define PredecessorOfJan12000: predecessor of DateTime(2000,1,1) -define PredecessorOfNoon: predecessor of @T12:00:00.000 -define PredecessorUnderflowDt: predecessor of DateTime(0001, 1, 1, 0, 0, 0, 0) -define PredecessorUnderflowT: predecessor of @T00:00:00.000 - -//Power -define PowerNullToNull: Power(null as Integer, null as Integer) -define Power0To0: Power(0, 0) -define Power2To2: Power(2, 2) -define Power2To2Long: Power(2L, 2L) -define PowerNeg2To2: Power(-2, 2) -define Power2ToNeg2: Power(2, -2) -define Power2DTo2D: Power(2.0, 2.0) -define PowerNeg2DTo2D: Power(-2.0, 2.0) -define Power2DToNeg2D: Power(2.0, -2.0) -define Power2DTo2: Power(2.0, 2) -define Power2To2D: Power(2, 2.0) -define Power2To4: 2^4 -define Power2To3Long: 2L^3L -define Power2DTo4D: 2.0^4.0 -define Power2DToNeg2DEquivalence: Power(2, -2) ~ 0.25 - -//Round -define RoundNull: Round(null as Decimal) -define Round1: Round(1) -define Round0D5: Round(0.5) -define Round0D4: Round(0.4) -define Round3D14159: Round(3.14159, 2) -define RoundNeg0D5: Round(-0.5) -define RoundNeg0D4: Round(-0.4) -define RoundNeg0D6: Round(-0.6) -define RoundNeg1D1: Round(-1.1) -define RoundNeg1D5: Round(-1.5) -define RoundNeg1D6: Round(-1.6) - -//Subtract -define SubtractNull: 1 - null -define Subtract1And1 : 1 - 1 -define Subtract1And1Long : 1L - 1L -define Subtract1DAnd2D : 1.0 - 2.0 -define Subtract1CMAnd2CM : 1.0 'cm' - 2.0 'cm' -define Subtract2And11D: 2 - 1.1 - -//Successor -define SuccessorNull: successor of (null as Integer) -define SuccessorOf0: successor of 0 -define SuccessorOf1: successor of 1 -define SuccessorOf1Long: successor of 1L -define SuccessorOf1D: successor of 1.0 -define SuccessorOf101D: successor of 1.01 -define SuccessorOfJan12000: successor of DateTime(2000,1,1) -define SuccessorOfNoon: successor of @T12:00:00.000 -define SuccessorOverflowDt: successor of DateTime(9999, 12, 31, 23, 59, 59, 999) -define SuccessorOverflowT: successor of @T23:59:59.999 - -//Truncate -define TruncateNull: Truncate(null as Decimal) -define Truncate0: Truncate(0) -define Truncate0D0: Truncate(0.0) -define Truncate0D1: Truncate(0.1) -define Truncate1: Truncate(1) -define Truncate1D0: Truncate(1.0) -define Truncate1D1: Truncate(1.1) -define Truncate1D9: Truncate(1.9) -define TruncateNeg1: Truncate(-1) -define TruncateNeg1D0: Truncate(-1.0) -define TruncateNeg1D1: Truncate(-1.1) -define TruncateNeg1D9: Truncate(-1.9) - -//Truncated Divide -define TruncatedDivideNull: (null as Integer) div (null as Integer) -define TruncatedDivide2By1: 2 div 1 -define TruncatedDivide10By3: 10 div 3 -define TruncatedDivide10d1By3D1: 10.1 div 3.1 -define TruncatedDivideNeg2ByNeg1: -2 div -1 -define TruncatedDivideNeg10ByNeg3: -10 div -3 -define TruncatedDivideNeg10d1ByNeg3D1: -10.1 div -3.1 -define TruncatedDivideNeg2By1: -2 div 1 -define TruncatedDivideNeg10By3: -10 div 3 -define TruncatedDivideNeg10d1By3D1: -10.1 div 3.1 -define TruncatedDivide2ByNeg1: 2 div -1 -define TruncatedDivide10ByNeg3: 10 div -3 -define TruncatedDivide10d1ByNeg3D1: 10.1 div -3.1 -define TruncatedDivide10By5D: 10 div 5.0 -define TruncatedDivide10By5DQuantity: 10.0 'g' div 5.0 'g' -define TruncatedDivide414By206DQuantity: 4.14 'm' div 2.06 'm' -define TruncatedDivide10By0DQuantity: 10.0 'g' div 0.0 'g' \ No newline at end of file diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlComparisonOperatorsTest.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlComparisonOperatorsTest.cql deleted file mode 100644 index 3861b1f79..000000000 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlComparisonOperatorsTest.cql +++ /dev/null @@ -1,222 +0,0 @@ -library CqlComparisonOperatorsTest version '1' - -//Between -define BetweenIntTrue: 4 between 2 and 6 - -//Equal -define SimpleEqTrueTrue : true = true -define SimpleEqTrueFalse : true = false -define SimpleEqFalseFalse : false = false -define SimpleEqFalseTrue : false = true -define SimpleEqNullNull : null as String = null -define SimpleEqTrueNull : true = null -define SimpleEqNullTrue : null = true -define SimpleEqInt1Int1 : 1 = 1 -define SimpleEqInt1Int2 : 1 = 2 -define SimpleEqInt1Int2Long : 10L = 20L -define SimpleEqStringAStringA : 'a' = 'a' -define SimpleEqStringAStringB : 'a' = 'b' -define SimpleEqFloat1Float1 : 1.0 = 1.0 -define SimpleEqFloat1Float2 : 1.0 = 2.0 -define SimpleEqFloat1Float1WithZ : 1.0 = 1.00 -define SimpleEqFloat1Float1WithPrecisionAndZ : 1.50 = 1.55 -define SimpleEqFloat1Int1 : 1.0 = 1 -define SimpleEqFloat1Int2 : 1.0 = 2 -define QuantityEqCM1CM1 : 1'cm' = 1'cm' -define QuantityEqCM1M01 : 1'cm' = 0.01'm' -define QuantityEqDiffPrecision: 2.0'cm' = 2.00'cm' -define RatioEqual: 1'cm':2'cm' = 1'cm':2'cm' -define RatioNotEqual: 1'cm':2'cm' = 1.1'cm':2'cm' -define TupleEqJohnJohn: Tuple { Id : 1, Name : 'John' } = Tuple { Id : 1, Name : 'John' } -//define TupleEqJohnJohnFalse: Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } = Tuple { Id : 1, Name : 'John' } -//define TupleEqJohnJohnFalse2: Tuple { Id : 1, Name : 'John' } = Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } -define TupleEqJohnJane: Tuple { Id : 1, Name : 'John' } = Tuple { Id : 2, Name : 'Jane' } -define TupleEqJohn1John2: Tuple { Id : 1, Name : 'John' } = Tuple { Id : 2, Name : 'John' } -define TupleEqDateTimeTrue: Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 0, 0, 0, 0) } = Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 0, 0, 0, 0) } -define TupleEqDateTimeFalse: Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 0, 0, 0, 0) } = Tuple { dateId: 1, Date: DateTime(2012, 10, 5, 5, 0, 0, 0) } -define TupleEqTimeTrue: Tuple { timeId: 55, TheTime: @T05:15:15.541 } = Tuple { timeId: 55, TheTime: @T05:15:15.541 } -define TupleEqTimeFalse: Tuple { timeId: 55, TheTime: @T05:15:15.541 } = Tuple { timeId: 55, TheTime: @T05:15:15.540 } -define DateTimeEqTodayToday: Today() = Today() -define DateTimeEqTodayYesterday: Today() = Today() - 1 days -define DateTimeEqJanJan: DateTime(2014, 1, 5, 5, 0, 0, 0, 0) = DateTime(2014, 1, 5, 5, 0, 0, 0, 0) -define DateTimeEqJanJuly: DateTime(2014, 1, 5, 5, 0, 0, 0, 0) = DateTime(2014, 7, 5, 5, 0, 0, 0, 0) -define DateTimeEqNull: DateTime(null) = DateTime(null) -define DateTimeUTC: @2014-01-25T14:30:14.559+01:00 = @2014-01-25T14:30:14.559+01:00 -define TimeEq10A10A: @T10:00:00.000 = @T10:00:00.000 -define TimeEq10A10P: @T10:00:00.000 = @T22:00:00.000 - -//Greater -define GreaterZZ : 0 > 0 -define GreaterZ1 : 0 > 1 -define GreaterLong : 00L > 10L -define GreaterZNeg1 : 0 > -1 -define GreaterDecZZ : 0.0 > 0.0 -define GreaterDecZ1 : 0.0 > 1.0 -define GreaterDecZNeg1 : 0.0 > -1.0 -define GreaterCM0CM0 : 0'cm' > 0'cm' -define GreaterCM0CM1 : 0'cm' > 1'cm' -define GreaterCM0NegCM1 : 0'cm' > -1'cm' -define GreaterM1CM1 : 1'm' > 1'cm' -define GreaterM1CM10 : 1'm' > 10'cm' -define GreaterAA : 'a' > 'a' -define GreaterAB : 'a' > 'b' -define GreaterBA : 'b' > 'a' -define GreaterAThanAA : 'a' > 'aa' -define GreaterAAThanA : 'aa' > 'a' -define GreaterJackJill : 'Jack' > 'Jill' -define DateTimeGreaterTrue: DateTime(2012, 2, 12) > DateTime(2012, 2, 10) -define DateTimeGreaterFalse: DateTime(2012, 2, 12) > DateTime(2012, 2, 13) -define TimeGreaterTrue: @T10:00:00.001 > @T10:00:00.000 -define TimeGreaterFalse: @T10:00:00.000 > @T10:00:00.001 -define UncertaintyGreaterNull: DateTime(2014) > DateTime(2014, 2, 15) -define UncertaintyGreaterTrue: DateTime(2015) > DateTime(2014, 2, 15) -define UncertaintyGreaterFalse: DateTime(2013) > DateTime(2014, 2, 15) - -//Greater Or Equal -define GreaterOrEqualZZ : 0 >= 0 -define GreaterOrEqualZ1 : 0 >= 1 -define GreaterOrEqualZ1Long : 00L >= 10L -define GreaterOrEqualZNeg1 : 0 >= -1 -define GreaterOrEqualDecZZ : 0.0 >= 0.0 -define GreaterOrEqualDecZ1 : 0.0 >= 1.0 -define GreaterOrEqualDecZNeg1 : 0.0 >= -1.0 -define GreaterOrEqualCM0CM0 : 0'cm' >= 0'cm' -define GreaterOrEqualCM0CM1 : 0'cm' >= 1'cm' -define GreaterOrEqualCM0NegCM1 : 0'cm' >= -1'cm' -define GreaterOrEqualM1CM1 : 1'm' >= 1'cm' -define GreaterOrEqualM1CM10 : 1'm' >= 10'cm' -define GreaterOrEqualAA : 'a' >= 'a' -define GreaterOrEqualAB : 'a' >= 'b' -define GreaterOrEqualBA : 'b' >= 'a' -define GreaterOrEqualAThanAA : 'a' >= 'aa' -define GreaterOrEqualAAThanA : 'aa' >= 'a' -define GreaterOrEqualJackJill : 'Jack' >= 'Jill' -define DateTimeGreaterEqTrue: DateTime(2012, 2, 12, 0, 0, 0, 0) >= DateTime(2012, 2, 10, 0, 0, 0, 0) -define DateTimeGreaterEqTrue2: DateTime(2012, 2, 12, 0, 0, 0, 0) >= DateTime(2012, 2, 12, 0, 0, 0, 0) -define DateTimeGreaterEqFalse: DateTime(2012, 2, 12, 0, 0, 0, 0) >= DateTime(2012, 2, 13, 0, 0, 0, 0) -define TimeGreaterEqTrue: @T10:00:00.001 >= @T10:00:00.000 -define TimeGreaterEqTrue2: @T10:00:00.000 >= @T10:00:00.000 -define TimeGreaterEqFalse: @T10:00:00.000 >= @T10:00:00.001 -define UncertaintyGreaterEqualNull: DateTime(2014) >= DateTime(2014, 2, 15) -define UncertaintyGreaterEqualTrue: DateTime(2015) >= DateTime(2014, 2, 15) -define UncertaintyGreaterEqualFalse: DateTime(2013) >= DateTime(2014, 2, 15) - -//Less -define LessZZ : 0 < 0 -define LessZ1 : 0 < 1 -define LessLong : 00L < 10L -define LessLongNeg : -30L < -20L -define LessZNeg1 : 0 < -1 -define LessDecZZ : 0.0 < 0.0 -define LessDecZ1 : 0.0 < 1.0 -define LessDecZNeg1 : 0.0 < -1.0 -define LessCM0CM0 : 0'cm' < 0'cm' -define LessCM0CM1 : 0'cm' < 1'cm' -define LessCM0NegCM1 : 0'cm' < -1'cm' -define LessM1CM1 : 1'm' < 1'cm' -define LessM1CM10 : 1'm' < 10'cm' -define LessAA : 'a' < 'a' -define LessAB : 'a' < 'b' -define LessBA : 'b' < 'a' -define LessAThanAA : 'a' < 'aa' -define LessAAThanA : 'aa' < 'a' -define LessJackJill : 'Jack' < 'Jill' -define DateTimeLessTrue: DateTime(2012, 2, 9) < DateTime(2012, 2, 10) -define DateTimeLessFalse: DateTime(2012, 2, 14) < DateTime(2012, 2, 13) -define TimeLessTrue: @T10:00:00.001 < @T10:00:00.002 -define TimeLessFalse: @T10:10:00.000 < @T10:00:00.001 -define UncertaintyLessNull: DateTime(2014) < DateTime(2014, 2, 15) -define UncertaintyLessTrue: DateTime(2013) < DateTime(2014, 2, 15) -define UncertaintyLessFalse: DateTime(2015) < DateTime(2014, 2, 15) - -//Less Or Equal -define LessOrEqualZZ : 0 <= 0 -define LessOrEqualZ1 : 0 <= 1 -define LessOrEqualZ1Long : 00L <= 10L -define LessOrEqualZNeg1 : 0 <= -1 -define LessOrEqualDecZZ : 0.0 <= 0.0 -define LessOrEqualDecZ1 : 0.0 <= 1.0 -define LessOrEqualDecZNeg1 : 0.0 <= -1.0 -define LessOrEqualCM0CM0 : 0'cm' <= 0'cm' -define LessOrEqualCM0CM1 : 0'cm' <= 1'cm' -define LessOrEqualCM0NegCM1 : 0'cm' <= -1'cm' -define LessOrEqualM1CM1 : 1'm' <= 1'cm' -define LessOrEqualM1CM10 : 1'm' <= 10'cm' -define LessOrEqualAA : 'a' <= 'a' -define LessOrEqualAB : 'a' <= 'b' -define LessOrEqualBA : 'b' <= 'a' -define LessOrEqualAThanAA : 'a' <= 'aa' -define LessOrEqualAAThanA : 'aa' <= 'a' -define LessOrEqualJackJill : 'Jack' <= 'Jill' -define DateTimeLessEqTrue: DateTime(2012, 2, 9, 0, 0, 0, 0) <= DateTime(2012, 2, 10, 0, 0, 0, 0) -define DateTimeLessEqTrue2: DateTime(2012, 2, 12, 0, 0, 0, 0) <= DateTime(2012, 2, 12, 0, 0, 0, 0) -define DateTimeLessEqFalse: DateTime(2012, 2, 12, 1, 0, 0, 0) <= DateTime(2012, 2, 12, 0, 0, 0, 0) -define TimeLessEqTrue: @T10:00:00.001 <= @T10:00:00.002 -define TimeLessEqTrue2: @T10:00:00.000 <= @T10:00:00.000 -define TimeLessEqFalse: @T10:00:00.002 <= @T10:00:00.001 -define UncertaintyLessEqualNull: DateTime(2014) <= DateTime(2014, 2, 15) -define UncertaintyLessEqualTrue: DateTime(2013) <= DateTime(2014, 2, 15) -define UncertaintyLessEqualFalse: DateTime(2015) <= DateTime(2014, 2, 15) - -//Equivalent -define EquivTrueTrue : true ~ true -define EquivTrueFalse : true ~ false -define EquivFalseFalse : false ~ false -define EquivFalseTrue : false ~ true -define EquivNullNull : null as String ~ null -define EquivTrueNull : true ~ null -define EquivNullTrue : null ~ true -define EquivInt1Int1 : 1 ~ 1 -define EquivInt1Int2 : 1 ~ 2 -define EquivStringAStringA : 'a' ~ 'a' -define EquivStringAStringB : 'a' ~ 'b' -define EquivStringIgnoreCase : 'Abel' ~ 'abel' -define EquivFloat1Float1 : 1.0 ~ 1.0 -define EquivFloat1Float2 : 1.0 ~ 2.0 -define EquivFloat1Float1WithZ : 1.0 ~ 1.00 -define EquivFloat1Float1WithPrecision : 1.5 ~ 1.55 -define EquivFloat1Float1WithPrecisionAndZ : 1.50 ~ 1.55 -define EquivFloatTrailingZero : 1.001 ~ 1.000 -define EquivFloat1Int1 : 1.0 ~ 1 -define EquivFloat1Int2 : 1.0 ~ 2 -define EquivEqCM1CM1 : 1'cm' ~ 1'cm' -define EquivEqCM1M01 : 1'cm' ~ 0.01'm' -define RatioEquivalent : 1'cm':2'cm' ~ 1'cm':2'cm' -define RatioNotEquivalent : 1'cm':2'cm' ~ 1'cm':3'cm' -define EquivTupleJohnJohn : Tuple { Id : 1, Name : 'John' } ~ Tuple { Id : 1, Name : 'John' } -define EquivTupleJohnJohnWithNulls : Tuple { Id : 1, Name : 'John', Position: null } ~ Tuple { Id : 1, Name : 'John', Position: null } -define EquivTupleJohnJohnFalse : Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } ~ Tuple { Id : 1, Name : 'John' } -define EquivTupleJohnJohnFalse2 : Tuple { Id : 1, Name : 'John' } ~ Tuple { Id : 1, Name : 'John', Position: 'Shift Manager' } -define EquivTupleJohnJane : Tuple { Id : 1, Name : 'John' } ~ Tuple { Id : 2, Name : 'Jane' } -define EquivTupleJohn1John2 : Tuple { Id : 1, Name : 'John' } ~ Tuple { Id : 2, Name : 'John' } -define EquivDateTimeTodayToday : Today() ~ Today() -define EquivDateTimeTodayYesterday : Today() ~ Today() - 1 days -define EquivTime10A10A : @T10:00:00.000 ~ @T10:00:00.000 -define EquivTime10A10P : @T10:00:00.000 ~ @T22:00:00.000 - -//Not Equal -define SimpleNotEqTrueTrue : true != true -define SimpleNotEqTrueFalse : true != false -define SimpleNotEqFalseFalse : false != false -define SimpleNotEqFalseTrue : false != true -define SimpleNotEqNullNull : null as String != null -define SimpleNotEqTrueNull : true != null -define SimpleNotEqNullTrue : null != true -define SimpleNotEqInt1Int1 : 1 != 1 -define SimpleNotEqInt1Int2 : 1 != 2 -define SimpleNotEqInt1Int2Long : 10L != 20L -define SimpleNotEqStringAStringA : 'a' != 'a' -define SimpleNotEqStringAStringB : 'a' != 'b' -define SimpleNotEqFloat1Float1 : 1.0 != 1.0 -define SimpleNotEqFloat1Float2 : 1.0 != 2.0 -define SimpleNotEqFloat1Int1 : 1.0 != 1 -define SimpleNotEqFloat1Int2 : 1.0 != 2 -define QuantityNotEqCM1CM1 : 1'cm' != 1'cm' -define QuantityNotEqCM1M01 : 1'cm' != 0.01'm' -define TupleNotEqJohnJohn: Tuple{ Id : 1, Name : 'John' } != Tuple{ Id : 1, Name : 'John' } -define TupleNotEqJohnJane: Tuple{ Id : 1, Name : 'John' } != Tuple{ Id : 2, Name : 'Jane' } -define TupleNotEqJohn1John2: Tuple{ Id : 1, Name : 'John' } != Tuple{ Id : 2, Name : 'John' } -define DateTimeNotEqTodayToday: Today() != Today() -define DateTimeNotEqTodayYesterday: Today() != Today() - 1 days -define TimeNotEq10A10A: @T10:00:00.000 != @T10:00:00.000 -define TimeNotEq10A10P: @T10:00:00.000 != @T22:00:00.000 diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlConditionalOperatorsTest.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlConditionalOperatorsTest.cql deleted file mode 100644 index a988783a5..000000000 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlConditionalOperatorsTest.cql +++ /dev/null @@ -1,59 +0,0 @@ -library CqlConditionalOperatorsTest - -define x: 5 -define y: 10 -define z: null - -// if-then-else -define IfTrue1: - if y > x then x else y - -define IfFalse1: - if y = x then y + x else y - x - -define IfNull1: - if y = z then x else y - -// standard case -define StandardCase1: - case - when y > x then x - when x > y then y - else z - end - -define StandardCase2: - case - when x > y then x + y - when x = y then x - else y - x - end - -define StandardCase3: - case - when z ~ y then z + y - when z ~ x then x - else x + y - end - -// selected case -define SelectedCase1: - case x - when 5 then 12 - when 10 then y + x - else y - x - end - -define SelectedCase2: - case y - when 5 then 12 - when 10 then y + x - else y - x - end - -define SelectedCase3: - case y + x - when 5 then 12 - when 10 then y + x - else y - x - end diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.cql index 3c1660bea..57f5bdb84 100644 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.cql +++ b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.cql @@ -1,354 +1,26 @@ library CqlDateTimeOperatorsTest -//Add -define DateTimeAdd5Years: DateTime(2005, 10, 10) + 5 years -define DateTimeAddInvalidYears: DateTime(2005, 10, 10) + 8000 years -define DateTimeAdd5Months: DateTime(2005, 5, 10) + 5 months -define DateTimeAddMonthsOverflow: DateTime(2005, 5, 10) + 10 months -define DateTimeAddThreeWeeks: DateTime(2018, 5, 2) + 3 weeks -define DateTimeAddYearInWeeks: DateTime(2018, 5, 23) + 52 weeks -define DateTimeAdd5Days: DateTime(2005, 5, 10) + 5 days -define DateTimeAddDaysOverflow: DateTime(2016, 6, 10) + 21 days -define DateTimeAdd5Hours: DateTime(2005, 5, 10, 5) + 5 hours -define DateTimeAdd5HoursWithLeftMinPrecisionSecond: DateTime(2005, 5, 10, 5, 20, 30) + 5 hours -define DateTimeAdd5HoursWithLeftMinPrecisionDay: DateTime(2005, 5, 10) + 5 hours -define DateTimeAdd5HoursWithLeftMinPrecisionDayOverflow: DateTime(2005, 5, 10) + 25 hours -define DateAdd2YearsAsMonths: Date(2014) + 24 months -define DateAdd2YearsAsMonthsRem1: Date(2014) + 25 months -define DateAdd33Days: Date(2014,6) + 33 days -define DateAdd1Year: Date(2014,6) + 1 year -define DateTimeAddHoursOverflow: DateTime(2016, 6, 10, 5) + 19 hours -define DateTimeAdd5Minutes: DateTime(2005, 5, 10, 5, 5) + 5 minutes -define DateTimeAddMinutesOverflow: DateTime(2016, 6, 10, 5, 5) + 55 minutes -define DateTimeAdd5Seconds: DateTime(2005, 5, 10, 5, 5, 5) + 5 seconds -define DateTimeAddSecondsOverflow: DateTime(2016, 6, 10, 5, 5, 5) + 55 seconds -define DateTimeAdd5Milliseconds: DateTime(2005, 5, 10, 5, 5, 5, 5) + 5 milliseconds -define DateTimeAddMillisecondsOverflow: DateTime(2016, 6, 10, 5, 5, 5, 5) + 995 milliseconds -define DateTimeAddLeapYear: DateTime(2012, 2, 29) + 1 year -define DateTimeAdd2YearsByMonths: DateTime(2014) + 24 months -define DateTimeAdd2YearsByDays: DateTime(2014) + 730 days -define DateTimeAdd2YearsByDaysRem5Days: DateTime(2014) + 735 days -define TimeAdd5Hours: @T15:59:59.999 + 5 hours -define TimeAdd1Minute: @T15:59:59.999 + 1 minute -define TimeAdd1Second: @T15:59:59.999 + 1 seconds -define TimeAdd1Millisecond: @T15:59:59.999 + 1 milliseconds -define TimeAdd5Hours1Minute: @T15:59:59.999 + 5 hours + 1 minutes -define TimeAdd5hoursByMinute: @T15:59:59.999 + 300 minutes - -//After -define DateTimeAfterYearTrue: DateTime(2005, 10, 10) after year of DateTime(2004, 10, 10) -define DateTimeAfterYearFalse: DateTime(2004, 11, 10) after year of DateTime(2004, 10, 10) -define DateTimeAfterMonthTrue: DateTime(2004, 12, 10) after month of DateTime(2004, 11, 10) -define DateTimeAfterMonthFalse: DateTime(2004, 9, 10) after month of DateTime(2004, 10, 10) -define DateTimeAfterDayTrue: DateTime(2004, 12, 11) after day of DateTime(2004, 10, 10) -define DateTimeAfterDayTrue2: DateTime(2004, 12, 09) after day of DateTime(2003, 10, 10) -define DateTimeAfterDayFalse: DateTime(2004, 10, 9) after day of DateTime(2004, 10, 10) -define DateTimeAfterHourTrue: DateTime(2004, 10, 10, 10) after hour of DateTime(2004, 10, 10, 5) -define DateTimeAfterHourFalse: DateTime(2004, 10, 10, 20) after hour of DateTime(2004, 10, 10, 21) -define DateTimeAfterMinuteTrue: DateTime(2004, 10, 10, 20, 30) after minute of DateTime(2004, 10, 10, 20, 29) -define DateTimeAfterMinuteFalse: DateTime(2004, 10, 10, 20, 30) after minute of DateTime(2004, 10, 10, 20, 31) -define DateTimeAfterSecondTrue: DateTime(2004, 10, 10, 20, 30, 15) after second of DateTime(2004, 10, 10, 20, 30, 14) -define DateTimeAfterSecondFalse: DateTime(2004, 10, 10, 20, 30, 15) after second of DateTime(2004, 10, 10, 20, 30, 16) -define DateTimeAfterMillisecondTrue: DateTime(2004, 10, 10, 20, 30, 15, 512) after millisecond of DateTime(2004, 10, 10, 20, 30, 15, 510) -define DateTimeAfterMillisecondFalse: DateTime(2004, 10, 10, 20, 30, 15, 512) after millisecond of DateTime(2004, 10, 10, 20, 30, 15, 513) -define DateTimeAfterUncertain: DateTime(2005, 10, 10) after day of DateTime(2005, 9) -define AfterTimezoneTrue: @2012-03-10T10:20:00.999+07:00 after hour of @2012-03-10T08:20:00.999+06:00 -define AfterTimezoneFalse: @2012-03-10T10:20:00.999+07:00 after hour of @2012-03-10T10:20:00.999+06:00 -define TimeAfterHourTrue: @T15:59:59.999 after hour of @T14:59:59.999 -define TimeAfterHourFalse: @T15:59:59.999 after hour of @T16:59:59.999 -define TimeAfterMinuteTrue: @T15:59:59.999 after minute of @T15:58:59.999 -define TimeAfterMinuteFalse: @T15:58:59.999 after minute of @T15:59:59.999 -define TimeAfterSecondTrue: @T15:59:59.999 after second of @T15:59:58.999 -define TimeAfterSecondFalse: @T15:59:58.999 after second of @T15:59:59.999 -define TimeAfterMillisecondTrue: @T15:59:59.999 after millisecond of @T15:59:59.998 -define TimeAfterMillisecondFalse: @T15:59:59.998 after millisecond of @T15:59:59.999 -define TimeAfterTimeCstor: Time(12, 30) after hour of Time(11, 55) -// TODO: do uncertainty tests for Time once the Time(x,x,x,x,x) format has been implemented - -//Before -define DateTimeBeforeYearTrue: DateTime(2003) before year of DateTime(2004, 10, 10) -define DateTimeBeforeYearFalse: DateTime(2004, 11, 10) before year of DateTime(2003, 10, 10) -define DateTimeBeforeMonthTrue: DateTime(2004, 10, 10) before month of DateTime(2004, 11, 10) -define DateTimeBeforeMonthFalse: DateTime(2004, 11, 10) before month of DateTime(2004, 10, 10) -define DateTimeBeforeDayTrue: DateTime(2004, 10, 1) before day of DateTime(2004, 10, 10) -define DateTimeBeforeDayTrue2: DateTime(2003, 10, 11) before day of DateTime(2004, 10, 10) -define DateTimeBeforeDayFalse: DateTime(2004, 10, 11) before day of DateTime(2004, 10, 10) -define DateTimeBeforeHourTrue: DateTime(2004, 10, 10, 1) before hour of DateTime(2004, 10, 10, 5) -define DateTimeBeforeHourFalse: DateTime(2004, 10, 10, 23) before hour of DateTime(2004, 10, 10, 21) -define DateTimeBeforeMinuteTrue: DateTime(2004, 10, 10, 20, 28) before minute of DateTime(2004, 10, 10, 20, 29) -define DateTimeBeforeMinuteFalse: DateTime(2004, 10, 10, 20, 35) before minute of DateTime(2004, 10, 10, 20, 31) -define DateTimeBeforeSecondTrue: DateTime(2004, 10, 10, 20, 30, 12) before second of DateTime(2004, 10, 10, 20, 30, 14) -define DateTimeBeforeSecondFalse: DateTime(2004, 10, 10, 20, 30, 55) before second of DateTime(2004, 10, 10, 20, 30, 16) -define DateTimeBeforeMillisecondTrue: DateTime(2004, 10, 10, 20, 30, 15, 508) before millisecond of DateTime(2004, 10, 10, 20, 30, 15, 510) -define DateTimeBeforeMillisecondFalse: DateTime(2004, 10, 10, 20, 30, 15, 599) before millisecond of DateTime(2004, 10, 10, 20, 30, 15, 513) -define BeforeTimezoneTrue: @2012-03-10T10:20:00.999+07:00 before hour of @2012-03-10T10:20:00.999+06:00 -define BeforeTimezoneFalse: @2012-03-10T10:20:00.999+07:00 before hour of @2012-03-10T09:20:00.999+06:00 -define TimeBeforeHourTrue: @T13:59:59.999 before hour of @T14:59:59.999 -define TimeBeforeHourFalse: @T16:59:59.999 before hour of @T15:59:59.999 -define TimeBeforeMinuteTrue: @T15:57:59.999 before minute of @T15:58:59.999 -define TimeBeforeMinuteFalse: @T15:59:59.999 before minute of @T15:59:59.999 -define TimeBeforeSecondTrue: @T15:59:57.999 before second of @T15:59:58.999 -define TimeBeforeSecondFalse: @T15:59:56.999 before second of @T15:59:55.999 -define TimeBeforeMillisecondTrue: @T15:59:59.997 before millisecond of @T15:59:59.998 -define TimeBeforeMillisecondFalse: @T15:59:59.998 before millisecond of @T15:59:59.997 -// TODO: do uncertainty tests for Time once the Time(x,x,x,x,x) format has been implemented - -//DateTime -define DateTimeYear: DateTime(2003) -define DateTimeMonth: DateTime(2003, 10) -define DateTimeDay: DateTime(2003, 10, 29) -define DateTimeHour: DateTime(2003, 10, 29, 20) -define DateTimeMinute: DateTime(2003, 10, 29, 20, 50) -define DateTimeSecond: DateTime(2003, 10, 29, 20, 50, 33) -define DateTimeMillisecond: DateTime(2003, 10, 29, 20, 50, 33, 955) - -//DateTimeComponentFrom -define DateTimeComponentFromYear: year from DateTime(2003, 10, 29, 20, 50, 33, 955) -define DateTimeComponentFromMonth: month from DateTime(2003, 10, 29, 20, 50, 33, 955) -define DateTimeComponentFromMonthMinBoundary: month from DateTime(2003, 01, 29, 20, 50, 33, 955) -define DateTimeComponentFromDay: day from DateTime(2003, 10, 29, 20, 50, 33, 955) -define DateTimeComponentFromHour: hour from DateTime(2003, 10, 29, 20, 50, 33, 955) -define DateTimeComponentFromMinute: minute from DateTime(2003, 10, 29, 20, 50, 33, 955) -define DateTimeComponentFromSecond: second from DateTime(2003, 10, 29, 20, 50, 33, 955) -define DateTimeComponentFromMillisecond: millisecond from DateTime(2003, 10, 29, 20, 50, 33, 955) -define DateTimeComponentFromTimezone: timezoneoffset from DateTime(2003, 10, 29, 20, 50, 33, 955, 1) -define DateTimeComponentFromDate: date from DateTime(2003, 10, 29, 20, 50, 33, 955, 1) -define TimeComponentFromHour: hour from @T23:20:15.555 -define TimeComponentFromMinute: minute from @T23:20:15.555 -define TimeComponentFromSecond: second from @T23:20:15.555 -define TimeComponentFromMilli: millisecond from @T23:20:15.555 - -//Difference -define DateTimeDifferenceYear: difference in years between DateTime(2000) and DateTime(2005, 12) -define DateTimeDifferenceMonth: difference in months between DateTime(2000, 2) and DateTime(2000, 10) -define DateTimeDifferenceDay: difference in days between DateTime(2000, 10, 15, 10, 30) and DateTime(2000, 10, 25, 10, 0) -define DateTimeDifferenceHour: difference in hours between DateTime(2000, 4, 1, 12) and DateTime(2000, 4, 1, 20) -define DateTimeDifferenceMinute: difference in minutes between DateTime(2005, 12, 10, 5, 16) and DateTime(2005, 12, 10, 5, 25) -define DateTimeDifferenceSecond: difference in seconds between DateTime(2000, 10, 10, 10, 5, 45) and DateTime(2000, 10, 10, 10, 5, 50) -define DateTimeDifferenceMillisecond: difference in milliseconds between DateTime(2000, 10, 10, 10, 5, 45, 500, -6.0) and DateTime(2000, 10, 10, 10, 5, 45, 900, -7.0) -define DateTimeDifferenceWeeks: difference in weeks between DateTime(2000, 10, 15) and DateTime(2000, 10, 28) -define DateTimeDifferenceWeeks2: difference in weeks between DateTime(2000, 10, 15) and DateTime(2000, 10, 29) -define DateTimeDifferenceWeeks3: difference in weeks between @2012-03-10T22:05:09 and @2012-03-24T07:19:33 -define DateTimeDifferenceNegative: difference in years between DateTime(2016) and DateTime(1998) -define DateTimeDifferenceUncertain: difference in months between DateTime(2005) and DateTime(2006, 7) > 5 -define TimeDifferenceHour: difference in hours between @T20 and @T23:25:15.555 -define TimeDifferenceMinute: difference in minutes between @T20:20:15.555 and @T20:25:15.555 -define TimeDifferenceSecond: difference in seconds between @T20:20:15.555 and @T20:20:20.555 -define TimeDifferenceMillis: difference in milliseconds between @T20:20:15.555 and @T20:20:15.550 - -// From Github issue #29 -define DateTimeA: @2017-03-12T01:00:00-07:00 // March 12, 2017, 1:00 AM MST -define DateTimeAA: DateTime(2017, 3, 12, 1, 0, 0, 0, -7.0) -define DateTimeB: @2017-03-12T03:00:00-06:00 // 3:00 AM MDT -define DateTimeBB: DateTime(2017, 3, 12, 3, 0, 0, 0, -6.0) -define DateTimeC: @2017-11-05T01:30:00-06:00 // November 5, 2017, 1:30 AM MDT -define DateTimeCC: DateTime(2017, 11, 5, 1, 30, 0, 0, -6.0) -define DateTimeD: @2017-11-05T01:15:00-07:00 // November 5, 2017, 1:15 AM MST -define DateTimeDD: DateTime(2017, 11, 5, 1, 15, 0, 0, -7.0) -define DateTimeE: @2017-03-12T00:00:00-07:00 // March 12, 2017 -define DateTimeEE: DateTime(2017, 3, 12, 0, 0, 0, 0, -7.0) -define DateTimeF: @2017-03-13T00:00:00-06:00 // March 13, 2017 -define DateTimeFF: DateTime(2017, 3, 13, 0, 0, 0, 0, -6.0) - -define DifferenceInHoursA: difference in hours between DateTimeA and DateTimeB // Should be 1 -define DifferenceInMinutesA: difference in minutes between DateTimeC and DateTimeD // Should be 45 -define DifferenceInDaysA: difference in days between DateTimeE and DateTimeF // Should be 1 -define DifferenceInHoursAA: difference in hours between DateTimeAA and DateTimeBB // Should be 1 -define DifferenceInMinutesAA: difference in minutes between DateTimeCC and DateTimeDD // Should be 45 -define DifferenceInDaysAA: difference in days between DateTimeEE and DateTimeFF // Should be 1 -// TODO: Time uncertainty tests - //Duration define DateTimeDurationBetweenYear: years between DateTime(2005) and DateTime(2010) -define DateTimeDurationBetweenYearOffset: years between DateTime(2005, 5) and DateTime(2010, 4) -define DateTimeDurationBetweenMonth: months between DateTime(2014, 1, 31) and DateTime(2014, 2, 1) -define DateTimeDurationBetweenDaysDiffYears: days between DateTime(2010, 10, 12, 12, 5) and DateTime(2008, 8, 15, 8, 8) + // Uncertainty tests define DateTimeDurationBetweenUncertainInterval: days between DateTime(2014, 1, 15) and DateTime(2014, 2) define DateTimeDurationBetweenUncertainInterval2: months between DateTime(2005) and DateTime(2006, 5) define DateTimeDurationBetweenUncertainAdd: DateTimeDurationBetweenUncertainInterval + DateTimeDurationBetweenUncertainInterval define DateTimeDurationBetweenUncertainSubtract: DateTimeDurationBetweenUncertainInterval - DateTimeDurationBetweenUncertainInterval2 define DateTimeDurationBetweenUncertainMultiply: DateTimeDurationBetweenUncertainInterval * DateTimeDurationBetweenUncertainInterval -define DateTimeDurationBetweenUncertainDiv: DateTimeDurationBetweenUncertainInterval div DateTimeDurationBetweenUncertainInterval2 -define DateTimeDurationBetweenMonthUncertain: months between DateTime(2005) and DateTime(2006, 7) > 5 -define DateTimeDurationBetweenMonthUncertain2: months between DateTime(2005) and DateTime(2006, 2) > 5 -define DateTimeDurationBetweenMonthUncertain3: months between DateTime(2005) and DateTime(2006, 7) > 25 -define DateTimeDurationBetweenMonthUncertain4: months between DateTime(2005) and DateTime(2006, 7) < 24 -define DateTimeDurationBetweenMonthUncertain5: months between DateTime(2005) and DateTime(2006, 7) = 24 -define DateTimeDurationBetweenMonthUncertain6: months between DateTime(2005) and DateTime(2006, 7) >= 5 -define DateTimeDurationBetweenMonthUncertain7: months between DateTime(2005) and DateTime(2006, 7) <= 24 -define DateTime1: @2012-03-10T10:20:00 -define DateTime2: @2013-03-10T09:20:00 -define DurationInYears: years between (date from DateTime1) and (date from DateTime2) +define DurationInDaysA: days between @2017-03-12T00:00:00-07:00 and @2017-03-13T00:00:00-06:00 // Should be 1 +define DurationInDaysAA: days between DateTime(2017, 3, 12, 0, 0, 0, 0, -7.0) and DateTime(2017, 3, 13, 0, 0, 0, 0, -6.0) // Should be 1 -define DurationInWeeks: weeks between @2012-03-10T22:05:09 and @2012-03-20T07:19:33 -define DurationInWeeks2: weeks between @2012-03-10T22:05:09 and @2012-03-24T07:19:33 -define DurationInWeeks3: weeks between @2012-03-10T06:05:09 and @2012-03-24T07:19:33 - -define TimeDurationBetweenHour: hours between @T20:26:15.555 and @T23:25:15.555 -define TimeDurationBetweenHourDiffPrecision: hours between @T06 and @T07:00:00 -define TimeDurationBetweenMinute: minutes between @T23:20:16.555 and @T23:25:15.555 -define TimeDurationBetweenSecond: seconds between @T23:25:10.556 and @T23:25:15.555 -define TimeDurationBetweenMillis: milliseconds between @T23:25:25.555 and @T23:25:25.560 -// TODO: Time uncertainty tests - -define DurationInHoursA: hours between DateTimeA and DateTimeB // Should be 1 -define DurationInMinutesA: minutes between DateTimeC and DateTimeD // Should be 45 -define DurationInDaysA: days between DateTimeE and DateTimeF // Should be 1 -define DurationInHoursAA: hours between DateTimeAA and DateTimeBB // Should be 1 -define DurationInMinutesAA: minutes between DateTimeCC and DateTimeDD // Should be 45 -define DurationInDaysAA: days between DateTimeEE and DateTimeFF // Should be 1 //Now -define DateTimeNow: Now() = Now() define Issue34A: Now() -//SameAs -define DateTimeSameAsYearTrue: DateTime(2014) same year as DateTime(2014) -define DateTimeSameAsYearFalse: DateTime(2013) same year as DateTime(2014) -define DateTimeSameAsMonthTrue: DateTime(2014, 12) same month as DateTime(2014, 12) -define DateTimeSameAsMonthFalse: DateTime(2014, 12) same month as DateTime(2014, 10) -define DateTimeSameAsDayTrue: DateTime(2014, 12, 10) same day as DateTime(2014, 12, 10) -define DateTimeSameAsDayFalse: DateTime(2014, 10, 10) same day as DateTime(2014, 10, 11) -define DateTimeSameAsHourTrue: DateTime(2014, 12, 10, 20) same hour as DateTime(2014, 12, 10, 20) -define DateTimeSameAsHourFalse: DateTime(2014, 10, 10, 20) same hour as DateTime(2014, 10, 10, 21) -define DateTimeSameAsMinuteTrue: DateTime(2014, 12, 10, 20, 55) same minute as DateTime(2014, 12, 10, 20, 55) -define DateTimeSameAsMinuteFalse: DateTime(2014, 10, 10, 20, 55) same minute as DateTime(2014, 10, 10, 21, 56) -define DateTimeSameAsSecondTrue: DateTime(2014, 12, 10, 20, 55, 45) same second as DateTime(2014, 12, 10, 20, 55, 45) -define DateTimeSameAsSecondFalse: DateTime(2014, 10, 10, 20, 55, 45) same second as DateTime(2014, 10, 10, 21, 55, 44) -define DateTimeSameAsMillisecondTrue: DateTime(2014, 12, 10, 20, 55, 45, 500) same millisecond as DateTime(2014, 12, 10, 20, 55, 45, 500) -define DateTimeSameAsMillisecondFalse: DateTime(2014, 10, 10, 20, 55, 45, 500) same millisecond as DateTime(2014, 10, 10, 21, 55, 45, 501) -define DateTimeSameAsNull: DateTime(2014, 10) same day as DateTime(2014, 10, 12) -define SameAsTimezoneTrue: @2012-03-10T10:20:00.999+07:00 same hour as @2012-03-10T09:20:00.999+06:00 -define SameAsTimezoneFalse: @2012-03-10T10:20:00.999+07:00 same hour as @2012-03-10T10:20:00.999+06:00 -define TimeSameAsHourTrue: @T23:25:25.555 same hour as @T23:55:25.900 -define TimeSameAsHourFalse: @T22:25:25.555 same hour as @T23:25:25.555 -define TimeSameAsMinuteTrue: @T23:55:22.555 same minute as @T23:55:25.900 -define TimeSameAsMinuteFalse: @T23:26:25.555 same minute as @T23:25:25.555 -define TimeSameAsSecondTrue: @T23:55:25.555 same second as @T23:55:25.900 -define TimeSameAsSecondFalse: @T23:25:35.555 same second as @T23:25:25.555 -define TimeSameAsMillisTrue: @T23:55:25.555 same millisecond as @T23:55:25.555 -define TimeSameAsMillisFalse: @T23:25:25.555 same millisecond as @T23:25:25.554 - -//SameOrAfter -define DateTimeSameOrAfterYearTrue1: DateTime(2014) same year or after DateTime(2014) -define DateTimeSameOrAfterYearTrue2: DateTime(2016) same year or after DateTime(2014) -define DateTimeSameOrAfterYearFalse: DateTime(2013) same year or after DateTime(2014) -define DateTimeSameOrAfterMonthTrue1: DateTime(2014, 12) same month or after DateTime(2014, 12) -define DateTimeSameOrAfterMonthTrue2: DateTime(2014, 10) same month or after DateTime(2014, 9) -define DateTimeSameOrAfterMonthFalse: DateTime(2014, 10) same month or after DateTime(2014, 11) -define DateTimeSameOrAfterDayTrue1: DateTime(2014, 12, 20) same day or after DateTime(2014, 12, 20) -define DateTimeSameOrAfterDayTrue2: DateTime(2014, 10, 25) same day or after DateTime(2014, 10, 20) -define DateTimeSameOrAfterDayFalse: DateTime(2014, 10, 20) same day or after DateTime(2014, 10, 25) -define DateTimeSameOrAfterHourTrue1: DateTime(2014, 12, 20, 12) same hour or after DateTime(2014, 12, 20, 12) -define DateTimeSameOrAfterHourTrue2: DateTime(2014, 10, 25, 12) same hour or after DateTime(2014, 10, 25, 10) -define DateTimeSameOrAfterHourFalse: DateTime(2014, 10, 25, 12) same hour or after DateTime(2014, 10, 25, 15) -define DateTimeSameOrAfterMinuteTrue1: DateTime(2014, 12, 20, 12, 30) same minute or after DateTime(2014, 12, 20, 12, 30) -define DateTimeSameOrAfterMinuteTrue2: DateTime(2014, 10, 25, 10, 30) same minute or after DateTime(2014, 10, 25, 10, 25) -define DateTimeSameOrAfterMinuteFalse: DateTime(2014, 10, 25, 15, 30) same minute or after DateTime(2014, 10, 25, 15, 45) -define DateTimeSameOrAfterSecondTrue1: DateTime(2014, 12, 20, 12, 30, 15) same second or after DateTime(2014, 12, 20, 12, 30, 15) -define DateTimeSameOrAfterSecondTrue2: DateTime(2014, 10, 25, 10, 25, 25) same second or after DateTime(2014, 10, 25, 10, 25, 20) -define DateTimeSameOrAfterSecondFalse: DateTime(2014, 10, 25, 15, 45, 20) same second or after DateTime(2014, 10, 25, 15, 45, 21) -define DateTimeSameOrAfterMillisecondTrue1: DateTime(2014, 12, 20, 12, 30, 15, 250) same millisecond or after DateTime(2014, 12, 20, 12, 30, 15, 250) -define DateTimeSameOrAfterMillisecondTrue2: DateTime(2014, 10, 25, 10, 25, 20, 500) same millisecond or after DateTime(2014, 10, 25, 10, 25, 20, 499) -define DateTimeSameOrAfterMillisecondFalse: DateTime(2014, 10, 25, 15, 45, 20, 500) same millisecond or after DateTime(2014, 10, 25, 15, 45, 20, 501) -define DateTimeSameOrAfterNull1: DateTime(2014, 12, 20) same day or after DateTime(2014, 12) -define SameOrAfterTimezoneTrue: @2012-03-10T10:20:00.999+07:00 same hour or after @2012-03-10T09:20:00.999+06:00 -define SameOrAfterTimezoneFalse: @2012-03-10T10:20:00.999+07:00 same hour or after @2012-03-10T10:20:00.999+06:00 -define TimeSameOrAfterHourTrue1: @T23:25:25.555 same hour or after @T23:55:25.900 -define TimeSameOrAfterHourTrue2: @T23:25:25.555 same hour or after @T22:55:25.900 -define TimeSameOrAfterHourFalse: @T22:25:25.555 same hour or after @T23:55:25.900 -define TimeSameOrAfterMinuteTrue1: @T23:25:25.555 same minute or after @T23:25:25.900 -define TimeSameOrAfterMinuteTrue2: @T23:25:25.555 same minute or after @T22:15:25.900 -define TimeSameOrAfterMinuteFalse: @T23:25:25.555 same minute or after @T23:55:25.900 -define TimeSameOrAfterSecondTrue1: @T23:25:25.555 same second or after @T23:25:25.900 -define TimeSameOrAfterSecondTrue2: @T23:25:35.555 same second or after @T22:25:25.900 -define TimeSameOrAfterSecondFalse: @T23:55:25.555 same second or after @T23:55:35.900 -define TimeSameOrAfterMillisTrue1: @T23:25:25.555 same millisecond or after @T23:25:25.555 -define TimeSameOrAfterMillisTrue2: @T23:25:25.555 same millisecond or after @T22:25:25.550 -define TimeSameOrAfterMillisFalse: @T23:55:25.555 same millisecond or after @T23:55:25.900 -define OnOrAfterTrue: @2017-12-20T11:00:00.000 on or after @2017-12-20T11:00:00.000 -define Issue32DateTime: @2017-12-21T02:00:00.0 same or after @2017-12-20T11:00:00.0 - -//SameOrBefore -define DateTimeSameOrBeforeYearTrue1: DateTime(2014) same year or before DateTime(2014) -define DateTimeSameOrBeforeYearTrue2: DateTime(2013) same year or before DateTime(2014) -define DateTimeSameOrBeforeYearFalse: DateTime(2015) same year or before DateTime(2014) -define DateTimeSameOrBeforeMonthTrue1: DateTime(2014, 12) same month or before DateTime(2014, 12) -define DateTimeSameOrBeforeMonthTrue2: DateTime(2014, 8) same month or before DateTime(2014, 9) -define DateTimeSameOrBeforeMonthFalse: DateTime(2014, 12) same month or before DateTime(2014, 11) -define DateTimeSameOrBeforeDayTrue1: DateTime(2014, 12, 20) same day or before DateTime(2014, 12, 20) -define DateTimeSameOrBeforeDayTrue2: DateTime(2014, 10, 15) same day or before DateTime(2014, 10, 20) -define DateTimeSameOrBeforeDayFalse: DateTime(2014, 10, 30) same day or before DateTime(2014, 10, 25) -define DateTimeSameOrBeforeHourTrue1: DateTime(2014, 12, 20, 12) same hour or before DateTime(2014, 12, 20, 12) -define DateTimeSameOrBeforeHourTrue2: DateTime(2014, 10, 25, 5) same hour or before DateTime(2014, 10, 25, 10) -define DateTimeSameOrBeforeHourFalse: DateTime(2014, 10, 25, 20) same hour or before DateTime(2014, 10, 25, 15) -define DateTimeSameOrBeforeMinuteTrue1: DateTime(2014, 12, 20, 12, 30) same minute or before DateTime(2014, 12, 20, 12, 30) -define DateTimeSameOrBeforeMinuteTrue2: DateTime(2014, 10, 25, 10, 20) same minute or before DateTime(2014, 10, 25, 10, 25) -define DateTimeSameOrBeforeMinuteFalse: DateTime(2014, 10, 25, 15, 55) same minute or before DateTime(2014, 10, 25, 15, 45) -define DateTimeSameOrBeforeSecondTrue1: DateTime(2014, 12, 20, 12, 30, 15) same second or before DateTime(2014, 12, 20, 12, 30, 15) -define DateTimeSameOrBeforeSecondTrue2: DateTime(2014, 10, 25, 10, 25, 15) same second or before DateTime(2014, 10, 25, 10, 25, 20) -define DateTimeSameOrBeforeSecondFalse: DateTime(2014, 10, 25, 15, 45, 25) same second or before DateTime(2014, 10, 25, 15, 45, 21) -define DateTimeSameOrBeforeMillisecondTrue1: DateTime(2014, 12, 20, 12, 30, 15, 250) same millisecond or before DateTime(2014, 12, 20, 12, 30, 15, 250) -define DateTimeSameOrBeforeMillisecondTrue2: DateTime(2014, 10, 25, 10, 25, 20, 450) same millisecond or before DateTime(2014, 10, 25, 10, 25, 20, 499) -define DateTimeSameOrBeforeMillisecondFalse: DateTime(2014, 10, 25, 15, 45, 20, 505) same millisecond or before DateTime(2014, 10, 25, 15, 45, 20, 501) -define DateTimeSameOrBeforeNull1: DateTime(2014, 12, 20) same minute or before DateTime(2014, 12, 20, 15) -define SameOrBeforeTimezoneTrue: @2012-03-10T09:20:00.999+07:00 same hour or before @2012-03-10T10:20:00.999+06:00 -define SameOrBeforeTimezoneFalse: @2012-03-10T10:20:00.999+06:00 same hour or before @2012-03-10T10:20:00.999+07:00 -define TimeSameOrBeforeHourTrue1: @T23:25:25.555 same hour or before @T23:55:25.900 -define TimeSameOrBeforeHourFalse0: @T21:25:25.555 same hour or before @T22:55:25.900 -define TimeSameOrBeforeHourFalse: @T22:25:25.555 same hour or before @T21:55:25.900 -define TimeSameOrBeforeMinuteTrue1: @T23:25:25.555 same minute or before @T23:25:25.900 -define TimeSameOrBeforeMinuteFalse0: @T23:10:25.555 same minute or before @T22:15:25.900 -define TimeSameOrBeforeMinuteFalse: @T23:56:25.555 same minute or before @T23:55:25.900 -define TimeSameOrBeforeSecondTrue1: @T23:25:25.555 same second or before @T23:25:25.900 -define TimeSameOrBeforeSecondFalse0: @T23:25:35.555 same second or before @T22:25:45.900 -define TimeSameOrBeforeSecondFalse: @T23:55:45.555 same second or before @T23:55:35.900 -define TimeSameOrBeforeMillisTrue1: @T23:25:25.555 same millisecond or before @T23:25:25.555 -define TimeSameOrBeforeMillisTrue2: @T23:25:25.200 same millisecond or before @T22:25:25.550 -define TimeSameOrBeforeMillisFalse: @T23:55:25.966 same millisecond or before @T23:55:25.900 - -//Subtract -define DateTimeSubtract5Years: DateTime(2005, 10, 10) - 5 years -define DateTimeSubtractInvalidYears: DateTime(2005, 10, 10) - 2005 years -define DateTimeSubtract5Months: DateTime(2005, 6, 10) - 5 months -define DateTimeSubtractMonthsUnderflow: DateTime(2005, 5, 10) - 6 months -define DateTimeSubtractThreeWeeks: DateTime(2018, 5, 23) - 3 weeks -define DateTimeSubtractYearInWeeks: DateTime(2018, 5, 23) - 52 weeks -define DateTimeSubtract5Days: DateTime(2005, 5, 10) - 5 days -define DateTimeSubtractDaysUnderflow: DateTime(2016, 6, 10) - 11 days -define DateTimeSubtract5Hours: DateTime(2005, 5, 10, 10) - 5 hours -define DateTimeSubtractHoursUnderflow: DateTime(2016, 6, 10, 5) - 6 hours -define DateTimeSubtract5Minutes: DateTime(2005, 5, 10, 5, 10) - 5 minutes -define DateTimeSubtractMinutesUnderflow: DateTime(2016, 6, 10, 5, 5) - 6 minutes -define DateTimeSubtract5Seconds: DateTime(2005, 5, 10, 5, 5, 10) - 5 seconds -define DateTimeSubtract1YearInSeconds: DateTime(2016,5) - 31535999 seconds -define DateTimeSubtract15HourPrecisionSecond: DateTime(2016, 10, 1, 10, 20, 30) - 15 hours -define DateTimeSubtractSecondsUnderflow: DateTime(2016, 6, 10, 5, 5, 5) - 6 seconds -define DateTimeSubtract5Milliseconds: DateTime(2005, 5, 10, 5, 5, 5, 10) - 5 milliseconds -define DateTimeSubtractMillisecondsUnderflow: DateTime(2016, 6, 10, 5, 5, 5, 5) - 6 milliseconds -define DateTimeSubtract2YearsAsMonths: DateTime(2014) - 24 months -define DateTimeSubtract2YearsAsMonthsRem1: DateTime(2014) - 25 months -define DateSubtract2YearsAsMonths: Date(2014) - 24 months -define DateSubtract2YearsAsMonthsRem1: Date(2014) - 25 months -define DateSubtract33Days: Date(2014,6) - 33 days -define DateSubtract1Year: Date(2014,6) - 1 year -define TimeSubtract5Hours: @T15:59:59.999 - 5 hours -define TimeSubtract1Minute: @T15:59:59.999 - 1 minutes -define TimeSubtract1Second: @T15:59:59.999 - 1 seconds -define TimeSubtract1Millisecond: @T15:59:59.0 - 1 milliseconds -define TimeSubtract5Hours1Minute: @T15:59:59.999 - 5 hours - 1 minutes -define TimeSubtract5hoursByMinute: @T15:59:59.999 - 300 minutes - -//Time -define TimeTest2: @T23:59:59.999 - //TimeOfDay define TimeOfDayTest: TimeOfDay() //Today -define DateTimeSameOrBeforeTodayTrue1: Today() same day or before Today() -define DateTimeSameOrBeforeTodayTrue2: Today() same day or before Today() + 1 days -define DateTimeSameOrBeforeTodayFalse: Today() + 1 years same day or before Today() -define DateTimeAddTodayTrue: Today() + 1 days > Today() -define Issue34B: Today() \ No newline at end of file +define Issue34B: Today() + +// Default timezoneOffset +define Issue1420: hours between DateTime(2024, 10, 3, 15, 54, 0, 0) and DateTime(2024, 10, 3, 15, 54, 0, 0, 0) \ No newline at end of file diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlFunctionTests.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlFunctionTests.cql index 4a81de55b..2647423d5 100644 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlFunctionTests.cql +++ b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlFunctionTests.cql @@ -22,4 +22,7 @@ define FunctionTestNullTupleArg: f3(Tuple { y: null as Integer }) define function f3(x Quantity): x.unit define FunctionTestQuantityArg: f3(12'cm') + +// Here the call to f3 can only be unambiguously resolved at runtime +// if the library is compiled with signature level set to Overloads or All define FunctionTestNullQuantityArg: f3(null as Quantity) \ No newline at end of file diff --git a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloads.cql b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloads.cql index 1af5bba41..c733aa7f7 100644 --- a/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloads.cql +++ b/Src/java/engine/src/test/resources/org/opencds/cqf/cql/engine/execution/CqlListDistinguishedOverloads.cql @@ -17,8 +17,12 @@ define function toString(value List): else Combine((value V return toString(V)), ', ') // This is the function that _should_ be selected at runtime for the -// List distinguised overloads test. At the time this CQL was written, -// The engine had a bug that was selecting the List overload +// List distinguished overloads test. The engine currently cannot +// distinguish between the List and List overloads +// (and throws the error "Ambiguous call to function 'toString'") +// unless the library is compiled with signature level set to +// Overloads or All. +// See also https://github.com/cqframework/clinical_quality_language/issues/1408. define function toString(value List): if value is null then 'null' diff --git a/Src/java/gradle.properties b/Src/java/gradle.properties index c4a6a0c1d..fc3a144e9 100644 --- a/Src/java/gradle.properties +++ b/Src/java/gradle.properties @@ -2,10 +2,12 @@ org.gradle.caching=true org.gradle.parallel=true org.gradle.jvmargs=-Xmx4096m +systemProp.sonar.gradle.skipCompile=true + group=info.cqframework -version=3.14.0-SNAPSHOT +version=3.18.0-SNAPSHOT specification.version=1.5.2 -hapi.version=7.2.1 +hapi.version=7.4.3 jackson.version=2.16.1 antlr.version=4.13.1 android.api.level=28 diff --git a/Src/java/model/src/test/resources/org/hl7/cql/model/a-modelinfo.xml b/Src/java/model/src/test/resources/org/hl7/cql/model/a-modelinfo.xml index 85e4edb86..a52cd87d2 100644 --- a/Src/java/model/src/test/resources/org/hl7/cql/model/a-modelinfo.xml +++ b/Src/java/model/src/test/resources/org/hl7/cql/model/a-modelinfo.xml @@ -3562,7 +3562,7 @@ - + diff --git a/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-1.5.1.xml b/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-1.5.1.xml index 006937bff..40c007958 100644 --- a/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-1.5.1.xml +++ b/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-1.5.1.xml @@ -3562,7 +3562,7 @@ - + diff --git a/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-with-metadata.xml b/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-with-metadata.xml index 5105a4e70..ac7c247bd 100644 --- a/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-with-metadata.xml +++ b/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1-with-metadata.xml @@ -8753,7 +8753,7 @@ - + diff --git a/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1.xml b/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1.xml index f4e66fb74..fa9142aea 100644 --- a/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1.xml +++ b/Src/java/model/src/test/resources/org/hl7/cql/model/fhir-modelinfo-4.0.1.xml @@ -3562,7 +3562,7 @@ - + diff --git a/Src/java/model/src/test/resources/org/hl7/cql/model/mat-fhir-modelinfo-4.0.1.xml b/Src/java/model/src/test/resources/org/hl7/cql/model/mat-fhir-modelinfo-4.0.1.xml index bb79f42da..92d951d44 100644 --- a/Src/java/model/src/test/resources/org/hl7/cql/model/mat-fhir-modelinfo-4.0.1.xml +++ b/Src/java/model/src/test/resources/org/hl7/cql/model/mat-fhir-modelinfo-4.0.1.xml @@ -3685,7 +3685,7 @@ - + diff --git a/Src/java/model/src/test/resources/org/hl7/cql/model/new-fhir-modelinfo-4.0.1.xml b/Src/java/model/src/test/resources/org/hl7/cql/model/new-fhir-modelinfo-4.0.1.xml index fe7d6dea1..c628592aa 100644 --- a/Src/java/model/src/test/resources/org/hl7/cql/model/new-fhir-modelinfo-4.0.1.xml +++ b/Src/java/model/src/test/resources/org/hl7/cql/model/new-fhir-modelinfo-4.0.1.xml @@ -3562,7 +3562,7 @@ - + diff --git a/Src/java/quick/src/main/resources/org/hl7/fhir/fhir-modelinfo-4.0.1.xml b/Src/java/quick/src/main/resources/org/hl7/fhir/fhir-modelinfo-4.0.1.xml index ecee11d55..2c6996244 100644 --- a/Src/java/quick/src/main/resources/org/hl7/fhir/fhir-modelinfo-4.0.1.xml +++ b/Src/java/quick/src/main/resources/org/hl7/fhir/fhir-modelinfo-4.0.1.xml @@ -8753,7 +8753,7 @@ - +