diff --git a/.gitignore b/.gitignore
index 6485b6c5..5974a0a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
*~
*.lock
*.DS_Store
-*.swp
*.out
ALLTESTS
ALPHAT
@@ -51,7 +50,6 @@ hs_err_pid*
##############################
## Maven
##############################
-target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
diff --git a/CC##99 b/CC##99
deleted file mode 100755
index cbb035ae..00000000
Binary files a/CC##99 and /dev/null differ
diff --git a/CC##TEST b/CC##TEST
deleted file mode 100755
index d950f7d3..00000000
Binary files a/CC##TEST and /dev/null differ
diff --git a/approvaltest b/approvaltest
old mode 100755
new mode 100644
index 31b05276..56269004
--- a/approvaltest
+++ b/approvaltest
@@ -1,6 +1,6 @@
-./cobolcheck -p NUMBERS > approval-test-actual.txt
-./cobolcheck -p ALPHA >> approval-test-actual.txt
-./cobolcheck -p GREETING >> approval-test-actual.txt
-./cobolcheck -p FILECOPY >> approval-test-actual.txt
-./cobolcheck -p MOCKTEST >> approval-test-actual.txt
-./cobolcheck -p DPICNUMBERS >> approval-test-actual.txt
+./temp/approvalTest/cobolcheck -p NUMBERS > ./actual-output.txt
+./temp/approvalTest/cobolcheck -p ALPHA >> ./actual-output.txt
+./temp/approvalTest/cobolcheck -p GREETING >> ./actual-output.txt
+./temp/approvalTest/cobolcheck -p FILECOPY >> ./actual-output.txt
+./temp/approvalTest/cobolcheck -p MOCKTEST >> ./actual-output.txt
+./temp/approvalTest/cobolcheck -p DPICNUMBERS >> ./actual-output.txt
\ No newline at end of file
diff --git a/approvaltestWin.cmd b/approvaltestWin.cmd
index 0a829e35..475f021f 100644
--- a/approvaltestWin.cmd
+++ b/approvaltestWin.cmd
@@ -1 +1 @@
-./cobolcheck -p ALPHA DB2PROG DPICNUMBERS FILECOPY GREETING MOCK MOCKPARA MOCKTEST NUMBERS RETURNCODE TESTNESTED LONGLINESANDNUMBERS > approval-test-actual.txt
\ No newline at end of file
+temp/approvalTest/cobolcheck.cmd -p NUMBERS ALPHA GREETING FILECOPY MOCKTEST DPICNUMBERS > actual-output.txt
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index aef457b5..1e7ef183 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,8 +11,8 @@ def productName = 'cobol-check'
group = 'org.openmainframeproject'
description = 'Unit testing framework for Cobol'
-def approvalExpectedOutput = "approval-test-expected.txt"
-def approvalActualOutput = "output/testResults.txt"
+def approvalExpectedOutput = "./expected-output.txt"
+def approvalActualOutput = "./actual-output.txt"
sonarqube {
properties {
@@ -162,12 +162,17 @@ task copyJarToExtension(type: Copy) {
task copyRunScripts(type: Copy) {
description 'Makes copies of run scripts'
+
+ // copy and modify run scripts for bin version
from "${projectDir}/cobolcheck.cmd"
- into "${projectDir}/GradleTemp"
+ into "${projectDir}/temp/approvalTest"
filter { line -> line.replaceAll('@VERSION@', productVersion) }
from "${projectDir}/cobolcheck"
- into "${projectDir}/GradleTemp"
+ into "${projectDir}/temp/approvalTest"
filter { line -> line.replaceAll('@VERSION@', productVersion) }
+
+ println("Copied with jar version ${productVersion} to approvalTest directory")
+
}
task prepareDistribution(type: Zip) {
@@ -185,37 +190,52 @@ task prepareDistribution(type: Zip) {
rename("build/libs/(.*)", "\$1")
}
- from ("${projectDir}/GradleTemp")
+ from ("${projectDir}/gradleBuildTemp")
doLast {
- delete "GradleTemp"
+ delete "${projectDir}/gradleBuildTemp"
}
}
-def approvalTest = tasks.register("approvalTest", Test) {
+def approvalTest
+approvalTest = tasks.register("approvalTest", Test) {
description 'Run approval test only'
- dependsOn fatJar
+ dependsOn copyJarToBin, copyRunScripts
+
def output = -1
+ def weRanATest = false
+ def runningOs = OS_NAME.toLowerCase()
- if ("${OS_NAME}" == "linux") {
+ if (runningOs == "linux") {
println "Linux detected"
+
+ // grant execute permission to run script
+ def procChmod = "chmod +x ./approvaltest".execute()
+ procChmod.waitForProcessOutput(System.out, System.err)
+
def proc = "./approvaltest".execute()
proc.waitForProcessOutput(System.out, System.err)
+ weRanATest = true
}
- if ("${OS_NAME}".toLowerCase().contains("windows")) {
+ if (runningOs.contains("windows")) {
println "Windows detected"
def proc = "./approvaltestWin.cmd".execute()
proc.waitForProcessOutput(System.out, System.err)
+ weRanATest = true
}
- output = new BuildHelper().compareFiles(approvalExpectedOutput, approvalActualOutput, true)
- println "exit from compare: ${output}"
-
- if (output != 0) {
- println "*** FAIL ***"
- throw new StopExecutionException("${approvalExpectedOutput} and ${approvalActualOutput} are different")
+ if (!weRanATest){
+ println "No prepared test for the OS detected: ${runningOs} - skipping"
} else {
- println "${approvalExpectedOutput} matches ${approvalActualOutput} - PASS"
+ output = new BuildHelper().compareFiles(approvalExpectedOutput, approvalActualOutput, true)
+ println "exit from compare: ${output}"
+
+ if (output != 0) {
+ println "*** FAIL ***"
+ throw new StopExecutionException("${approvalExpectedOutput} and ${approvalActualOutput} are different")
+ } else {
+ println "${approvalExpectedOutput} matches ${approvalActualOutput} - PASS"
+ }
}
}
@@ -241,7 +261,7 @@ task osInfo {
}
class BuildHelper{
- int compareFiles(String file1, String file2, boolean trimLines){
+ static int compareFiles(String file1, String file2, boolean trimLines){
BufferedReader reader1
BufferedReader reader2
try{
diff --git a/build/distributions/cobol-check-0.2.1.zip b/build/distributions/cobol-check-0.2.1.zip
deleted file mode 100644
index 624c7221..00000000
Binary files a/build/distributions/cobol-check-0.2.1.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.10.zip b/build/distributions/cobol-check-0.2.10.zip
deleted file mode 100644
index b0e8c4b2..00000000
Binary files a/build/distributions/cobol-check-0.2.10.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.11.zip b/build/distributions/cobol-check-0.2.11.zip
deleted file mode 100644
index 7f47d5f6..00000000
Binary files a/build/distributions/cobol-check-0.2.11.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.12.zip b/build/distributions/cobol-check-0.2.12.zip
deleted file mode 100644
index 4de28a99..00000000
Binary files a/build/distributions/cobol-check-0.2.12.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.13.zip b/build/distributions/cobol-check-0.2.13.zip
deleted file mode 100644
index 608f62fb..00000000
Binary files a/build/distributions/cobol-check-0.2.13.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.14.zip b/build/distributions/cobol-check-0.2.14.zip
deleted file mode 100644
index 3616f097..00000000
Binary files a/build/distributions/cobol-check-0.2.14.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.15.zip b/build/distributions/cobol-check-0.2.15.zip
deleted file mode 100644
index 5581678b..00000000
Binary files a/build/distributions/cobol-check-0.2.15.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.16.zip b/build/distributions/cobol-check-0.2.16.zip
deleted file mode 100644
index d6bf2d30..00000000
Binary files a/build/distributions/cobol-check-0.2.16.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.17.zip b/build/distributions/cobol-check-0.2.17.zip
deleted file mode 100644
index 0665270d..00000000
Binary files a/build/distributions/cobol-check-0.2.17.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.18.zip b/build/distributions/cobol-check-0.2.18.zip
deleted file mode 100644
index ea5d17cf..00000000
Binary files a/build/distributions/cobol-check-0.2.18.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.2.zip b/build/distributions/cobol-check-0.2.2.zip
deleted file mode 100644
index 5b99a4ff..00000000
Binary files a/build/distributions/cobol-check-0.2.2.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.3.zip b/build/distributions/cobol-check-0.2.3.zip
deleted file mode 100644
index ff8b3185..00000000
Binary files a/build/distributions/cobol-check-0.2.3.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.4.zip b/build/distributions/cobol-check-0.2.4.zip
deleted file mode 100644
index f8693aa8..00000000
Binary files a/build/distributions/cobol-check-0.2.4.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.5.zip b/build/distributions/cobol-check-0.2.5.zip
deleted file mode 100644
index 71a86fe5..00000000
Binary files a/build/distributions/cobol-check-0.2.5.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.7.zip b/build/distributions/cobol-check-0.2.7.zip
deleted file mode 100644
index 82bf06c7..00000000
Binary files a/build/distributions/cobol-check-0.2.7.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.8.zip b/build/distributions/cobol-check-0.2.8.zip
deleted file mode 100644
index fe845773..00000000
Binary files a/build/distributions/cobol-check-0.2.8.zip and /dev/null differ
diff --git a/build/distributions/cobol-check-0.2.9.zip b/build/distributions/cobol-check-0.2.9.zip
deleted file mode 100644
index 86766430..00000000
Binary files a/build/distributions/cobol-check-0.2.9.zip and /dev/null differ
diff --git a/cobolcheck b/cobolcheck
old mode 100755
new mode 100644
diff --git a/config.properties b/config.properties
index f3b8f2ba..4812bd75 100644
--- a/config.properties
+++ b/config.properties
@@ -58,7 +58,7 @@ cobolcheck.append.rules = null
# cobolcheck.test.program.path = /home/myName/temp
# cobolcheck.test.program.path = c:\\Developer\\temp
# cobolcheck.test.program.path = c:/Developer/temp
-cobolcheck.test.program.path = ./
+cobolcheck.test.program.path = ./testruns
#---------------------------------------------------------------------------------------------------------------------
# Suffix to append to the name of each program under test to produce the name of the corresponding
@@ -73,13 +73,13 @@ cobolcheck.test.program.name = CC##99.CBL
# When true, COBOL Check will report the unmocked calls, and the test summary will contain the number of unmocked calls.
# Default: false
#---------------------------------------------------------------------------------------------------------------------
-cobolcheck.test.unmockcall.display = false
+cobolcheck.test.unmockcall.display =false
#---------------------------------------------------------------------------------------------------------------------
# Path for the generated testsuite parse error log
# Default: ./
#---------------------------------------------------------------------------------------------------------------------
-testsuite.parser.error.log.path = ./
+testsuite.parser.error.log.path = ./testruns
#---------------------------------------------------------------------------------------------------------------------
# Name of the generated testsuite parse error log file - with extension
@@ -132,7 +132,7 @@ test.suite.directory = src/test/cobol
#---------------------------------------------------------------------------------------------------------------------
# Location of test output. File extension is determined by a given format.
#---------------------------------------------------------------------------------------------------------------------
-test.results.file = output/testResults
+test.results.file = testruns/testResults
#---------------------------------------------------------------------------------------------------------------------
# Determines the format of the test results written to the output file.
@@ -173,7 +173,7 @@ application.copybook.filename.suffix = CBL,cbl,COB,cob,CPY,cpy
# This is the relative or absolute path of the concatenated file. If not specified, the default
# is "./ALLTESTS" relative to the directory in which Cobol Check was started.
#---------------------------------------------------------------------------------------------------------------------
-concatenated.test.suites = ./ALLTESTS
+concatenated.test.suites = ./testruns/ALLTESTS
#---------------------------------------------------------------------------------------------------------------------
# The GnuCOBOL compiler has a lot of different compile options.
diff --git a/approval-test-expected.txt b/expected-output.txt
similarity index 100%
rename from approval-test-expected.txt
rename to expected-output.txt
diff --git a/src/main/cobol/MOCKTEST.CBL b/src/main/cobol/MOCKTEST.CBL
index 0a03711f..103629fc 100644
--- a/src/main/cobol/MOCKTEST.CBL
+++ b/src/main/cobol/MOCKTEST.CBL
@@ -10,7 +10,7 @@
WORKING-STORAGE SECTION.
01 FILLER.
05 VALUE-1 PIC X(80).
- 05 VALUE-2 PIC X(80).
+ 05 VALUE-2 PIC X(80) Value 'initial'.
05 VALUE-3 PIC X(80).
05 TEMP PIC X(80).
@@ -85,15 +85,15 @@
CALL 'PROG1'
.
- 610-MAKE-CALL-VALUE-IN-STRUCTURE.
- MOVE "1" to ACTION-PARAM
- MOVE "2" to BOOK-PARAM
- MOVE "3" to OUTPUT-VALUE
- CALL 'MYCOBOL' USING ACTION-PARAM,
- BOOK-PARAM IN BOOK-STRUCTURE,
- OUTPUT-PARAM
+ 610-CALL-VALUE-IN-STRUCTURE.
+ MOVE "1" to action-value
+ MOVE "2" to book-value
+ MOVE "3" to output-value
+ CALL 'MYCOBOL' USING action-value,
+ book-value IN BOOK-STRUCTURE,
+ output-value
END-CALL
- MOVE OUTPUT-PARAM TO VALUE-1
+ MOVE OUTPUT-VALUE TO VALUE-1
.
700-MAKE-CALL.
@@ -115,7 +115,7 @@
BY CONTENT VALUE-1,
BY VALUE VALUE-2,
VALUE-3
- CALL 'PROG3' USING VALUE-1.
+ CALL 'PROG3' USING VALUE-1.
900-MAKE-CALL.
CALL 'PROGRAM' USING VALUE-1
diff --git a/src/main/cobol/REPLAC.CBL b/src/main/cobol/REPLAC.CBL
new file mode 100644
index 00000000..472fd67a
--- /dev/null
+++ b/src/main/cobol/REPLAC.CBL
@@ -0,0 +1,65 @@
+ IDENTIFICATION DIVISION.
+ PROGRAM-ID. GREETING.
+ *****************************************************************
+ * Trivial program to exercise CobolCheck.
+ * With regards to REPLACE statement.
+ *****************************************************************
+ ENVIRONMENT DIVISION.
+ INPUT-OUTPUT SECTION.
+ FILE-CONTROL.
+ DATA DIVISION.
+ WORKING-STORAGE SECTION.
+
+ REPLACE ==:PROGRAM:== BY ==REPDEMO3==
+ ==:REQUEST:== BY ==UPDATE-MY-ADVANCED-REQUEST==
+ ==:RESPONSE:== BY ==UPDATE-MY-ADVANCED-RESPONSE==.
+
+123456 01 FILLER.
+ 05 WS-COUNT PIC S9(5) COMP-3.
+ 05 FILLER PIC X VALUE 'G'.
+ 88 MESSAGE-IS-GREETING VALUE 'G'.
+ 88 MESSAGE-IS-FAREWELL VALUE 'F'.
+ 01 WS-FRIEND PIC X(10) VALUE SPACES.
+ 01 WS-GREETING.
+ 10 FILLER PIC X(07) VALUE 'Hello, '.
+ 10 WS-USER-NAME PIC X(05) VALUE SPACES.
+ 10 FILLER PIC X VALUE '!'.
+ 01 WS-FAREWELL.
+ 10 FILLER PIC X(15) VALUE 'See you later, '.
+ 10 WS-USER-NAME PIC X(09) VALUE SPACES.
+ 10 FILLER PIC X VALUE '!'.
+
+ 01 :program:-param PIC X(10) VALUE "program".
+ 01 :request:-data PIC X(10) VALUE "data data".
+ 01 :response:-element PIC X(10) VALUE ".response.".
+
+ PROCEDURE DIVISION.
+
+ ACCEPT WS-FRIEND.
+
+
+ 2000-SPEAK.
+ IF MESSAGE-IS-GREETING
+ IF WS-FRIEND EQUAL SPACES
+ MOVE 'World' TO WS-USER-NAME OF WS-GREETING
+ ELSE
+ MOVE WS-FRIEND TO WS-USER-NAME OF WS-GREETING
+ END-IF
+ END-IF
+ IF MESSAGE-IS-FAREWELL
+ IF WS-FRIEND EQUAL SPACES
+ MOVE 'alligator!' TO WS-USER-NAME OF WS-FAREWELL
+ ELSE
+ MOVE WS-FRIEND TO WS-USER-NAME OF WS-FAREWELL
+ END-IF
+ END-IF
+ .
+
+ 3000-SAY-NO-MORE.
+ DISPLAY WS-GREETING :PROGRAM:-param
+ DISPLAY WS-FAREWELL :PROGRAM:-param :REQUEST:-data
+ DISPLAY :PROGRAM:-param :REQUEST:-data :RESPONSE:-element
+ .
+
+ 9999-END.
+ .
diff --git a/src/main/cobol/WS88LEVEL.CBL b/src/main/cobol/WS88LEVEL.CBL
new file mode 100644
index 00000000..586eb189
--- /dev/null
+++ b/src/main/cobol/WS88LEVEL.CBL
@@ -0,0 +1,113 @@
+ ID DIVISION.
+ PROGRAM-ID. LV88TEST.
+ AUTHOR. TNP.
+ DATE-WRITTEN. 12.05.2025.
+
+ ENVIRONMENT DIVISION.
+ CONFIGURATION SECTION.
+ SPECIAL-NAMES.
+ DECIMAL-POINT IS COMMA.
+ /
+ INPUT-OUTPUT SECTION.
+ FILE-CONTROL.
+ SELECT LINEFILE ASSIGN TO UT-S-LINEFILE
+ ORGANIZATION IS SEQUENTIAL
+ ACCESS MODE IS SEQUENTIAL
+ FILE STATUS STATUS-LINEFILE.
+
+ DATA DIVISION.
+ FILE SECTION.
+ FD LINEFILE
+ BLOCK CONTAINS 0 RECORDS
+ RECORD CONTAINS 160 CHARACTERS
+ LABEL RECORD STANDARD
+ RECORDING F
+ DATA RECORD LINIE1.
+ 01 LINIE1.
+ 02 FD-88-LEVELS PIC X.
+ 88 FD-88-value-1 VALUE 'W'.
+ 88 FD-88-value-2 VALUE 'X'.
+ 88 FD-88-value-3 VALUE 'Y'.
+ 88 FD-88-value-4 VALUE 'Z'.
+ 88 FD-88-ALL VALUE 'Z',
+ 'Y',
+ 'X',
+ 'W'.
+
+ 02 FD-88-LEVELS-NUMERIC PIC 99.
+ 88 FD-88-NUMERIC-LOW
+ VALUE 0
+ through 9.
+ 88 FD-88-NUMERIC-MEDIUM
+ VALUE 10
+ through 49.
+ 88 FD-88-NUMERIC-HIGH
+ VALUE 50
+ through 99.
+ 88 FD-88-NUMERIC-MED-ODD
+ VALUE 11, 13, 15, 17, 19,
+ 21, 23, 25, 27, 29,
+ 31, 33, 35, 37, 39,
+ 41, 43, 45, 47, 49.
+ 88 FD-88-NUMERIC-MED-EVEN
+ VALUE 12, 14, 16, 18,
+ 22, 24, 26, 28,
+ 32, 34, 36, 38,
+ 42, 44, 46, 48.
+ *-----------------------------------------------------------------
+ WORKING-STORAGE SECTION.
+ *-----------------------------------------------------------------
+ 01 STATUS-LINEFILE PIC 99 VALUE ZERO.
+ 01 WORK-FIELDS.
+ 03 WS-PROG-NAME PIC X(8) VALUE 'LV88TEST'.
+ 77 WS-OMEGA PIC X.
+ 77 WS-SUBPROGRAM-NAME PIC X(08).
+
+ 01 WS-88-LEVELS PIC X.
+ 88 level-88-value-1 VALUE 'A'.
+ 88 level-88-value-2 VALUE 'B'.
+ 88 level-88-value-3 VALUE 'C'.
+ 88 level-88-value-4 VALUE 'D'.
+ 88 level-88-ALL VALUE 'D',
+ 'C',
+ 'B',
+ 'A'.
+
+ 01 WS-88-LEVELS-NUMERIC PIC 99.
+ 88 SW-88-NUMERIC-LOW
+ VALUE 0
+ through 9.
+ 88 SW-88-NUMERIC-MEDIUM
+ VALUE 10
+ through 49.
+ 88 SW-88-NUMERIC-HIGH
+ VALUE 50
+ through 99.
+ 88 SW-88-NUMERIC-MED-ODD
+ VALUE 11, 13, 15, 17, 19,
+ 21, 23, 25, 27, 29,
+ 31, 33, 35, 37, 39,
+ 41, 43, 45, 47, 49.
+ 88 SW-88-NUMERIC-MED-EVEN
+ VALUE 12, 14, 16, 18,
+ 22, 24, 26, 28,
+ 32, 34, 36, 38,
+ 42, 44, 46, 48.
+
+
+ PROCEDURE DIVISION.
+ PERFORM 001-INITIALIZE
+
+ DISPLAY SPACE
+ DISPLAY "TESTING 88-LEVELS IN FD and WORKING-STORAGE"
+ DISPLAY
+ 'DEMONSTRATE WS 88 STATEMENT are parsed correctly'
+ DISPLAY SPACES
+ .
+
+ 001-INITIALIZE SECTION.
+ MOVE 'Z' TO WS-OMEGA
+ MOVE 'LV88SUB ' TO WS-SUBPROGRAM-NAME
+ OPEN OUTPUT LINEFILE
+ .
+
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/CopybookExpander.java b/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/CopybookExpander.java
index c574615e..c642950a 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/CopybookExpander.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/CopybookExpander.java
@@ -9,25 +9,27 @@
import org.openmainframeproject.cobolcheck.services.cobolLogic.CobolLine;
import org.openmainframeproject.cobolcheck.services.cobolLogic.Interpreter;
import org.openmainframeproject.cobolcheck.services.cobolLogic.TokenExtractor;
+import org.openmainframeproject.cobolcheck.services.log.Log;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
/**
* Expand copybooks referenced by the code under test.
- *
+ *
* In the general use case, COPY statements are left alone and the compiler handles expansion.
- *
+ *
* Cobol-check runs as a precompiler and does not "see" the expanded Cobol source code.
* There are two cases in which cobol-check may need to "see" the expanded code.
- *
+ *
* 1. If source statements pertinent to a mock are contained in copybooks, cobol-check needs to
* be able to comment-out those statements in the merged test program.
- *
+ *
* 2. IBM z/OS compilers support nested COPY statements with the REPLACING option. Off-platform
* compilers written by others do not support this, as it is an IBM extension to Cobol. When the
* code under test uses this feature, and cobol-check is running on a platform other than z/OS,
@@ -47,10 +49,6 @@ public CopybookExpander() {
}
- public List expand(List expandedLines, String copybookFilename) throws IOException {
- return expand(expandedLines, copybookFilename, new StringTuple(null, null));
- }
-
public List expand(List expandedLines, String copybookFilename,
StringTuple... textReplacement) throws IOException {
String fullPath = PathHelper.findFilePath(pathToCopybooks, copybookFilename, copybookFilenameSuffixes);
@@ -89,8 +87,27 @@ public List expand(List expandedLines, String copybookFilename,
return expandedLines;
}
- private String commentOut(String sourceLine) {
+ /**
+ * Comments out a source line by placing an asterisk in column 7.
+ * If the line is shorter than 7 characters, it is returned unchanged.
+ * @param sourceLine
+ * @return
+ */
+ String commentOut(String sourceLine) {
+ // Create StringBuilder from sourceLine ensuring it is at least 7 characters long
+ if (sourceLine == null || sourceLine.length() < 7) {
+ return sourceLine;
+ }
StringBuilder tempLine = new StringBuilder(sourceLine);
+ // IF the column 7 is not space, then we are probably dealing with a
+ // non-standard source format. In that case, we will throw an exception.
+ if (tempLine.charAt(6) != ' ' && tempLine.charAt(6) != '*') {
+ throw new PossibleInternalLogicErrorException(
+ Messages.get("ERR034",
+ sourceLine));
+ }
+
+ // put an asterisk in column 7 (index 6)
tempLine.setCharAt(6, '*');
return tempLine.toString();
}
@@ -123,8 +140,7 @@ private String getPathToCopybooks() {
return PathHelper.endWithFileSeparator(Config.getCopyBookSourceDirectoryPathString());
}
- public List expandDB2(List expandedLines, String copybookFilename,
- StringTuple... textReplacement) throws IOException {
+ public List expandDB2(List expandedLines, String copybookFilename) throws IOException {
String fullPath = PathHelper.findFilePath(pathToCopybooks, copybookFilename, copybookFilenameSuffixes);
if (fullPath == null)
throw new IOException("could not find copybook " + copybookFilename + " in " + pathToCopybooks);
@@ -147,4 +163,63 @@ public List expandDB2(List expandedLines, String copybookFilenam
return expandedLines;
}
+ /**
+ * Returns the lines included by the INCLUDE statement
+ * @param 'exec sql' statement with an INCLUDE clause
+ * @return list of lines included by the INCLUDE statement
+ * @throws IOException
+ */
+ public List getIncludedLines(String line) throws IOException {
+ List expandedLines = new ArrayList<>();
+ String copybookName = extractCopybookNameFromCopyStatement(line);
+ return this.expandDB2(expandedLines, copybookName);
+ }
+
+
+ String extractCopybookNameFromCopyStatement(String line) {
+ String copybookName ;
+ int copybookNameStartPosition = findCopybookNamePositionInCopyStatement(line);
+ int copybookNameEndPosition = findCopyBookNameEndPositionInCopyStatement(line, copybookNameStartPosition);
+ if (copybookNameStartPosition >= 0 && copybookNameEndPosition > copybookNameStartPosition) {
+ copybookName = line.substring(copybookNameStartPosition, copybookNameEndPosition).trim();
+ if (copybookName.endsWith(Constants.PERIOD)) {
+ copybookName = copybookName.substring(0, copybookName.length() - 1);
+ }
+ } else {
+ throw new PossibleInternalLogicErrorException(
+ Messages.get("ERR008",
+ line,
+ "LineRepository.extractCopybookNameFromCopyStatement(line)",
+ line));
+ }
+ return copybookName;
+ }
+
+ int findCopybookNamePositionInCopyStatement(String line) {
+ int copybookNamePosition = -1;
+ int copyWordPosition = line.indexOf(Constants.INCLUDE);
+ if (copyWordPosition >= 0) {
+ copybookNamePosition = copyWordPosition + Constants.INCLUDE.length() + 1;
+ }
+ return copybookNamePosition;
+ }
+
+ int findCopyBookNameEndPositionInCopyStatement(String line, int copybookNameStartPosition) {
+ int copybookNameEndPosition = -1;
+ if (copybookNameStartPosition >= 0) {
+ int periodPosition = line.indexOf(Constants.PERIOD, copybookNameStartPosition);
+ int spacePosition = line.indexOf(Constants.SPACE, copybookNameStartPosition);
+ if (periodPosition >= 0 && spacePosition >= 0) {
+ copybookNameEndPosition = Math.min(periodPosition, spacePosition);
+ } else if (periodPosition >= 0) {
+ copybookNameEndPosition = periodPosition;
+ } else if (spacePosition >= 0) {
+ copybookNameEndPosition = spacePosition;
+ } else {
+ copybookNameEndPosition = line.length();
+ }
+ }
+ return copybookNameEndPosition;
+ }
+
}
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/InterpreterController.java b/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/InterpreterController.java
index 0f00c174..a34fd75f 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/InterpreterController.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/InterpreterController.java
@@ -10,7 +10,6 @@
import org.openmainframeproject.cobolcheck.services.log.Log;
import org.openmainframeproject.cobolcheck.services.platform.Platform;
import org.openmainframeproject.cobolcheck.services.platform.PlatformLookup;
-import org.openmainframeproject.cobolcheck.services.RunInfo;
import java.io.BufferedReader;
import java.io.IOException;
@@ -27,7 +26,6 @@ public class InterpreterController {
private String possibleMockIdentifier;
private String possibleMockType;
private List possibleMockArgs;
- private List extractedCopyBook;
private boolean insideSectionOrParagraphMockBody;
private TreeMap currentDataStructure;
private final String stubTag;
@@ -69,6 +67,11 @@ public DataType getNumericFieldDataTypeFor(String fieldName) {
return numericFields.dataTypeOf(fieldName);
}
+ /**
+ * returns the NumericFields object, which contains information about
+ * all numeric fields found in the DATA DIVISION of the program under test.
+ * @return
+ */
public NumericFields getNumericFields() {
return numericFields;
}
@@ -263,7 +266,9 @@ public void closeReader() {
* @param line - The line the update is based upon
*/
private void updateDependencies(CobolLine line) throws IOException {
+
reader.updateState();
+
updateLineRepository(line);
List currentStatement = new ArrayList<>();
@@ -273,6 +278,8 @@ private void updateDependencies(CobolLine line) throws IOException {
currentStatement.add(line);
}
+ updateLineRepositoryUpdateSecondPass(line);
+
if (reader.isFlagSet(Constants.SPECIAL_NAMES_PARAGRAPH)) {
updateDecimalPointIsComma(line);
}
@@ -432,6 +439,8 @@ private String generateVariableNameBasedOnDataStructure(TreeMap
* @param line - current source line
*/
private void updateLineRepository(CobolLine line) throws IOException {
+ List extractedCopyBook;
+
if (reader.isFlagSet(Constants.FILE_CONTROL)) {
lineRepository.addFileControlStatement(line.getUnNumberedString());
@@ -450,7 +459,7 @@ private void updateLineRepository(CobolLine line) throws IOException {
lineRepository.addFileSectionStatement(line.getUnNumberedString());
}
}
-
+
if (reader.isFlagSet(Constants.WORKING_STORAGE_SECTION) && line.containsToken(Constants.EXEC_SQL_TOKEN)) {
String statement = reader.readStatementAsOneLine().getTrimmedString().replaceAll("\\s+", " ");
if (statement.startsWith(Constants.EXEC_SQL_TOKEN + " " + Constants.INCLUDE)) {
@@ -460,13 +469,14 @@ private void updateLineRepository(CobolLine line) throws IOException {
if (statement.contains("SQLCA") || statement.contains("SQLDA"))
return;
default:
- extractedCopyBook = lineRepository.addExpandedCopyDB2Statements(reader.readStatementAsOneLine());
+ extractedCopyBook = lineRepository.getExpandedCopyDB2Statements(statement);
+ lineRepository.addAllLinesFromCopyDB2StatementsToFileSectionStatements(extractedCopyBook);
for (int i = 0; i < extractedCopyBook.size(); i++) {
CobolLine cobolLine = new CobolLine(extractedCopyBook.get(i), tokenExtractor);
List currentStatement = new ArrayList<>();
currentStatement.add(cobolLine);
this.currentDataStructure = updateCurrentDataStructure(currentStatement, currentDataStructure);
- updateNumericFields(cobolLine);
+ updateNumericFields(cobolLine);
}
break;
}
@@ -474,6 +484,31 @@ private void updateLineRepository(CobolLine line) throws IOException {
}
}
+ /**
+ * If we are in the FILE SECTION and have read a multiline statement, we need to
+ * add all lines except the first one, to the list of file section statements.
+ * The first line is already added as part of reading the statement in the first pass.
+ *
+ * @param line - current source line
+ */
+ private void updateLineRepositoryUpdateSecondPass(CobolLine line) throws IOException {
+
+ if (reader.isFlagSet(Constants.FILE_SECTION) && reader.isFlagSet(Constants.FD_TOKEN)) {
+ if (reader.isFlagSet(Constants.LEVEL_01_TOKEN)) {
+ if ( this.hasStatementBeenRead()) {
+ List lines = reader.getCurrentStatement();
+
+ // we Skip first line, it is already added as part of reading the statement in the first pass
+ for (int idx = 1; idx < lines.size(); idx++) {
+ // updateNumericFields(l);
+ lineRepository.addFileSectionStatement(lines.get(idx).getUnNumberedString());
+ }
+ }
+ }
+ }
+ }
+
+
/**
* If the given line contains a SELECT token, the file identifier will be added, waiting for
* a mapping to a corresponding file status.
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/LineRepository.java b/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/LineRepository.java
index 6f332ab3..3340dc6f 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/LineRepository.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/features/interpreter/LineRepository.java
@@ -5,7 +5,7 @@
import org.openmainframeproject.cobolcheck.services.Constants;
import org.openmainframeproject.cobolcheck.services.Messages;
import org.openmainframeproject.cobolcheck.services.StringTuple;
-import org.openmainframeproject.cobolcheck.services.cobolLogic.CobolLine;
+import org.openmainframeproject.cobolcheck.services.log.Log;
import java.io.IOException;
import java.util.ArrayList;
@@ -50,9 +50,6 @@ Map getFileIdentifiersAndStatuses() {
List getCopyTokens() {
return copyTokens;
}
- void setCopyTokens(List copyTokens) {
- this.copyTokens = copyTokens;
- }
void addFileControlStatement(String statement){
if (fileControlStatements == null){
@@ -68,9 +65,6 @@ void addFileSectionStatement(String statement){
fileSectionStatements.add(statement);
}
- void putFileIdentifierAndStatus(String key, String value){
- fileIdentifiersAndStatuses.put(key, value);
- }
void addFileIdentifierWithNoStatus(String identifier){
fileIdentifiersAndStatuses.put(identifier, Constants.EMPTY_STRING);
currentExpectFileIdentifier = identifier;
@@ -79,14 +73,9 @@ void addStatusForLastSetIdentifier(String status){
fileIdentifiersAndStatuses.put(currentExpectFileIdentifier, status);
}
- void addCopyToken(String token){
- if (copyTokens == null){
- copyTokens = new ArrayList<>();
- }
- copyTokens.add(token);
- }
-
void addAccumulatedTokensFromCopyStatementToCopyTokens(String line) {
+ // warn on the number og tokens collected
+ Log.warn("addAccumulatedTokensFromCopyStatementToCopyTokens: " + line);
if (copyTokens == null) {
copyTokens = new ArrayList<>();
}
@@ -99,6 +88,9 @@ void addAccumulatedTokensFromCopyStatementToCopyTokens(String line) {
}
List addExpandedCopyStatementsToFileSectionStatements() {
+ // warn on the number og tokens collected
+ Log.warn("addExpandedCopyStatementsToFileSectionStatements: " + copyTokens.size() + " tokens collected");
+
for (int i = 0 ; i < copyTokens.size() ; i++) {
if (copyTokens.get(i).equals(Constants.EMPTY_STRING)) {
copyTokens.remove(i);
@@ -127,24 +119,33 @@ List addExpandedCopyStatementsToFileSectionStatements() {
try {
copyLines = copybookExpander.expand(copyLines, copybookName, replacingValues);
} catch (IOException ioException) {
- ioException.printStackTrace();
+ throw new PossibleInternalLogicErrorException("addExpandedCopyStatementsToFileSectionStatements",ioException);
}
fileSectionStatements.addAll(copyLines);
return copyLines;
}
- List addExpandedCopyDB2Statements(CobolLine line) throws IOException {
- List copyLines = new ArrayList<>();
+ /**
+ * Expands an 'EXEC SQL' statement into multiple lines
+ * by locating and reading the INCLUDED copybook.
+ * @param line
+ * @return
+ * @throws IOException
+ */
+ List getExpandedCopyDB2Statements(String line) throws IOException {
+ List copyLines;
CopybookExpander copybookExpander = new CopybookExpander();
- String copybookName = line.getToken(2);
- StringTuple replacingValues = new StringTuple(null, null);
try {
- copyLines = copybookExpander.expandDB2(copyLines, copybookName, replacingValues);
+ copyLines = copybookExpander.getIncludedLines(line);
} catch (IOException ioEx) {
throw new CopybookCouldNotBeExpanded(ioEx);
}
- fileSectionStatements.addAll(copyLines);
return copyLines;
}
+
+
+ public void addAllLinesFromCopyDB2StatementsToFileSectionStatements(List extractedCopyBook) {
+ this.fileSectionStatements.addAll(extractedCopyBook);
+ }
}
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/features/writer/CobolWriter.java b/src/main/java/org/openmainframeproject/cobolcheck/features/writer/CobolWriter.java
index ea728a5b..9a508c42 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/features/writer/CobolWriter.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/features/writer/CobolWriter.java
@@ -3,6 +3,7 @@
import org.openmainframeproject.cobolcheck.services.Config;
import org.openmainframeproject.cobolcheck.services.cobolLogic.Interpreter;
import org.openmainframeproject.cobolcheck.services.StringHelper;
+import org.openmainframeproject.cobolcheck.services.log.Log;
import java.io.IOException;
import java.io.Writer;
@@ -39,6 +40,7 @@ void writeLine(String line) throws IOException {
storedLines.add(line);
else
writer.write(line);
+ writer.flush();
}
else {
//We need to check if this line is to be commented out or if it is already a comment
@@ -95,12 +97,8 @@ void writeStubbedLine(String line) throws IOException {
writeLine(StringHelper.stubLine(line, stubTag));
}
- void writeFormattedLine(String format, Object... args) throws IOException {
- writeLine(String.format(format, args));
- }
-
/**
- * Writes all the given lines of cobol code to the test output file. If the any of the lines
+ * Writes all the given lines of cobol code to the test output file. If any of the lines
* are too long for cobol to handle, it will be correctly split into multiple lines.
*
* @param lines - lines to be written
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/Interpreter.java b/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/Interpreter.java
index 80cf22dd..8309b97d 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/Interpreter.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/Interpreter.java
@@ -36,7 +36,6 @@ public static int getSequenceNumberAreaIndex(){
return sequenceNumberAreaEnd;
}
- //TODO: Speed up method by adding 'else if's and putting 'if's inside 'if's
/**
* Sets flags based on a line, to be able to know which kinds of source
* statements to look for when reading and interpreting lines.
@@ -50,92 +49,88 @@ public static String setFlagsForCurrentLine(CobolLine line, CobolLine nextLine,
if (line.containsToken(Constants.IDENTIFICATION_DIVISION)) {
state.setFlagFor(Constants.IDENTIFICATION_DIVISION);
partOfProgram = Constants.IDENTIFICATION_DIVISION;
- }
- if (line.containsToken(Constants.ENVIRONMENT_DIVISION)) {
+
+ } else if (line.containsToken(Constants.ENVIRONMENT_DIVISION)) {
state.setFlagFor(Constants.ENVIRONMENT_DIVISION);
partOfProgram = Constants.ENVIRONMENT_DIVISION;
- }
- if (line.containsToken(Constants.CONFIGURATION_SECTION)) {
+
+ } else if (line.containsToken(Constants.CONFIGURATION_SECTION)) {
state.setFlagFor(Constants.CONFIGURATION_SECTION);
partOfProgram = Constants.CONFIGURATION_SECTION;
- }
- if (line.containsToken(Constants.SPECIAL_NAMES_PARAGRAPH)) {
+
+ } else if (line.containsToken(Constants.SPECIAL_NAMES_PARAGRAPH)) {
state.setFlagFor(Constants.SPECIAL_NAMES_PARAGRAPH);
partOfProgram = Constants.SPECIAL_NAMES_PARAGRAPH;
- }
- if (line.containsToken(Constants.INPUT_OUTPUT_SECTION)) {
+
+ } else if (line.containsToken(Constants.INPUT_OUTPUT_SECTION)) {
state.setFlagFor(Constants.INPUT_OUTPUT_SECTION);
partOfProgram = Constants.INPUT_OUTPUT_SECTION;
- }
- if (line.containsToken(Constants.FILE_CONTROL)) {
+
+ } else if (line.containsToken(Constants.FILE_CONTROL)) {
state.setFlagFor(Constants.FILE_CONTROL);
partOfProgram = Constants.FILE_CONTROL;
- }
- if (line.containsToken(Constants.DATA_DIVISION)) {
+
+ } else if (line.containsToken(Constants.DATA_DIVISION)) {
state.setFlagFor(Constants.DATA_DIVISION);
partOfProgram = Constants.DATA_DIVISION;
- }
- if (line.containsToken(Constants.PROCEDURE_DIVISION)) {
+
+ } else if (line.containsToken(Constants.PROCEDURE_DIVISION)) {
state.setFlagFor(Constants.PROCEDURE_DIVISION);
partOfProgram = Constants.PROCEDURE_DIVISION;
- }
- if (line.containsToken(Constants.FILE_SECTION)) {
+
+ } else if (line.containsToken(Constants.FILE_SECTION)) {
state.setFlagFor(Constants.FILE_SECTION);
partOfProgram = Constants.FILE_SECTION;
- }
- if (line.containsToken(Constants.LOCAL_STORAGE_SECTION)) {
+
+ } else if (line.containsToken(Constants.LOCAL_STORAGE_SECTION)) {
state.setFlagFor(Constants.LOCAL_STORAGE_SECTION);
partOfProgram = Constants.LOCAL_STORAGE_SECTION;
- }
- if (line.containsToken(Constants.LINKAGE_SECTION)) {
+
+ } else if (line.containsToken(Constants.LINKAGE_SECTION)) {
state.setFlagFor(Constants.LINKAGE_SECTION);
partOfProgram = Constants.LINKAGE_SECTION;
- }
- if (line.containsToken(Constants.WORKING_STORAGE_SECTION)) {
+
+ } else if (line.containsToken(Constants.WORKING_STORAGE_SECTION)) {
state.setFlagFor(Constants.WORKING_STORAGE_SECTION);
partOfProgram = Constants.WORKING_STORAGE_SECTION;
- }
- if (line.containsToken(Constants.SELECT_TOKEN)) {
- state.unsetFlagFor(Constants.SELECT_TOKEN);
- state.setFlagFor(Constants.SELECT_TOKEN);
- partOfProgram = Constants.SELECT_TOKEN;
- }
- if (line.containsToken(Constants.FILE_STATUS_TOKEN)) {
+
+ } else if (line.containsToken(Constants.FILE_STATUS_TOKEN)) {
state.setFlagFor(Constants.FILE_STATUS_TOKEN);
partOfProgram = Constants.FILE_STATUS_TOKEN;
- }
- if (line.containsToken(Constants.IS_TOKEN)) {
- state.setFlagFor(Constants.IS_TOKEN);
- partOfProgram = Constants.IS_TOKEN;
- }
- if (line.containsToken(Constants.IS_TOKEN)) {
+
+ } else if (line.containsToken(Constants.IS_TOKEN)) {
state.setFlagFor(Constants.IS_TOKEN);
partOfProgram = Constants.IS_TOKEN;
- }
- if (line.containsToken(Constants.FD_TOKEN)) {
- state.unsetFlagFor(Constants.FD_TOKEN);
- state.setFlagFor(Constants.FD_TOKEN);
- partOfProgram = Constants.FD_TOKEN;
- }
- if (line.containsToken(Constants.LEVEL_01_TOKEN)) {
+
+ } else if (line.containsToken(Constants.LEVEL_01_TOKEN)) {
state.setFlagFor(Constants.LEVEL_01_TOKEN);
partOfProgram = Constants.LEVEL_01_TOKEN;
- }
- if (line.containsToken(Constants.COPY_TOKEN)) {
+
+ } else if (line.containsToken(Constants.COPY_TOKEN)) {
state.setFlagFor(Constants.COPY_TOKEN);
partOfProgram = Constants.COPY_TOKEN;
- }
- if (line.containsToken(Constants.SECTION_TOKEN)) {
+// tokens with unset flags, as they can appear multiple times
+ } else if (line.containsToken(Constants.SELECT_TOKEN)) {
+ state.unsetFlagFor(Constants.SELECT_TOKEN);
+ state.setFlagFor(Constants.SELECT_TOKEN);
+ partOfProgram = Constants.SELECT_TOKEN;
+
+ } else if (line.containsToken(Constants.SECTION_TOKEN)) {
state.unsetFlagFor(Constants.SECTION_TOKEN);
state.setFlagFor(Constants.SECTION_TOKEN);
partOfProgram = Constants.SECTION_TOKEN;
- }
- if (isParagraphHeader(line, nextLine, state)) {
+
+ } else if (isParagraphHeader(line, nextLine, state)) {
state.unsetFlagFor(Constants.PARAGRAPH_TOKEN);
state.setFlagFor(Constants.PARAGRAPH_TOKEN);
partOfProgram = Constants.PARAGRAPH_TOKEN;
- }
- if (line.containsToken(Constants.CALL_TOKEN)) {
+
+ } else if (line.containsToken(Constants.FD_TOKEN)) {
+ state.unsetFlagFor(Constants.FD_TOKEN);
+ state.setFlagFor(Constants.FD_TOKEN);
+ partOfProgram = Constants.FD_TOKEN;
+
+ } else if (line.containsToken(Constants.CALL_TOKEN)) {
state.unsetFlagFor(Constants.CALL_TOKEN);
state.setFlagFor(Constants.CALL_TOKEN);
partOfProgram = Constants.CALL_TOKEN;
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/NumericFields.java b/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/NumericFields.java
index 0b8a542c..071d27fb 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/NumericFields.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/NumericFields.java
@@ -30,6 +30,15 @@ public DataType dataTypeOf(String fieldName) {
return fieldTypes.getOrDefault(fieldName, DataType.ALPHANUMERIC);
}
+ /**
+ * Returns the count of all fields known
+ * @return integer count of known fields
+ */
+ public int getNumberOfFields() {
+ if (fieldTypes != null) return fieldTypes.size();
+ return 0;
+ }
+
public void setDataTypeOf(String fieldName, DataType dataType) {
argumentCheck(fieldName, "ERR028");
argumentCheck(dataType, "ERR029");
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/Replace.java b/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/Replace.java
index 7b547e41..0dfc093a 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/Replace.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/Replace.java
@@ -1,6 +1,8 @@
package org.openmainframeproject.cobolcheck.services.cobolLogic.replace;
+import org.openmainframeproject.cobolcheck.services.Config;
import org.openmainframeproject.cobolcheck.services.filehelpers.EncodingIO;
+import org.openmainframeproject.cobolcheck.services.filehelpers.FilePermission;
import org.openmainframeproject.cobolcheck.services.log.Log;
import org.openmainframeproject.cobolcheck.services.log.LogLevel;
@@ -45,6 +47,13 @@ public class Replace {
+ COBOL_COMMENT_INDICATOR + ")(.+)");
private static final int SOURCE_COMMENT_INDICATOR = 2;
+ /**
+ * Suffix for the replaced file name.
+ */
+ private static final String FILE_PERIOD = ".";
+ private static final String REPLACED = FILE_PERIOD + "replaced";
+ private static final String DEFAULT_EXTENSION = "CBL";
+
/**
* The state of the REPLACE statement.
@@ -160,7 +169,7 @@ private static void reset() {
public static String replaceInProgram(File program) {
// write the replaced program back to disk
- String newFileName = program+"_MOD";
+ String newFileName = getOutputFileName(program.getAbsolutePath());
Log.warn("Replace.replaceInProgram(): Writing the COBOL program file: " + newFileName);
try {
BufferedWriter writer = (BufferedWriter) EncodingIO.getWriterWithCorrectEncoding(newFileName);
@@ -178,6 +187,7 @@ public static String replaceInProgram(File program) {
} catch (IOException e) {
Log.error("Replace.replaceInProgram(): Error writing the COBOL program file: " + program);
}
+ updateFilePermissions(newFileName);
return newFileName;
}
@@ -186,4 +196,67 @@ public static void showReplaceSets() {
Log.info("Replace.showReplaceSets():" + replaceSet.toString());
}
}
+
+ static String getOutputFileName(String inputFileName) {
+ String newFileNAme = getFilenameWithoutPath(inputFileName);
+
+ String outputDir = Config.getGeneratedTestCodePath();
+
+ if (!outputDir.endsWith(File.separator)) {
+ outputDir = outputDir + File.separator;
+ }
+
+ newFileNAme = outputDir + getFileNameWithoutExtension(newFileNAme)
+ + REPLACED + FILE_PERIOD + getFileExtension(newFileNAme);
+
+ return newFileNAme;
+ }
+
+ /**
+ * Set the file permissions of the generated file according to the configuration
+ * so other users can read/write/execute the file if so configured.
+ * @param newFileName
+ */
+ private static void updateFilePermissions(String newFileName) {
+ String permissions = Config.getGeneratedFilesPermissionAll();
+ FilePermission.setFilePermissionForAllUsers(new File(newFileName), permissions);
+ }
+
+ /** Get the file name without the path.
+ * If there is no path, return the file name as is.
+ * @param filePath the file name with or without path
+ * @return the file name without the path
+ */
+ static String getFilenameWithoutPath(String filePath) {
+ filePath = filePath.trim();
+ filePath = filePath.replace("\\", "/");
+ int lastSlash = filePath.lastIndexOf('/');
+ return filePath.substring(lastSlash + 1);
+ }
+
+ /** Get the file name without the extension.
+ * If there is no extension, return the file name as is.
+ * @param fileName the file name with or without path and extension
+ * @return the file name without the extension
+ */
+ static String getFileNameWithoutExtension(String fileName) {
+ int lastDot = fileName.lastIndexOf('.');
+ if (lastDot == -1) {
+ return fileName;
+ }
+ return fileName.substring(0, lastDot);
+ }
+
+ /** Get the file extension.
+ * If there is no extension, return ".cbl" as default extension.
+ * @param fileName the file name with or without path and extension
+ * @return the file extension including the dot, e.g. ".cbl"
+ */
+ static String getFileExtension(String fileName) {
+ int lastDot = fileName.lastIndexOf('.');
+ if (lastDot == -1) {
+ return DEFAULT_EXTENSION;
+ }
+ return fileName.substring(lastDot + 1);
+ }
}
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/services/filehelpers/PathHelper.java b/src/main/java/org/openmainframeproject/cobolcheck/services/filehelpers/PathHelper.java
index 5cf440c2..476d7077 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/services/filehelpers/PathHelper.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/services/filehelpers/PathHelper.java
@@ -8,6 +8,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.List;
@@ -62,7 +63,8 @@ public static List getMatchingDirectories(String name, String path) thro
Files.walkFileTree(Paths.get(path), directoryFinder);
matchingDirectories = directoryFinder.getMatchingDirectories();
if (matchingDirectories.isEmpty()) {
- Log.warn(Messages.get("WRN001", name, path));
+ Log.error(Messages.get("ERR033", name, path));
+ throw new NoSuchFileException("no directories found at " + path);
}
return matchingDirectories;
}
diff --git a/src/main/java/org/openmainframeproject/cobolcheck/workers/Generator.java b/src/main/java/org/openmainframeproject/cobolcheck/workers/Generator.java
index 68469bbb..207beea9 100644
--- a/src/main/java/org/openmainframeproject/cobolcheck/workers/Generator.java
+++ b/src/main/java/org/openmainframeproject/cobolcheck/workers/Generator.java
@@ -72,8 +72,11 @@ public void prepareAndRunMerge(String programName, String testFileNames) {
Replace.inspectProgram(originalSource);
matchingTestDirectories = PrepareMergeController.getMatchingTestDirectoriesForProgram(programName);
- //replace in the program, return the program name with the corrected source code.
- programName = Replace.replaceInProgram(originalSource);
+ // Handle REPLACE statements in the original source code
+ if (Replace.isReplaceOn()) {
+ //replace in the program, return the file name where the corrected source code was placed.
+ programName = Replace.replaceInProgram(originalSource);
+ }
for (String matchingDirectory : matchingTestDirectories) {
Reader sourceReader = PrepareMergeController.getSourceReader(programName);
@@ -186,8 +189,12 @@ private void writeToSource(String sourceLine) throws IOException {
if (interpreter.shouldCurrentLineBeStubbed()) {
if(interpreter.isReading(Constants.WORKING_STORAGE_SECTION)) {
writerController.writeStubbedLine(interpreter.getCurrentLineAsStatement().getUnNumberedString());
- if (!interpreter.getFileSectionStatements().isEmpty())
+ if (!interpreter.getFileSectionStatements().isEmpty()) {
writerController.writeLines(interpreter.getFileSectionStatements());
+ if (!(interpreter.getFileSectionStatements() == null)) {
+ interpreter.getFileSectionStatements().clear();
+ }
+ }
}
else
writerController.writeStubbedLine(sourceLine);
diff --git a/src/main/resources/org/openmainframeproject/cobolcheck/messages/messages.properties b/src/main/resources/org/openmainframeproject/cobolcheck/messages/messages.properties
index 1a003f20..7c28a76f 100644
--- a/src/main/resources/org/openmainframeproject/cobolcheck/messages/messages.properties
+++ b/src/main/resources/org/openmainframeproject/cobolcheck/messages/messages.properties
@@ -32,6 +32,8 @@ ERR029 = ERR029: NumericFields.setDataTypeOf() was called with null dataType.
ERR030 = ERR030: Command line missing program argument '-p programName' .
ERR031 = ERR031: A test suite with the name %1$s already exists.
ERR032 = ERR032: A test case with the name %1$s already exists in the test suite %2$s.
+ERR033 = ERR033: No test suite directory for program %1$s was found under directory %2$s.
+ERR034 = ERR034: Not abel to put comment indicator in line %1$s. The position was in used by a literal.
WRN001 = WRN001: No test suite directory for program %1$s was found under directory %2$s.
WRN002 = WRN002: No test suite files were found under directory %1$s.
diff --git a/src/test/approvalTest/approvaltest b/src/test/approvalTest/approvaltest
new file mode 100644
index 00000000..0789605e
--- /dev/null
+++ b/src/test/approvalTest/approvaltest
@@ -0,0 +1,17 @@
+./build/approvalTest/cobolcheck -p NUMBERS > ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p ALPHA >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p GREETING >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p FILECOPY >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p MOCKTEST >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p DB2PROG >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p MOCK >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p MOCKPARA >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p MOCKTEST >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p RETURNCODE >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p TESTNESTED >> ./build/approvalTest/actual-output.txt
+./build/approvalTest/cobolcheck -p LONGLINESANDNUMBERS >> ./build/approvalTest/actual-output.txt
+
+
+
+
+
diff --git a/src/test/approvalTest/approvaltestWin.cmd b/src/test/approvalTest/approvaltestWin.cmd
new file mode 100644
index 00000000..17249444
--- /dev/null
+++ b/src/test/approvalTest/approvaltestWin.cmd
@@ -0,0 +1 @@
+build/approvalTest/cobolcheck -p ALPHA DB2PROG DPICNUMBERS FILECOPY GREETING MOCK MOCKPARA MOCKTEST NUMBERS RETURNCODE TESTNESTED LONGLINESANDNUMBERS > build/approvalTest/actual-output.txt
\ No newline at end of file
diff --git a/src/test/approvalTest/expected-output.txt b/src/test/approvalTest/expected-output.txt
new file mode 100644
index 00000000..018e799c
--- /dev/null
+++ b/src/test/approvalTest/expected-output.txt
@@ -0,0 +1,235 @@
+TESTSUITE:
+Verify Cobol Check handles numeric relations properly
+ PASS: 1. Equal sign with literal compare
+**** FAIL: 2. Equal sign with literal compare (should fail)
+ EXPECTED +00000000025.7500000, WAS +00000000025.7400000
+ PASS: 3. Not equal sign with literal compare
+**** FAIL: 4. Not equal sign with literal compare (should fail)
+ EXPECTED +00000000025.7400000, WAS +00000000025.7400000
+ PASS: 5. Not-equal sign with literal compare
+**** FAIL: 6. Not-equal sign with literal compare (should fail)
+ EXPECTED +00000000013.6000000, WAS +00000000013.6000000
+ PASS: 7. Not not-equal sign with literal compare
+**** FAIL: 8. Not not-equal sign with literal compare (should fail)
+ EXPECTED +00000000013.6000000, WAS +00000000013.7000000
+ PASS: 9. Equal sign with ield compare
+**** FAIL: 10. Equal sign with field compare (should fail)
+ EXPECTED +00000000025.7500000, WAS +00000000025.7400000
+ PASS: 11. Not equal sign with field compare
+**** FAIL: 12. Not equal sign with field compare (should fail)
+ EXPECTED +00000000025.7400000, WAS +00000000025.7400000
+ PASS: 13. Not-equal sign with field compare
+**** FAIL: 14. Not-equal sign with field compare (should fail)
+ EXPECTED +00000000025.7400000, WAS +00000000025.7400000
+ PASS: 15. Not not-equal sign with field compare
+**** FAIL: 16. Not not-equal sign with field compare (should fail)
+ EXPECTED +00000000025.7400000, WAS +00000000025.7500000
+ PASS: 17. Less-than sign with literal compare
+**** FAIL: 18. Less-than sign with literal compare (should fail)
+ EXPECTED +00000000018.0660000, WAS +00000000018.0670000
+ PASS: 19. Not less-than sign with literal compare
+**** FAIL: 20. Not less-than sign with literal compare (should fail)
+ EXPECTED +00000000018.0670000, WAS +00000000018.0660000
+ PASS: 21. Less-than sign with field compare
+**** FAIL: 22. Less-than sign with field compare (should fail)
+ EXPECTED +00000000416.0720000, WAS +00000000416.0720000
+ PASS: 23. Not less-than sign with field compare
+**** FAIL: 24. Not less-than sign with field compare (should fail)
+ EXPECTED +00000000416.0720000, WAS +00000000416.0710000
+ PASS: 25. Greater-than sign with literal compare
+**** FAIL: 26. Greater-than sign with literal compare (should fail)
+ EXPECTED +00000000010.0000000, WAS +00000000009.8050000
+ PASS: 27. Not greater-than sign with literal compare
+**** FAIL: 28. Not greater-than sign with literal compare (should fail)
+ EXPECTED +00000000107.7010000, WAS +00000000107.7020000
+ PASS: 29. Greater-than sign with field compare
+**** FAIL: 30. Greater-than sign with field compare (should fail)
+ EXPECTED +00000001766.0314400, WAS +00000001766.0314300
+ PASS: 31. Not greater-than sign with field to field compare
+**** FAIL: 32. Not greater-than sign with field compare (should fail)
+ EXPECTED +00000001766.0314400, WAS +00000001766.0314500
+ PASS: 33. Greater-than-or-equal-to sign with literal compare when greater
+ PASS: 34. Greater-than-or-equal-to sign with literal compare when equal
+**** FAIL: 35. Greater-than-or-equal-to sign with literal compare (should fail)
+ EXPECTED +00000000010.0000000, WAS +00000000009.8050000
+ PASS: 36. Not greater-than-or-equal-to sign with literal compare when less
+**** FAIL: 37. Not greater-than-or-equal-to sign with literal compare when equal (should fail)
+ EXPECTED +00000000018.0670000, WAS +00000000018.0670000
+**** FAIL: 38. Greater-than-or-equal-to sign with literal compare when greater (should fail)
+ EXPECTED +00000000013.4400000, WAS +00000000013.4500000
+ PASS: 39. Greater-than-or-equal-to-sign with field compare when equal
+ PASS: 40. Greater-than-or-equal-to-sign with field compare when greater
+**** FAIL: 41. Greater-than-or-equal-to-sign with field compare when less (should fail)
+ EXPECTED +00000000475.0620000, WAS +00000000475.0610000
+ PASS: 42. Not greater-than-or-equal-to-sign with field compare when less
+**** FAIL: 43. Not greater-than-or-equal-to-sign with field compare when equal (should fail)
+ EXPECTED +00000000475.0620000, WAS +00000000475.0620000
+**** FAIL: 44. Not greater-than-or-equal-to-sign with field compare when greater (should fail)
+ EXPECTED +00000000475.0620000, WAS +00000000475.0630000
+ PASS: 45. Less-than-or-equal-to-sign with field compare when equal
+ PASS: 46. Less-than-or-equal-to-sign with field compare when less
+**** FAIL: 47. Less-than-or-equal-to-sign with field compare when greater (should fail)
+ EXPECTED +00000000475.0620000, WAS +00000000475.0630000
+ PASS: 48. Not less-than-or-equal-to-sign with field compare when greater
+**** FAIL: 49. Not greater-than-or-equal-to-sign with field compare when equal (should fail)
+ EXPECTED +00000000475.0620000, WAS +00000000475.0620000
+**** FAIL: 50. Not greater-than-or-equal-to-sign with field compare when less (should fail)
+ EXPECTED +00000000475.0630000, WAS +00000000475.0620000
+ PASS: 51. Display Numeric field equals literal
+
+ 51 TEST CASES WERE EXECUTED
+ 26 PASSED
+ 25 FAILED
+=================================================
+TESTSUITE:
+Tests of alphanumeric expectations
+ PASS: 1. Equality with an alphanumeric literal using TO BE
+ PASS: 2. Equality with an alphanumeric literal using TO EQUAL
+ PASS: 3. Equality with an alphanumeric literal using '='
+ PASS: 4. Equality with an alphanumeric literal and reference modification
+ PASS: 5. Non-equality with an alphanumeric literal using TO BE
+ PASS: 6. Non-equality with an alphanumeric literal using TO EQUAL
+ PASS: 7. Non-equality with an alphanumeric literal using '!='
+ PASS: 8. Non-equality with an alphanumeric literal and reference modification
+ PASS: 9. Greater-than sign with an alphanumeric literal
+ PASS: 10. Less-than sign with an alphanumeric literal
+ PASS: 11. Not greater-than sign with an alphanumeric literal
+ PASS: 12. Not less-than sign with an alphanumeric literal
+ PASS: 13. Display numeric
+
+ 13 TEST CASES WERE EXECUTED
+ 13 PASSED
+ 0 FAILED
+=================================================
+TESTSUITE:
+Greeting includes the user name when it is provided
+**** FAIL: 1. When message type is greeting it returns Hello, James!
+ EXPECTED , WAS
+**** FAIL: 2. When message type is farewell it returns Goodbye, James!
+ EXPECTED , WAS
+ PASS: 3. User name for greeting and farewell are consistent
+TESTSUITE:
+Greeting returns the appropriate message based on message type
+**** FAIL: 4. When message type is greeting it returns 'Hello, World!'
+ EXPECTED , WAS
+ PASS: 5. try numerical compare
+**** FAIL: 6. try 88 level compare
+ EXPECTED , WAS
+**** FAIL: 7. When message type is farewell it returns See you later, alligator!
+ EXPECTED , WAS
+**** FAIL: 8. Message type greeting is not true
+ EXPECTED , WAS
+
+ 8 TEST CASES WERE EXECUTED
+ 2 PASSED
+ 6 FAILED
+=================================================
+TESTSUITE:
+Tests for a sequential file copy program
+ PASS: 1. Output fields are populated from the input record
+ PASS: 2. Output fields are populated from the input record
+
+ 2 TEST CASES WERE EXECUTED
+ 2 PASSED
+ 0 FAILED
+=================================================
+TESTSUITE:
+Before and after tests 1
+ PASS: 1. Before sets value
+ PASS: 2. After sets value
+ PASS: 3. Before happens before Testcase
+ PASS: 4. After happens after Testcase (part 1)
+ PASS: 5. After happens after Testcase (part 2)
+TESTSUITE:
+Before and after tests 2
+ PASS: 6. VERIFY EXACT 0 ACCESSES TO SECTION 000-START
+ PASS: 7. VERIFY EXACT 0 ACCESSES TO SECTION 100-WELCOME
+ PASS: 8. Only global mocks apply in before/after (part 1)
+ PASS: 9. Only global mocks apply in before/after (part 1)
+ PASS: 10. Only global mocks apply in before/after (part 1)
+ PASS: 11. Only global mocks apply in before/after (part 2)
+ PASS: 12. Only global mocks apply in before/after (part 2)
+ PASS: 13. Only global mocks apply in before/after (part 2)
+ PASS: 14. Only global mocks apply in before/after (part 3)
+ PASS: 15. Only global mocks apply in before/after (part 3)
+ PASS: 16. Only global mocks apply in before/after (part 3)
+TESTSUITE:
+Before and after tests 3
+ PASS: 17. Before proceeds after from last testcase (part 1)
+ PASS: 18. Before proceeds after from last testcase (part 2)
+TESTSUITE:
+General tests
+ PASS: 19. Welcome section performs as intended
+ PASS: 20. Welcome section performs as intended
+ PASS: 21. Goodbye section performs as intended
+ PASS: 22. Goodbye section performs as intended
+ PASS: 23. Change-1 changes hello to bye
+ PASS: 24. Change-1 changes hello to bye
+ PASS: 25. Change-1 changes bye to hello
+ PASS: 26. Change-1 changes bye to hello
+ PASS: 27. Change-2 changes hi to see you
+ PASS: 28. Change-2 changes hi to see you
+ PASS: 29. Change-2 changes see you to hi
+ PASS: 30. Change-2 changes see you to hi
+ PASS: 31. Switches values
+ PASS: 32. Switches values
+TESTSUITE:
+Mock Call statements test
+ PASS: 33. Simple call mock works
+ PASS: 34. Simple call mock works
+ PASS: 35. VERIFY EXACT 1 ACCESS TO CALL 'PROG1'
+ PASS: 36. Simple global call mock works
+ PASS: 37. Simple global call mock works
+ PASS: 38. VERIFY EXACT 1 ACCESS TO CALL 'PROG1'
+ PASS: 39. Call mock with argument works
+ PASS: 40. Call mock with argument works
+ PASS: 41. VERIFY EXACT 1 ACCESS TO CALL VALUE-2
+ PASS: 42. Call mock with content reference for arguments work
+ PASS: 43. Call mock with content reference for arguments work
+ PASS: 44. Call mock with content reference for arguments work
+ PASS: 45. VERIFY EXACT 2 ACCESSES TO CALL 'PROG3'
+ PASS: 46. Paragraph mock is called and call mock is ignored
+ PASS: 47. Paragraph mock is called and call mock is ignored
+ PASS: 48. Paragraph mock is called and call mock is ignored
+ PASS: 49. VERIFY EXACT 1 ACCESS TO PARAGRAPH 800-MAKE-CALL
+ PASS: 50. VERIFY EXACT 0 ACCESSES TO CALL 'PROG3'
+**** FAIL: 51. Global call mock is not overwritten by local call mock (Should fail)
+ EXPECTED , WAS
+TESTSUITE:
+Mock Sections And Paragraphs
+ PASS: 52. Global mock behaves as intended
+ PASS: 53. Global mock behaves as intended
+ PASS: 54. VERIFY EXACT 1 ACCESS TO SECTION 000-START
+ PASS: 55. Local mock overwrites global mock
+ PASS: 56. Local mock overwrites global mock
+ PASS: 57. VERIFY AT LEAST 2 ACCESSES TO SECTION 000-START
+ PASS: 58. Multiple local mocks behaves as intended (First Verify should fail)
+ PASS: 59. Multiple local mocks behaves as intended (First Verify should fail)
+**** FAIL: 60. VERIFY ACCESSES TO SECTION 000-START
+ EXPECTED EXACT 5 ACCESSES, WAS 2
+ PASS: 61. VERIFY NO MORE THAN 10 ACCESSES TO SECTION 100-WELCOME
+ PASS: 62. VERIFY EXACT 0 ACCESSES TO PARAGRAPH 500-SWITCH
+ PASS: 63. Empty local mock makes section do nothing
+ PASS: 64. Empty local mock makes section do nothing
+ PASS: 65. VERIFY EXACT 1 ACCESS TO PARAGRAPH 500-SWITCH
+ PASS: 66. Local and global mocks can be used together
+ PASS: 67. Local and global mocks can be used together
+ PASS: 68. If no local or global mock run source code
+ PASS: 69. If no local or global mock run source code
+ PASS: 70. Global paragraph mock works
+ PASS: 71. Global paragraph mock works
+
+ 71 TEST CASES WERE EXECUTED
+ 69 PASSED
+ 2 FAILED
+=================================================
+TESTSUITE:
+Verify Cobol Check handles decimal is comma properly
+ PASS: 1. simple expect for number
+ PASS: 2. simple expect for number
+ PASS: 3. VERIFY EXACT 1 ACCESS TO PARAGRAPH 100-ASSIGN
+
+ 3 TEST CASES WERE EXECUTED
+ 3 PASSED
+ 0 FAILED
+=================================================
\ No newline at end of file
diff --git a/src/test/approvalTest/readme.md b/src/test/approvalTest/readme.md
new file mode 100644
index 00000000..5f0e7a3d
--- /dev/null
+++ b/src/test/approvalTest/readme.md
@@ -0,0 +1,59 @@
+# Approval Testing Documentation
+version 0.9 - not final yet...
+To use this setup, the verifyAction must be updated.
+
+## Approval Test Flow
+Approval testing verifies that the output of code matches an approved reference. The typical flow is:
+
+1. Run the approval tests.
+2. The test generates and output file based on the current code.
+3. The output is compared to a previously approved file.
+4. If the output matches, the test passes.
+5. If there are differences, the test fails and highlights the changes.
+6. Review the differences. If correct, approve the new output by replacing the approved file.
+7. Commit the updated approved files to version control.
+
+This process helps ensure that changes to code are intentional and reviewed.
+
+## Gradle Task and Used Files
+The approval tests are run using a custom Gradle task defined in build.gradle.
+This task executes the approval test script, which generates output files for comparison.
+
+**Key files:**
+- ```build.gradle```: Contains the configuration and task for running approval tests.
+- ```src/test/approvalTest/approvaltest```: The main script for running approval tests (use ```approvaltestWin.cmd``` on Windows).
+- ```src/test/approvalTest/received/```: Directory where test output files are generated.
+- ```src/test/approvalTest/approved/```: Directory containing the approved reference files.
+- ```src/test/approvalTest/readme.md```: Documentation for the approval test flow.
+
+The Gradle task automates running the approval test script and manages the comparison between received and approved files.
+If differences are found, you can review and update the approved files as needed.
+
+## Workings
+1. Developer maintains the code and the approved file that resides under ```src/test/approvalTest/```.
+2. When (the developer runs gradle or) the Gradle task executes, files are copied to ```build/approvalTest/```. During this process, the jar version is injected into the script.
+3. The script generates a new output file in the ```build/approvalTest/``` directory.
+
+## Script connections
+### Windows
+
+```mermaid
+graph LR;
+A[build.gradle.task.approvalTest] --> B[approvaltestWin.cmd]
+B --> C[cobolcheck.cmd]
+C --> D[cobolcheck.jar]
+```
+
+### Linux
+```mermaid
+graph LR;
+ A[build.gradle.task.approvalTest]-->B[approvaltest.cmd];
+ B-->C[cobolcheck];
+ C-->D[cobolcheck.jar];
+```
+
+## To do
+- [ ] Make it clear what tests are to be performed. developer must be able to remove/add tests easily
+- [ ] Split the cobol-check script into smaller parts to handle one test at a time and compare results 1:1
+- [ ] Improve the script to handle more dynamic scenarios for the developer
+- [ ] Finalize version 1.0
diff --git a/src/test/approvalTest/templates/cobolcheck b/src/test/approvalTest/templates/cobolcheck
new file mode 100644
index 00000000..304f8c39
--- /dev/null
+++ b/src/test/approvalTest/templates/cobolcheck
@@ -0,0 +1,2 @@
+#!/bin/sh
+java -jar bin/cobol-check-@VERSION@.jar $@
\ No newline at end of file
diff --git a/src/test/approvalTest/templates/cobolcheck.cmd b/src/test/approvalTest/templates/cobolcheck.cmd
new file mode 100644
index 00000000..7da8e498
--- /dev/null
+++ b/src/test/approvalTest/templates/cobolcheck.cmd
@@ -0,0 +1,2 @@
+@echo off
+java -jar bin\cobol-check-@VERSION@.jar %*
\ No newline at end of file
diff --git a/src/test/cobol/FDTEST/FDTEST01.CUT b/src/test/cobol/FDTEST/FDTEST01.CUT
new file mode 100644
index 00000000..8bb7739c
--- /dev/null
+++ b/src/test/cobol/FDTEST/FDTEST01.CUT
@@ -0,0 +1,27 @@
+ TestSuite "Verify FILE SECTION variables are handled properly"
+
+ TestCase "simple expect for number"
+ Move 12 to WS-A-VAR-2
+ Expect WS-A-VAR-2 to equal 12
+
+ TestCase "simple expect for string"
+ Move "H" to WS-A-VAR-1
+ Expect WS-A-VAR-1 to equal "H"
+
+ TestCase "simple expect for 88 level"
+ move 42 to WS-A-VAR-2
+ Expect SW-VAR-2-IS-42 to be true
+
+ TestCase "simple expect for 88 level false"
+ move 43 to WS-A-VAR-2
+ Expect SW-VAR-2-IS-42 to be false
+
+
+ TestCase "expect for 88 on two lines"
+ move 'HELLO' to WS-A-VAR-3
+ Expect SW-VAR-3-IS-HELLO to be true
+
+ move 'WORLD' to WS-A-VAR-3
+ Expect SW-VAR-3-IS-HELLO to be false
+ Expect SW-VAR-3-IS-WORLD to be true
+
diff --git a/src/test/cobol/MOCKTEST/BeforeAfterTest.cut b/src/test/cobol/MOCKTEST/BeforeAfterTest.cut
index 1218d33c..9d180be9 100644
--- a/src/test/cobol/MOCKTEST/BeforeAfterTest.cut
+++ b/src/test/cobol/MOCKTEST/BeforeAfterTest.cut
@@ -1,4 +1,4 @@
- TestSuite "Before and after tests 1"
+TestSuite "Suite A: Before and after tests 1"
BEFORE-EACH
MOVE "prepare" TO VALUE-1
@@ -8,25 +8,26 @@
MOVE "clean" TO VALUE-2
END-AFTER
- TestCase "Before sets value"
+ TestCase "A-1: BEFORE-EACH sets value"
EXPECT VALUE-1 TO BE "prepare"
+ * validate the initial state of VALUE-2, before AFTER-EACH runs and is validated in the next test
+ EXPECT VALUE-2 TO BE "initial"
- TestCase "After sets value"
+ TestCase "A-2: AFTER-EACH sets value"
EXPECT VALUE-2 TO BE "clean"
- TestCase "Before happens before Testcase"
+ TestCase "A-3: Before happens before Testcase"
MOVE "during" TO VALUE-1
EXPECT VALUE-1 TO BE "during"
- TestCase "After happens after Testcase (part 1)"
+ TestCase "A-4: After happens after Testcase (part 1)"
MOVE "during" TO VALUE-2
EXPECT VALUE-2 TO BE "during"
- TestCase "After happens after Testcase (part 2)"
+ TestCase "A-5: After happens after Testcase (part 2)"
EXPECT VALUE-2 TO BE "clean"
-
- TestSuite "Before and after tests 2"
+TestSuite "Suite A2: Before and after tests 2"
BEFORE-EACH
MOVE "before1" TO VALUE-1
MOVE "before2" TO VALUE-2
@@ -47,11 +48,11 @@
MOVE "GlobalMockedSection2" TO VALUE-3
END-MOCK
- TestCase "Performs of mocked items does not influence count"
+ TestCase "A2-1: Performs of mocked items does not influence count"
VERIFY SECTION 000-START HAPPENED 0 TIMES
VERIFY SECTION 100-WELCOME NEVER HAPPENED
- TestCase "Only global mocks apply in before/after (part 1)"
+ TestCase "A2-2: Only global mocks apply in before/after (part 1)"
MOVE "arg3" TO VALUE-3
MOCK SECTION 000-START
MOVE "LocalMockedSection1" TO VALUE-1
@@ -63,7 +64,7 @@
EXPECT VALUE-2 TO BE "before2"
EXPECT VALUE-3 TO BE "arg3"
- TestCase "Only global mocks apply in before/after (part 2)"
+ TestCase "A2-3: Only global mocks apply in before/after (part 2)"
MOCK SECTION 000-START
MOVE "LocalMockedSection1" TO VALUE-1
END-MOCK
@@ -74,7 +75,7 @@
EXPECT VALUE-2 TO BE "before2"
EXPECT VALUE-3 TO BE "GlobalMockedSection2"
- TestCase "Only global mocks apply in before/after (part 3)"
+ TestCase "A2-4: Only global mocks apply in before/after (part 3)"
MOCK SECTION 000-START
MOVE "LocalMockedSection1" TO VALUE-1
END-MOCK
@@ -85,7 +86,7 @@
EXPECT VALUE-2 TO BE "before2"
EXPECT VALUE-3 TO BE "GlobalMockedSection2"
- TestSuite "Before and after tests 3"
+TestSuite "Suite A3: Before and after tests 3"
BEFORE EACH
MOVE "before" TO VALUE-1
@@ -95,8 +96,9 @@
MOVE "after" TO VALUE-1
END-AFTER
- TestCase "Before proceeds after from last testcase (part 1)"
+ TestCase "A3-1: Before proceeds after from last testcase (part 1)"
EXPECT VALUE-1 TO BE "before"
- TestCase "Before proceeds after from last testcase (part 2)"
- EXPECT VALUE-1 TO BE "before"
\ No newline at end of file
+ TestCase "A3-2: Before proceeds after from last testcase (part 2)"
+ EXPECT VALUE-1 TO BE "before"
+ .
\ No newline at end of file
diff --git a/src/test/cobol/MOCKTEST/GeneralTest.cut b/src/test/cobol/MOCKTEST/GeneralTest.cut
index 4349a3e5..754c0a15 100644
--- a/src/test/cobol/MOCKTEST/GeneralTest.cut
+++ b/src/test/cobol/MOCKTEST/GeneralTest.cut
@@ -1,44 +1,45 @@
- TestSuite "General tests"
+TestSuite "Suite B: General tests"
- TestCase "No Expect or Verify"
+ TestCase "B-1: No Expect or Verify"
PERFORM 100-WELCOME
- TestCase "Welcome section performs as intended"
+ TestCase "B-2: Welcome section performs as intended"
PERFORM 100-WELCOME
Expect VALUE-1 to be "Hello"
Expect VALUE-2 to be "Hi"
- TestCase "Goodbye section performs as intended"
+ TestCase "B-3: Goodbye section performs as intended"
PERFORM 200-GOODBYE
Expect VALUE-1 to be "Bye"
Expect VALUE-2 to be "See you"
- TestCase "Change-1 changes hello to bye"
+ TestCase "B-4: Change-1 changes hello to bye"
PERFORM 100-WELCOME
PERFORM 300-CHANGE-1
Expect VALUE-1 to be "Bye"
Expect VALUE-2 to be "Hi"
- TestCase "Change-1 changes bye to hello"
+ TestCase "B-5: Change-1 changes bye to hello"
PERFORM 200-GOODBYE
PERFORM 300-CHANGE-1
Expect VALUE-1 to be "Hello"
Expect VALUE-2 to be "See you"
- TestCase "Change-2 changes hi to see you"
+ TestCase "B-6: Change-2 changes hi to see you"
PERFORM 100-WELCOME
PERFORM 400-CHANGE-2
Expect VALUE-1 to be "Hello"
Expect VALUE-2 to be "See you"
- TestCase "Change-2 changes see you to hi"
+ TestCase "B-7: Change-2 changes see you to hi"
PERFORM 200-GOODBYE
PERFORM 400-CHANGE-2
Expect VALUE-1 to be "Bye"
Expect VALUE-2 to be "Hi"
- TestCase "Switches values"
+ TestCase "B-8: Switches values"
PERFORM 100-WELCOME
PERFORM 500-SWITCH
Expect VALUE-1 to be "Hi"
Expect VALUE-2 to be "Hello"
+ .
\ No newline at end of file
diff --git a/src/test/cobol/MOCKTEST/MockCallTest.cut b/src/test/cobol/MOCKTEST/MockCallTest.cut
index 9022b2b3..5c4943f7 100644
--- a/src/test/cobol/MOCKTEST/MockCallTest.cut
+++ b/src/test/cobol/MOCKTEST/MockCallTest.cut
@@ -1,15 +1,16 @@
- * Test for mocking call statements
+* Test for mocking call statements
* Writing a comment to test stuff
*
*Yeah
*
- TestSuite "Mock Call statements test"
+ TestSuite "Suite C: Mock Call statements test"
MOCK CALL 'PROG1'
MOVE "Global PROG1" TO VALUE-1
END-MOCK
+
- TestCase "Simple call mock works"
+ TestCase "C-1 Simple call mock works"
MOCK CALL 'PROG1'
MOVE "From mocked PROG1" TO VALUE-1
END-MOCK
@@ -18,13 +19,13 @@
EXPECT VALUE-2 TO BE "arg2"
VERIFY CALL 'PROG1' HAPPENED ONCE
- TestCase "Simple global call mock works"
+ TestCase "C-2 Simple global call mock works"
PERFORM 600-MAKE-CALL
EXPECT VALUE-1 TO BE "Global PROG1"
EXPECT VALUE-2 TO BE "arg2"
VERIFY CALL 'PROG1' HAPPENED ONCE
- TestCase "Call mock with argument works"
+ TestCase "C-3 Call mock with argument works"
MOCK CALL VALUE-2 USING VALUE-1
MOVE "From mocked PROG2" TO VALUE-1
END-MOCK
@@ -34,7 +35,7 @@
VERIFY CALL VALUE-2 USING VALUE-1
HAPPENED ONCE
- TestCase "Call mock with field and no arguments work"
+ TestCase "C-4 Call mock with field and no arguments work"
MOCK CALL VALUE-2
MOVE "From mocked PROG2" TO VALUE-1
END-MOCK
@@ -49,7 +50,7 @@
*
*Yeah
*
- TestCase "Call mock with content reference for arguments work"
+ TestCase "C-5 Call mock with content reference for arguments work"
MOCK CALL 'PROG3' USING
BY CONTENT VALUE-1,
BY VALUE VALUE-2,
@@ -68,7 +69,7 @@
VALUE-3
HAPPENED 2 TIMES
- TestCase "Call mock with content reference for arguments with one comma work"
+ TestCase "C-6 Call mock with content reference for arguments with one comma work"
MOCK CALL 'PROG3' USING
BY CONTENT VALUE-1,
BY VALUE VALUE-2
@@ -88,7 +89,7 @@
HAPPENED 2 TIMES
- TestCase "Call mock with content reference for arguments without comma work"
+ TestCase "C-7 Call mock with content reference for arguments without comma work"
MOCK CALL 'PROG3' USING
BY CONTENT VALUE-1
BY VALUE VALUE-2
@@ -108,7 +109,7 @@
HAPPENED 2 TIMES
- TestCase "Paragraph mock is called and call mock is ignored"
+ TestCase "C-8 Paragraph mock is called and call mock is ignored"
MOCK CALL 'PROG3' USING
BY CONTENT VALUE-1,
BY VALUE VALUE-2,
@@ -133,14 +134,14 @@
VALUE-3
NEVER HAPPENED
- TestCase "Global call mock is not overwritten by local call mock (Should fail)"
+ TestCase "C-9 Global call mock is overwritten by local call mock"
MOCK CALL 'PROG1'
MOVE "Local PROG1" TO VALUE-1
END-MOCK
PERFORM 600-MAKE-CALL
- EXPECT VALUE-1 TO BE "Global PROG1"
+ EXPECT VALUE-1 TO BE "Local PROG1"
- TestCase "Mock of call with exception handling works"
+ TestCase "C-10 Mock of call with exception handling works"
MOCK CALL 'PROGRAM' USING
VALUE-1
MOVE "worked" TO VALUE-1
@@ -149,7 +150,7 @@
PERFORM 900-MAKE-CALL
EXPECT VALUE-1 TO BE "worked"
- TestCase "Mock of call with exception handling ending with period works"
+ TestCase "C-11 Mock of call with exception handling ending with period works"
MOCK CALL 'PROGRAM' USING VALUE-1
MOVE "worked" TO VALUE-1
END-MOCK
@@ -157,7 +158,7 @@
PERFORM 1000-MAKE-CALL
EXPECT VALUE-1 TO BE "worked"
- TestCase "Mock of call with a new call as exception handling works"
+ TestCase "C-12 Mock of call with a new call as exception handling works"
MOCK CALL 'PROGRAM' USING VALUE-1
MOVE "worked" TO VALUE-1
END-MOCK
@@ -165,7 +166,7 @@
PERFORM 1100-MAKE-CALL
EXPECT VALUE-1 TO BE "worked"
- TestCase "Mock of call with a new call as exception handling ending with period works"
+ TestCase "C-13 Mock of call with a new call as exception handling ending with period works"
MOCK CALL 'PROGRAM' USING VALUE-1
MOVE "worked" TO VALUE-1
END-MOCK
@@ -178,7 +179,7 @@
VERIFY CALL 'PROGRAM' USING VALUE-1 HAPPENED ONCE
VERIFY CALL 'PROGRAM2' USING VALUE-1 NEVER HAPPENED
- TestCase "Mock of calls in series with exception handling works"
+ TestCase "C-14 Mock of calls in series with exception handling works"
MOCK CALL 'PROGRAM' USING VALUE-1
MOVE "worked" TO VALUE-1
END-MOCK
@@ -187,12 +188,13 @@
EXPECT VALUE-1 TO BE "worked"
VERIFY CALL 'PROGRAM' USING VALUE-1 HAPPENED 2 TIMES
- TestCase "CALL MOCK using parameters with IN are recognized correctly"
+ TestCase "C-15 CALL MOCK using parameters with IN are recognized correctly"
MOVE SPACES TO OUTPUT-VALUE
- MOCK CALL 'MYCOBOL' USING ACTION-PARAM,
- BOOK-PARAM in COBOL-STRUCTURE,
- OUTPUT-PARAM.
+ MOCK CALL 'MYCOBOL' USING ACTION-value,
+ BOOK-value in book-STRUCTURE,
+ OUTPUT-value.
MOVE "MOCKED" TO OUTPUT-VALUE
END-MOCK
- PERFORM 610-MAKE-CALL-STRUCTURE
- EXPECT OUTPUT-VALUE TO BE "MOCKED"
\ No newline at end of file
+ PERFORM 610-CALL-VALUE-IN-STRUCTURE
+ EXPECT OUTPUT-VALUE TO BE "MOCKED"
+ .
\ No newline at end of file
diff --git a/src/test/cobol/MOCKTEST/MockSectionsAndParagraphsTest.cut b/src/test/cobol/MOCKTEST/MockSectionsAndParagraphsTest.cut
index a5695760..efca67ca 100644
--- a/src/test/cobol/MOCKTEST/MockSectionsAndParagraphsTest.cut
+++ b/src/test/cobol/MOCKTEST/MockSectionsAndParagraphsTest.cut
@@ -1,4 +1,4 @@
- TestSuite "Mock Sections And Paragraphs"
+ TestSuite "Suite D: Mock Sections And Paragraphs"
MOCK SECTION 000-START
PERFORM 100-WELCOME
@@ -14,14 +14,14 @@
END-EVALUATE
END-MOCK
- TestCase "Global mock behaves as intended"
+ TestCase "D-1: Global mock behaves as intended"
PERFORM 000-START
Expect VALUE-1 to be "Hi"
Expect VALUE-2 to be "Hello"
VERIFY SECTION 000-START HAPPENED ONCE
VERIFY PARAGRAPH 300-CHANGE-1 HAPPENED ZERO TIMES
- TestCase "Local mock overwrites global mock"
+ TestCase "D-2: Local mock overwrites global mock"
MOCK SECTION 000-START
MOVE "This is" TO VALUE-1
MOVE "a mock" TO VALUE-2
@@ -32,7 +32,7 @@
Expect VALUE-2 to be "a mock"
VERIFY SECTION 000-START HAPPENED AT LEAST 2 TIMES
- TestCase "Multiple local mocks behaves as intended (First Verify should fail)"
+ TestCase "D-3: Multiple local mocks behaves as intended"
MOCK SECTION 000-START
MOVE "This is" TO VALUE-1
MOVE "a mock" TO VALUE-2
@@ -43,17 +43,19 @@
MOCK SECTION 100-WELCOME
MOVE "I'll leave VALUE-2 alone" TO VALUE-1
END-MOCK
- MOCK PARAGRAPH 500-SWITCH
- END-MOCK
+
+ MOCK PARAGRAPH 500-SWITCH END-MOCK
+
PERFORM 000-START
PERFORM 000-START
- VERIFY SECTION 000-START HAPPENED 5 TIMES
+
+ VERIFY SECTION 000-START HAPPENED 2 TIMES
Expect VALUE-1 to be "I'll leave VALUE-2 alone"
- VERIFY SECTION 100-WELCOME HAPPENED NO MORE THAN 10 TIMES
+ VERIFY SECTION 100-WELCOME HAPPENED 6 TIMES
VERIFY PARAGRAPH 500-SWITCH NEVER HAPPENED
Expect VALUE-2 to be "a mock"
- TestCase "Empty local mock makes section do nothing"
+ TestCase "D-4: Empty local mock makes section do nothing"
MOCK PARAGRAPH 500-SWITCH
END-MOCK
PERFORM 100-WELCOME
@@ -62,7 +64,7 @@
Expect VALUE-2 to be "Hi"
VERIFY PARAGRAPH 500-SWITCH HAPPENED ONCE
- TestCase "Local and global mocks can be used together"
+ TestCase "D-5: Local and global mocks can be used together"
MOCK SECTION 100-WELCOME
MOVE "Mockity-" TO VALUE-1
MOVE "Mock" TO VALUE-2
@@ -71,42 +73,42 @@
Expect VALUE-1 to be "Mock"
Expect VALUE-2 to be "Mockity-"
- TestCase "If no local or global mock run source code"
+ TestCase "D-6: If no local or global mock run source code"
PERFORM 100-WELCOME
Expect VALUE-1 to be "Hello"
Expect VALUE-2 to be "Hi"
- TestCase "Global paragraph mock works"
+ TestCase "D-7: Global paragraph mock works"
PERFORM 100-WELCOME
PERFORM 300-CHANGE-1
Expect VALUE-1 to be "MOCKED"
Expect VALUE-2 to be "Hi"
- TestCase "Mock with EXIT SECTION in source works"
+ TestCase "D-8: Mock with EXIT SECTION in source works"
MOCK SECTION 400-CHANGE-2
MOVE "Exit section is handled correctly" TO VALUE-1
END-MOCK
PERFORM 400-CHANGE-2
Expect VALUE-1 to be "Exit section is handled correctly"
- TestCase "Mock one-liner with end mock on next line"
+ TestCase "D-9: Mock one-liner with end mock on next line"
MOCK SECTION 400-CHANGE-2 MOVE "it works1" TO VALUE-1
END-MOCK
PERFORM 400-CHANGE-2
Expect VALUE-1 to be "it works1"
- TestCase "Mock one-liner with content"
+ TestCase "D-10: Mock one-liner with content"
MOCK SECTION 400-CHANGE-2 MOVE "it works2" TO VALUE-1 END-MOCK
PERFORM 400-CHANGE-2
Expect VALUE-1 to be "it works2"
- TestCase "Mock content with end-mock on the same line"
+ TestCase "D-11: Mock content with end-mock on the same line"
MOCK SECTION 400-CHANGE-2
MOVE "it works3" TO VALUE-1 END-MOCK
PERFORM 400-CHANGE-2
Expect VALUE-1 to be "it works3"
- TestCase "Mock content on multiple lines, with end-mock on the same line as some content"
+ TestCase "D-12: content on multiple lines, flow style"
MOCK SECTION 400-CHANGE-2 MOVE "it works4" TO VALUE-1
MOVE "it works5" TO VALUE-2 END-MOCK
PERFORM 400-CHANGE-2
diff --git a/src/test/cobol/REPLAC/REPLAC_01.cut b/src/test/cobol/REPLAC/REPLAC_01.cut
new file mode 100644
index 00000000..75b664f4
--- /dev/null
+++ b/src/test/cobol/REPLAC/REPLAC_01.cut
@@ -0,0 +1,8 @@
+ TESTSUITE
+ "Greeting includes the user name when it is provided"
+
+ TESTCASE "When message type is greeting it returns Hello, James!"
+ SET MESSAGE-IS-GREETING TO TRUE
+ MOVE "James" TO WS-FRIEND
+ PERFORM 2000-SPEAK
+ EXPECT WS-GREETING TO BE "Hello, James!"
diff --git a/src/test/cobol/WS88LEVEL/MOVE88LEVELS.CUT b/src/test/cobol/WS88LEVEL/MOVE88LEVELS.CUT
new file mode 100644
index 00000000..b75f21c0
--- /dev/null
+++ b/src/test/cobol/WS88LEVEL/MOVE88LEVELS.CUT
@@ -0,0 +1,49 @@
+TestSuite "Suite 88 levels in program"
+ TestCase "t-01: WS 88 level variable on one line"
+
+ MOVE 'A' TO WS-88-LEVELS
+ perform 001-INITIALIZE
+ expect level-88-value-1 to be true
+ expect level-88-value-2 to be false
+
+ Testcase "t-02: WS 88 level variable on multiple lines"
+ MOVE 'B' TO WS-88-LEVELS
+ perform 001-INITIALIZE
+ expect level-88-all to be true
+
+ TestCase "t-03: WS 88 level variable"
+ MOVE 4 TO WS-88-LEVELS-NUMERIC
+ perform 001-INITIALIZE
+ expect SW-88-NUMERIC-LOW to be true
+ expect SW-88-NUMERIC-MED-ODD to be false
+ expect SW-88-NUMERIC-MED-EVEN to be false
+
+ TestCase "t-04: WS 88 level variable bigger value"
+ MOVE 32 TO WS-88-LEVELS-NUMERIC
+ perform 001-INITIALIZE
+ expect SW-88-NUMERIC-LOW to be false
+ expect SW-88-NUMERIC-MEDIUM to be true
+ expect SW-88-NUMERIC-MED-ODD to be false
+ expect SW-88-NUMERIC-MED-EVEN to be true
+
+ TestCase "t-05: FD 88 level variable are present"
+ MOVE 'Z' TO FD-88-LEVELS
+ perform 001-INITIALIZE
+ expect FD-88-VALUE-3 to be false
+ expect FD-88-VALUE-4 to be true
+ expect FD-88-ALL to be true
+
+ TestCase "t-06: FD 88 level variable numeric"
+ MOVE 15 TO FD-88-LEVELS-NUMERIC
+ perform 001-INITIALIZE
+ expect FD-88-NUMERIC-LOW to be false
+ expect FD-88-NUMERIC-MEDIUM to be true
+ expect FD-88-NUMERIC-HIGH to be false
+
+
+
+
+
+ .
+
+
diff --git a/src/test/java/org/openmainframeproject/cobolcheck/InterpreterControllerTest.java b/src/test/java/org/openmainframeproject/cobolcheck/InterpreterControllerTest.java
index 20dd88ce..7ff08d45 100644
--- a/src/test/java/org/openmainframeproject/cobolcheck/InterpreterControllerTest.java
+++ b/src/test/java/org/openmainframeproject/cobolcheck/InterpreterControllerTest.java
@@ -9,6 +9,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
+import org.openmainframeproject.cobolcheck.services.cobolLogic.DataType;
import java.io.BufferedReader;
import java.io.IOException;
@@ -744,7 +745,13 @@ public void it_updates_numeric_fields() throws IOException {
while (interpreterController.interpretNextLine() != null){
interpreterController.interpretNextLine();
}
- assertEquals("PACKED_DECIMAL",
+
+ // list the statements
+ for (String s : interpreterController.getFileSectionStatements()){
+ System.out.println("Section Statement: " + s);
+ }
+
+ assertEquals("PACKED_DECIMAL",
interpreterController.getNumericFieldDataTypeFor("WS-COUNT").name());
assertEquals("DISPLAY_NUMERIC",
interpreterController.getNumericFieldDataTypeFor("WS-DISPLAY-NUM2").name());
@@ -986,14 +993,14 @@ public void it_registers_DB2Copybook_on_multiple_lines_as_stub() throws IOExcept
boolean testsRan = false;
String currentLine = "";
+ String stubbedTriggerLine = "";
while (currentLine != null){
currentLine = interpreterController.interpretNextLine();
- if (currentLine != null && currentLine.contains("EXEC SQL")) {
- assertTrue(interpreterController.shouldCurrentLineBeStubbed());
- testsRan = true;
+ if (interpreterController.shouldCurrentLineBeStubbed()) {
+ stubbedTriggerLine = currentLine;
}
}
- assertTrue(testsRan);
+ assertNotEquals("", stubbedTriggerLine,"Stub trigger was set");
}
@Test
@@ -1016,4 +1023,188 @@ public void it_stubs_linkage_line() throws IOException {
}
assertTrue(testsRan);
}
+
+ @Test
+ public void it_parses_WS_section_variables_from_split_lines() throws IOException {
+ // debug flag, output to console if true
+ boolean debugOn = true;
+ // Store test data
+ ArrayList cobolLines = new ArrayList<>(Arrays.asList(
+ " DATA DIVISION. ",
+ " FILE SECTION. ",
+ " *-----------------------------------------------------------------",
+ " WORKING-STORAGE SECTION.",
+ " *-----------------------------------------------------------------",
+ " 01 WORK-FIELDS. ",
+ " 03 WS-PROGRAM-NAME PIC X(8) VALUE 'WSVARI'. ",
+ " 03 WS-NUM-9 PIC 9(9). ",
+ " 03 WS-NUM-S9 PIC S9(9) comp ",
+ " value zero. ",
+ " 03 WS-NUM-S9-C3 PIC S9(9) comp-3 ",
+ " value 1. ",
+ " "
+ ));
+// Set up the mock to return each line in sequence, then null
+ Mockito.when(mockedReader.readLine()).thenAnswer(invocation -> {
+ if (!cobolLines.isEmpty()) {
+ String s = cobolLines.get(0);
+ cobolLines.remove(0);
+ return s;
+ }
+ return null;
+ });
+
+ while (interpreterController.interpretNextLine() != null){
+ interpreterController.interpretNextLine();
+ }
+
+ if (debugOn) {
+ // list all the statements read from file section
+ System.out.println("File Section Statements:");
+ for (String statement : interpreterController.getFileSectionStatements()) {
+ System.out.println(statement);
+ }
+ }
+
+ assertTrue(interpreterController.isReading(Constants.WORKING_STORAGE_SECTION),"Right Section being read");
+ assertEquals(3,interpreterController.getNumericFields().getNumberOfFields(),"number of variables read");
+ assertEquals(DataType.DISPLAY_NUMERIC,interpreterController.getNumericFieldDataTypeFor("WS-NUM-9"),"var 'WS-NUM-9' read");
+ assertEquals(DataType.BINARY,interpreterController.getNumericFieldDataTypeFor("WS-NUM-S9"),"var 'WS-NUM-S9' read");
+ assertEquals(DataType.PACKED_DECIMAL,interpreterController.getNumericFieldDataTypeFor("WS-NUM-S9-C3"),"var 'WS-NUM-S9-C3' read");
+
+ }
+ // verify the variables in FILE-SECTION may be split on multiple lines
+ @Test
+ public void it_parses_file_section_variables_from_split_lines() throws IOException {
+ ArrayList cobolLines = new ArrayList<>(Arrays.asList(
+ " DATA DIVISION. ",
+ " FILE SECTION. ",
+ " *------BEFORE-----------------------------------------------------",
+ " FD INPUT-FILE. ",
+ " *------AFTER------------------------------------------------------",
+ " LABEL RECORD IS STANDARD ",
+ " RECORDING MODE F ",
+ " BLOCK CONTAINS 0 RECORDS ",
+ " DATA RECORD IS INPUT-RECORD. ",
+ " 01 INPUT-RECORD. ",
+ " 05 IN-FIELD-1 ",
+ " PIC X(23). ",
+ " 05 IN-FIELD-2 PIC X(3). ",
+ " "
+ ));
+
+ Mockito.when(mockedReader.readLine()).thenAnswer(invocation -> {
+ if (!cobolLines.isEmpty()) {
+ return cobolLines.remove(0);
+ }
+ return null;
+ });
+
+ int i = 1;
+ String currentTestlineOut = "";
+ while ((currentTestlineOut = interpreterController.interpretNextLine())!= null){
+
+ // Validate the returned line. Trimmed to avoid leading and trailing spaces are creating test issues.
+ // counter: i starts at 0 and counts up with each line read by the test.
+ // Some reads are performed inside the interpreterController so the count may not match
+ // the line number in the test data.
+ switch (i) {
+ case 1:
+ assertEquals("DATA DIVISION.", currentTestlineOut.trim(),"Line 1 read");
+ break;
+ case 2:
+ assertEquals("FILE SECTION.", currentTestlineOut.trim(),"Line 2 read");
+ break;
+ case 3:
+ assertEquals("*------BEFORE-----------------------------------------------------", currentTestlineOut.trim(),"Line 3 read");
+ break;
+ case 4:
+ assertEquals("FD INPUT-FILE.", currentTestlineOut.trim(),"Line 4 read");
+ break;
+ case 5:
+ assertEquals("*------AFTER------------------------------------------------------", currentTestlineOut.trim(),"Line 5 read");
+ break;
+ case 6:
+ assertTrue(interpreterController.hasStatementBeenRead(),"we have read a statement");
+ assertEquals("LABEL RECORD IS STANDARD", interpreterController.getCurrentStatement().get(0).trim(),"Line 6 read");
+ assertEquals("RECORDING MODE F", interpreterController.getCurrentStatement().get(1).trim(),"Line 7 read");
+ assertEquals("BLOCK CONTAINS 0 RECORDS", interpreterController.getCurrentStatement().get(2).trim(),"Line 8 read");
+ assertEquals("DATA RECORD IS INPUT-RECORD.", interpreterController.getCurrentStatement().get(3).trim(),"Line 9 read");
+ assertEquals("DATA RECORD IS INPUT-RECORD.", currentTestlineOut.trim(), "interpreter returns last read line after reading a multi-line statement");
+ break;
+ case 7:
+ assertFalse(interpreterController.hasStatementBeenRead(),"we have not a statement");
+ assertEquals("01 INPUT-RECORD.", currentTestlineOut.trim(),"Line 10 read");
+ break;
+ case 8:
+ assertTrue(interpreterController.hasStatementBeenRead(),"we have read a statement");
+ assertEquals("PIC X(23).", currentTestlineOut.trim(),"Line 11 read");
+ assertEquals("05 IN-FIELD-1", interpreterController.getCurrentStatement().get(0).trim(),"Line 12 read");
+ assertEquals("PIC X(23).", interpreterController.getCurrentStatement().get(1).trim(),"Line 13 read");
+ break;
+ case 9:
+ assertFalse(interpreterController.hasStatementBeenRead(),"we have not a statement");
+ assertEquals("05 IN-FIELD-2 PIC X(3).", currentTestlineOut.trim(),"Line 14 read");
+ break;
+ case 10:
+ assertEquals("",currentTestlineOut.trim(),"Line 15 read, only spaces expected, dese");
+ break;
+ default:
+ System.out.println("Line " + i + " read" );
+ // Assert error if we get an unexpected number of lines
+ fail("Test data has been changed, please update the test case. Unexpected line " + i + " read.");
+ break;
+ }
+ i++;
+ }
+ }
+
+ // Test the lines in the DATA DIVISION FILE SECTION are stored in the lineRepository
+ @Test
+ public void it_stores_file_section_lines_in_lineRepository() throws IOException {
+ ArrayList cobolLines = new ArrayList<>(Arrays.asList(
+ " DATA DIVISION. ",
+ " FILE SECTION. ",
+ " *------BEFORE-----------------------------------------------------",
+ " FD INPUT-FILE. ",
+ " *------AFTER------------------------------------------------------",
+ " LABEL RECORD IS STANDARD ",
+ " RECORDING MODE F ",
+ " BLOCK CONTAINS 0 RECORDS ",
+ " DATA RECORD IS INPUT-RECORD. ",
+ " 01 INPUT-RECORD. ",
+ " 05 IN-FIELD-1 ",
+ " PIC X(23). ",
+ " 05 IN-FIELD-2 PIC X(3). ",
+ " "
+ ));
+
+ // Return one line at a time from the list, then null when the list is empty
+ Mockito.when(mockedReader.readLine()).thenAnswer(invocation -> {
+ if (!cobolLines.isEmpty()) {
+ return cobolLines.remove(0);
+ }
+ return null;
+ });
+
+ String currentTestlineOut = "";
+ int i = 0;
+ while ((currentTestlineOut = interpreterController.interpretNextLine())!= null){
+ i++;
+ }
+
+ // Print all the lines stored in the file section for debugging
+ System.out.println("Lines stored in file section:");
+ for (String line : interpreterController.getFileSectionStatements()) {
+ System.out.println(line);
+ }
+
+ List fileSectionLines = interpreterController.getFileSectionStatements();
+ assertEquals(4,fileSectionLines.size(),"number of lines in file section");
+ assertEquals("01 INPUT-RECORD.",fileSectionLines.get(0).trim(),"first line in file section");
+ assertEquals("05 IN-FIELD-1",fileSectionLines.get(1).trim(),"line in file section");
+ assertEquals("PIC X(23).",fileSectionLines.get(2).trim(),"line in file section");
+ assertEquals("05 IN-FIELD-2 PIC X(3).",fileSectionLines.get(3).trim(),"line in file section");
+
+ }
}
diff --git a/src/test/java/org/openmainframeproject/cobolcheck/features/interpreter/CopybookExpanderTest.java b/src/test/java/org/openmainframeproject/cobolcheck/features/interpreter/CopybookExpanderTest.java
new file mode 100644
index 00000000..105ca0c3
--- /dev/null
+++ b/src/test/java/org/openmainframeproject/cobolcheck/features/interpreter/CopybookExpanderTest.java
@@ -0,0 +1,94 @@
+package org.openmainframeproject.cobolcheck.features.interpreter;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openmainframeproject.cobolcheck.exceptions.PossibleInternalLogicErrorException;
+import org.openmainframeproject.cobolcheck.services.Config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class CopybookExpanderTest {
+ // 1 2 3
+ // 012345678901234567890123456789012
+ String execSlq1 = "EXEC SQL INCLUDE TRMCKP END-EXEC.";
+ CopybookExpander ce;
+
+ @BeforeAll
+ static void oneTimeSetup() {
+ Config.load("testconfig.properties");
+ }
+
+
+ @BeforeEach
+ public void setup(){
+ ce = new CopybookExpander();
+ }
+
+ @Test
+ public void test_find_copybook_position_start(){
+ int start = ce.findCopybookNamePositionInCopyStatement(execSlq1);
+ assertEquals(17,start,"start found");
+ }
+
+ @Test
+ public void test_find_copybook_position_end() {
+ int endPos = ce.findCopyBookNameEndPositionInCopyStatement(execSlq1,17);
+ assertEquals(23,endPos,"we found the end");
+ }
+
+ @Test
+ public void test_we_actually_locate_the_copybook_name() {
+ int start = ce.findCopybookNamePositionInCopyStatement(execSlq1);
+ int endPos = ce.findCopyBookNameEndPositionInCopyStatement(execSlq1,start);
+ assertEquals("TRMCKP",execSlq1.substring(start,endPos));
+ }
+
+ @Test
+ public void test_extractCopybookNameFromCopyStatement(){
+ assertEquals("onebookname",ce.extractCopybookNameFromCopyStatement("EXEC SQL INCLUDE onebookname END-EXEC."));
+ }
+
+ @Test
+ public void commentOut_adds_asterisk_at_column_7() {
+ String sourceLine = "123456 89";
+ String result = ce.commentOut(sourceLine);
+ assertEquals("123456*89", result);
+ }
+
+ @Test
+ public void commentOut_handles_short_lines_without_exception() {
+ String sourceLine = "12345";
+ String result = ce.commentOut(sourceLine);
+ assertEquals("12345", result);
+ }
+
+ @Test
+ public void commentOut_handles_empty_string() {
+ String sourceLine = "";
+ String result = ce.commentOut(sourceLine);
+ assertEquals("", result);
+ }
+
+ @Test
+ public void commentOut_handles_null_input() {
+ String sourceLine = null;
+ String result = ce.commentOut(sourceLine);
+ assertEquals(null, result);
+ }
+
+ @Test
+ public void commentOut_handles_input_where_indicator_is_blocked() {
+ String sourceLine = "123456- 'test continuation'.";
+ assertThrows(PossibleInternalLogicErrorException.class, () -> ce.commentOut(sourceLine));
+ }
+
+ @Test
+ public void commentOut_handles_input_where_indicator_is_comment() {
+ String sourceLine = "123456* if more < to-to-come then";
+ String result = ce.commentOut(sourceLine);
+ assertEquals(sourceLine, result);
+ }
+
+}
diff --git a/src/test/java/org/openmainframeproject/cobolcheck/features/interpreter/LineRepositoryTest.java b/src/test/java/org/openmainframeproject/cobolcheck/features/interpreter/LineRepositoryTest.java
new file mode 100644
index 00000000..45ea8403
--- /dev/null
+++ b/src/test/java/org/openmainframeproject/cobolcheck/features/interpreter/LineRepositoryTest.java
@@ -0,0 +1,18 @@
+package org.openmainframeproject.cobolcheck.features.interpreter;
+
+import org.junit.jupiter.api.Test;
+
+import java.awt.*;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+public class LineRepositoryTest {
+ // 1 2 3
+ // 012345678901234567890123456789012
+ // List includedLines = {"EXEC SQL INCLUDE TRMCKP END-EXEC.",""}
+
+
+
+
+}
diff --git a/src/test/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/ReplaceTest.java b/src/test/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/ReplaceTest.java
index c1968ca5..3a8ea2a2 100644
--- a/src/test/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/ReplaceTest.java
+++ b/src/test/java/org/openmainframeproject/cobolcheck/services/cobolLogic/replace/ReplaceTest.java
@@ -1,14 +1,23 @@
package org.openmainframeproject.cobolcheck.services.cobolLogic.replace;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.openmainframeproject.cobolcheck.services.Config;
import java.io.File;
+import java.nio.file.FileSystems;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ReplaceTest {
+ @BeforeAll
+ public static void setup(){
+ Config.load();
+ }
+
+
@Test
public void test_General_setup() {
// test we can inspect multiple times and still have the correct state
@@ -82,6 +91,56 @@ public void test_for_linenumber_0_every_replace_is_performed() {
assertEquals(" MOVE 'Y' TO WS-EXPECTED",Replace.replace(" MOVE 'Y' TO WS-ACTUAL", 0));
}
+ @Test
+ public void test_what_happends_when_we_have_a_big_replace() {
+ Replace.inspectProgram(new File("./testfiles/REPLACE3.CBL"));
+ assertEquals(" MOVE 'B' TO REPDEMO3-PARAM",Replace.replace(" MOVE 'B' TO :PROGRAM:-PARAM", 0));
+ assertEquals("123456 MOVE MY-DATA in ADVANCED-REQUEST-DATA TO UPDATE-MY-ADVANCED-REQUEST-DATA",
+ Replace.replace("123456 MOVE MY-DATA in ADVANCED-REQUEST-DATA TO :REQUEST:-DATA", 0));
+
+ }
+
+ @Test
+ public void test_output_file_name_is_set_to_path_for_test_elements() {
+ //Config.getGeneratedTestCodePath() is where the modified files are written to
+ //regardless of where the input file is located
+
+ // this test must work on all OS - Windows, Linux, Mac therefore we get the OS path Separator
+ String osPathSeparator = FileSystems.getDefault().getSeparator();
+
+ String inputFileName = "." + osPathSeparator + "testfiles" + osPathSeparator + "REPLACE.CBL";
+ String expectedOutputFileName = Config.getGeneratedTestCodePath() + osPathSeparator + "REPLACE.replaced.CBL";
+ assertEquals(expectedOutputFileName, Replace.getOutputFileName(inputFileName));
+
+ inputFileName = "." + osPathSeparator + "test" + osPathSeparator + "files" + osPathSeparator + "REPLACE.CBL";
+ expectedOutputFileName = Config.getGeneratedTestCodePath() + osPathSeparator + "REPLACE.replaced.CBL";
+ assertEquals(expectedOutputFileName, Replace.getOutputFileName(inputFileName));
+
+ inputFileName = "." + osPathSeparator + "test" + osPathSeparator + "files" + osPathSeparator + "REPLACE.xxx";
+ expectedOutputFileName = Config.getGeneratedTestCodePath() + osPathSeparator + "REPLACE.replaced.xxx";
+ assertEquals(expectedOutputFileName, Replace.getOutputFileName(inputFileName));
+ }
+
+ // test internal functions for filename manipulation
+ @Test
+ public void test_internal_functions_for_filename_manipulation() {
+ String inputName = "REPLACE.CBL";
+ assertEquals("REPLACE.CBL", Replace.getFilenameWithoutPath(inputName));
+ assertEquals("CBL", Replace.getFileExtension(inputName));
+ assertEquals("REPLACE", Replace.getFileNameWithoutExtension(inputName));
+
+ // Default extension if none found
+ inputName = "REPLACE";
+ assertEquals("CBL", Replace.getFileExtension(inputName));
+ }
+
+ @Test
+ public void test_internal_functions_for_filename_manipulation_path() {
+ String input = "./test/files/REPLACE.CBL";
+ assertEquals("REPLACE.CBL", Replace.getFilenameWithoutPath(input));
+ }
+
+
diff --git a/testfiles/REPLACE3.CBL b/testfiles/REPLACE3.CBL
new file mode 100644
index 00000000..31ac2fdf
--- /dev/null
+++ b/testfiles/REPLACE3.CBL
@@ -0,0 +1,49 @@
+ **********************************************************************
+ * AUTHOR: T. N. KRAMER
+ * DATE: 25 APR 2025
+ * PURPOSE: DEMONSTRATE REPLACE STATEMENT
+ **********************************************************************
+ IDENTIFICATION DIVISION.
+ PROGRAM-ID. REPDEMO3.
+ ENVIRONMENT DIVISION.
+ DATA DIVISION.
+ WORKING-STORAGE SECTION.
+ 77 WS-OMEGA PIC X.
+ 77 WS-GAMMA PIC X.
+ 77 UT-OMEGA PIC X.
+ 77 UT-GAMMA PIC X.
+
+
+ REPLACE ==:PROGRAM:== BY ==REPDEMO3==
+ ==:REQUEST:== BY ==UPDATE-MY-ADVANCED-REQUEST==
+ ==:RESPONSE:== BY ==UPDATE-MY-ADVANCED-RESPONSE==.
+
+ PROCEDURE DIVISION.
+
+ SET UT-COMPARE-DEFAULT TO TRUE
+ PERFORM UT-ASSERT-EQUAL
+ ADD 1 TO UT-TEST-CASE-COUNT
+ SET UT-NORMAL-COMPARE TO TRUE
+ MOVE WS-OMEGA TO UT-ACTUAL
+ MOVE 'Y' TO UT-EXPECTED
+ .
+ 3000-DYNAMIC-CALL.
+ MOVE 'A' TO WS-ALPHA
+ MOVE 'Z' TO WS-OMEGA
+ * CALL WS-SUBPROGRAM-NAME
+ * USING WS-ALPHA WS-OMEGA
+ MOVE "B" TO WS-ALPHA
+ MOVE "Y" TO WS-OMEGA
+ .
+
+ 3001-DYNAMIC-CALL
+ MOVE 'A' TO WS-ALPHA
+ MOVE 'Z' TO WS-OMEGA
+ * CALL WS-SUBPROGRAM-NAME
+ * USING WS-ALPHA WS-OMEGA
+ MOVE "B" TO WS-ALPHA
+ MOVE "Y" TO WS-OMEGA
+ .
+
+ 9999-END.
+ .
\ No newline at end of file
diff --git a/vs-code-extension/snippets/cut-snippets.json b/vs-code-extension/snippets/cut-snippets.json
index db651fd3..9d27ca5c 100644
--- a/vs-code-extension/snippets/cut-snippets.json
+++ b/vs-code-extension/snippets/cut-snippets.json
@@ -1,7 +1,7 @@
{
"AFTER EACH Block": {
"prefix": "AFTER EACH",
- "description": "Instantiates an AFTER EACH block and places the curso in its body",
+ "description": "Instantiates an AFTER EACH block and places the cursor in its body",
"body": [
"AFTER EACH",
" ${0}",