diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6d60a3c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: ci + +on: + pull_request: + push: + branches: + - master + +defaults: + run: + shell: bash + +jobs: + +# check consistency between Groovy and C++ APIs + groovy-vs-cpp: + runs-on: [ ubuntu-latest ] + strategy: + fail-fast: false + matrix: + dataset: [ rga_inbending, rga_outbending, rga_spring19, rgb_fall, rgb_spring, rgb_winter, rgk_6, rgk_7 ] + steps: + - name: checkout + uses: actions/checkout@v2 + with: + submodules: recursive + - name: setup-groovy + run: | + sudo apt install groovy + - name: env + run: | + source env.sh + echo "QADB=${QADB}" >> $GITHUB_ENV + echo "JYPATH=${JYPATH}" >> $GITHUB_ENV + - name: compile-cpp-tests + run: | + cd srcC/tests + make + - name: test-diff-groovy-cpp + run: | + tests/test_diffGroovyCpp.loop.sh ${{matrix.dataset}} diff --git a/README.md b/README.md index f5fdcb4..0870710 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,7 @@ Documentation for QADB maintenance and revision version; repeat the same procedure for `bin/printGoldenFiles.sh > text/listOfGoldFiles.txt` * update customized QA criteria sets, such as `OkForAsymmetry` + * add any new dataset to the CI tests: `.github/workflows/ci.yml` * use `git status` and `git diff` to review changes, then add and commit to git, and push to the remote branch diff --git a/env.sh b/env.sh index 4291867..804e11b 100644 --- a/env.sh +++ b/env.sh @@ -1,6 +1,10 @@ #!/bin/bash -export QADB=$(dirname $(realpath $0)) +if [ -z "${BASH_SOURCE[0]}" ]; then + export QADB=$(dirname $(realpath $0)) +else + export QADB=$(dirname $(realpath ${BASH_SOURCE[0]})) +fi JYPATH="${JYPATH}:${QADB}/src/" export JYPATH=$(echo $JYPATH | sed 's/^://') diff --git a/src/examples/README.md b/src/examples/README.md index 3a8ac3c..e21498f 100644 --- a/src/examples/README.md +++ b/src/examples/README.md @@ -12,5 +12,3 @@ also the C++ implementation. * use `${COATJAVA}/bin/run-groovy` to execute these examples * it is useful to have `${COATJAVA}/bin` in your `$PATH` * most of the example scripts require a HIPO file as an argument -* example scripts named `test*.groovy` are actually developer tools for - testing the QADB, but may also be useful as additional examples diff --git a/src/examples/test1.groovy b/src/examples/test1.groovy deleted file mode 100644 index 4226daa..0000000 --- a/src/examples/test1.groovy +++ /dev/null @@ -1,125 +0,0 @@ -// dump QADB data for a specified file; this is for QADB validation and -// testing, but may still be useful as an example; see dumpQADB.cpp for a more -// user-friendly example -// - this program does requires a HIPO file - -// imports -import clasqa.QADB // access QADB -import org.jlab.io.hipo.HipoDataSource // to read HIPO files - -int maxbit = 5; // max defect bit number - -// print separator -def sep = { s,n -> n.times{print s}; println ""; } - -// print error -def err = { s -> System.err << "ERROR: $s\n" } - - -// instantiate HIPO file reader for specified HIPO file -def inHipoFile -if(args.length>=1) inHipoFile = args[0] -else { System.err << "ERROR: specify hipo file\n"; return; } -def reader = new HipoDataSource() -reader.open(inHipoFile) - - -// instantiate QADB -QADB qa = new QADB() -// alternatively, specify run range to restrict QADB (may be more efficient) -//QADB qa = new QADB(5000,5500); - - -// event loop -int evnum,runnum,filenum -int filenumTmp = -1000 -def defname -int chargeInt -while(reader.hasEvent()) { - - event = reader.getNextEvent() - - // get run and event numbers - runnum = event.getBank("RUN::config").getInt('run',0) - evnum = event.getBank("RUN::config").getInt('event',0) - - // query - qa.query(runnum,evnum) - - // skip tag1 events - if(runnum==0) continue - - // apply QA cut - //if(!(qa.OkForAsymmetry(runnum,evnum))) continue - - // get filenum; only print info if we queried a different file - filenum = qa.getFilenum() - if(filenum==filenumTmp) continue - filenumTmp = filenum - - sep("=",50) - //err("test error print") - - // check run and file number accessors: make sure that they - // are equal to what we asked for - println "- run,evnum,evnumRange,filenum" - println qa.getRunnum() + " " + runnum - if(qa.getRunnum() != runnum) err("QADB::getRunnum != runnum"); - - // check event number: report an error if evnum min>=max - println evnum - println qa.getEvnumMin() + " " + qa.getEvnumMax() - if(qa.getEvnumMin() >= qa.getEvnumMax()) - err("GetEvnumMin() >= GetEvnumMax()"); - println filenum - - // print charge (convert to pC and truncate, for easier comparison) - println "- charge,comment" - println ((int)(1000*qa.getCharge())) - qa.accumulateCharge(); - - // print comment - println "\"" + qa.getComment() + "\"" - - - // print overall defect info - println "- defect" - println qa.getDefect() - for(int sec=1; sec<=6; sec++) - print " " + qa.getDefect(sec); println "" - - // print defect bit info - for(int bit=0; bit<=maxbit; bit++) { - // translate bit number to name; check if QADB::Bit returns correct bit - switch(bit) { - case 0: defname="TotalOutlier"; break; - case 1: defname="TerminalOutlier"; break; - case 2: defname="MarginalOutlier"; break; - case 3: defname="SectorLoss"; break; - case 4: defname="LowLiveTime"; break; - case 5: defname="Misc"; break; - }; - if(qa.Bit(defname) != bit) err("QADB::Bit problem"); - println qa.Bit(defname) + " $bit $defname" - // print defect info - println (qa.hasDefect(defname)?1:0) - for(int sec=1; sec<=6; sec++) - print " " + (qa.hasDefect(defname,sec)?1:0); println "" - // print bit masking - qa.setMaskBit(defname); - println qa.getMask() + " " + (qa.pass(runnum,evnum)?1:0) - qa.setMaskBit(defname,false); - }; - - // print QA cuts (see above for custom cut check with mask) - println "- cuts" - println (qa.golden(runnum,evnum)?1:0) - println (qa.OkForAsymmetry(runnum,evnum)?1:0) -} -sep("=",50); - -// print accumulated charge -println "- charge" -println ((int)(1000*qa.getAccumulatedCharge())) - -sep("=",50); diff --git a/src/examples/testCharge.groovy b/src/tests/testCharge.groovy similarity index 100% rename from src/examples/testCharge.groovy rename to src/tests/testCharge.groovy diff --git a/src/examples/test2.groovy b/src/tests/testDumpQADB.groovy similarity index 100% rename from src/examples/test2.groovy rename to src/tests/testDumpQADB.groovy diff --git a/srcC/examples/README.md b/srcC/examples/README.md index c2a5d9e..f10ba66 100644 --- a/srcC/examples/README.md +++ b/srcC/examples/README.md @@ -12,5 +12,3 @@ groovy implementation. * compile examples with `make` * executables `*.exe` will appear; most of them require a HIPO file as an argument -* example programs named `test*.cpp` are actually developer tools for - testing the QADB, but may also be useful as additional examples diff --git a/srcC/examples/test1.cpp b/srcC/examples/test1.cpp deleted file mode 100644 index 5da43c0..0000000 --- a/srcC/examples/test1.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// dump QADB data for a specified file; this is for QADB validation and testing, -// but may still be useful as an example; see dumpQADB.cpp for a more -// user-friendly example -// - this program does requires a HIPO file and clas12root - -#include -#include - -// QADB header and namespace -#include "QADB.h" -using namespace QA; - -// clas12root headers -#include "reader.h" -#include "clas12reader.h" - -const int maxbit = 5; // max defect bit number - -using namespace std; -using namespace clas12; // for clas12root - -// print separator -void sep(string s,int n) {for(int k=0;knext()==true) { - - // get run number and event number - runnum = c12->runconfig()->getRun(); - evnum = c12->runconfig()->getEvent(); - - // query - qa->Query(runnum,evnum); - - // skip tag1 events - if(runnum==0) continue; - - // apply QA cut - //if(!(qa->OkForAsymmetry(runnum,evnum))) continue; - - // get filenum; only print info if we queried a different file - filenum = qa->GetFilenum(); - if(filenum==filenumTmp) continue; - filenumTmp = filenum; - - sep("=",50); - //err("test error print"); - - // check run and file number accessors: make sure that they - // are equal to what we asked for - cout << "- run,evnum,evnumRange,filenum" << endl; - cout << qa->GetRunnum() << " " << runnum << endl; - if(qa->GetRunnum() != runnum) err("QADB::GetRunnum != runnum"); - - // check event number: report an error if evnum min>=max - cout << evnum << endl; - cout << qa->GetEvnumMin() << " " << qa->GetEvnumMax() << endl; - if(qa->GetEvnumMin() >= qa->GetEvnumMax()) - err("GetEvnumMin() >= GetEvnumMax()"); - cout << filenum << endl; - - // print charge (convert to pC and truncate, for easier comparison) - chargeInt = (int) (1000*qa->GetCharge()); - cout << "- charge,comment" << endl; - cout << chargeInt << endl; - qa->AccumulateCharge(); - - // print comment - cout << "\"" << qa->GetComment() << "\"" << endl; - - - // print overall defect info - cout << "- defect" << endl; - cout << qa->GetDefect() << endl; - for(int sec=1; sec<=6; sec++) - cout << " " << qa->GetDefect(sec); cout << endl; - - // print defect bit info - for(int bit=0; bit<=maxbit; bit++) { - // translate bit number to name; check if QADB::Bit returns correct bit - switch(bit) { - case 0: defname="TotalOutlier"; break; - case 1: defname="TerminalOutlier"; break; - case 2: defname="MarginalOutlier"; break; - case 3: defname="SectorLoss"; break; - case 4: defname="LowLiveTime"; break; - case 5: defname="Misc"; break; - }; - if(qa->Bit(defname.c_str()) != bit) err("QADB::Bit problem"); - cout << qa->Bit(defname.c_str()) << " " << bit << " " << defname << endl; - // print defect info - cout << qa->HasDefect(defname.c_str()) << endl; - for(int sec=1; sec<=6; sec++) - cout << " " << qa->HasDefect(defname.c_str(),sec); cout << endl; - // print bit masking - qa->SetMaskBit(defname.c_str()); - cout << qa->GetMask() << " " << qa->Pass(runnum,evnum) << endl; - qa->SetMaskBit(defname.c_str(),false); - }; - - // print QA cuts (see above for custom cut check with mask) - cout << "- cuts" << endl; - cout << qa->Golden(runnum,evnum) << endl; - cout << qa->OkForAsymmetry(runnum,evnum) << endl; - }; - - sep("=",50); - - // print accumulated charge - cout << "- charge" << endl; - cout << ((int)(1000*qa->GetAccumulatedCharge())) << endl; - - sep("=",50); - - return 0; -} - diff --git a/srcC/tests/Makefile b/srcC/tests/Makefile new file mode 100644 index 0000000..2a452c4 --- /dev/null +++ b/srcC/tests/Makefile @@ -0,0 +1,29 @@ +CXX = g++ -std=c++11 +FLAGS = -g -Wno-deprecated -fPIC -m64 -fno-inline -Wno-write-strings + +# QADB and rapidjson +DEPS += -I$(QADB)/srcC/rapidjson/include -I$(QADB)/srcC/include + +#-------------------------------------------- + +SOURCES := $(basename $(wildcard *.cpp)) +EXES := $(addsuffix .exe, $(SOURCES)) + +#-------------------------------------------- + +all: + make exe + +exe: $(EXES) + +%.exe: %.o + @echo "--- make executable $@" + $(CXX) -o $@ $< $(LIBS) + +%.o: %.cpp + @echo "----- build $@ -----" + $(CXX) -c $^ -o $@ $(FLAGS) $(DEPS) + +clean: + @echo "----- clean -----" + $(RM) $(EXES) diff --git a/srcC/examples/testCharge.cpp b/srcC/tests/testCharge.cpp similarity index 100% rename from srcC/examples/testCharge.cpp rename to srcC/tests/testCharge.cpp diff --git a/srcC/examples/test2.cpp b/srcC/tests/testDumpQADB.cpp similarity index 100% rename from srcC/examples/test2.cpp rename to srcC/tests/testDumpQADB.cpp diff --git a/tests/test_diffGroovyCpp.loop.sh b/tests/test_diffGroovyCpp.loop.sh new file mode 100755 index 0000000..7ac2562 --- /dev/null +++ b/tests/test_diffGroovyCpp.loop.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# loop over runs in specified dataset, running test_diffGroovyCpp.sh on each run + +set -e + +if [ -z "$QADB" ]; then + echo "ERROR: you must source env.sh first"; exit 1 +fi + +if [ $# -lt 1 ]; then + echo "USAGE: $0 [dataset]"; exit 2 +fi + +dataset=$1 + +mkdir -p ${QADB}/tmp + +echo """ + +begin TEST: +- test DumpQADB for dataset ${dataset} +- check for differences between C++ and Groovy APIs +- a diff will be dumped for any differences found, and the script will stop + +""" + +grep -E '^RUN: ' ${QADB}/qadb/qa.${dataset}/qaTree.json.table |\ + awk '{print $2}' |\ + while read run; do + ${QADB}/tests/test_diffGroovyCpp.sh DumpQADB $run + done diff --git a/tests/test_diffGroovyCpp.sh b/tests/test_diffGroovyCpp.sh new file mode 100755 index 0000000..cfe086f --- /dev/null +++ b/tests/test_diffGroovyCpp.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# run test program to cross check groovy and c++ readers + +set -e + +if [ -z "$QADB" ]; then + echo "ERROR: you must source env.sh first" + exit 1 +fi + +if [ $# -lt 2 ]; then + echo """ +USAGE: $0 [test name] [run number] + +- [test name] can be any of: +$(ls src/tests | sed 's/^test//' | sed 's/\.groovy$//') + +- [run number] is a run number (only used for some tests) + """ + exit 2 +fi + +testname=$1 +run=$2 + +mkdir -p ${QADB}/tmp + +# groovy test +echo "EXECUTE GROOVY TEST $testname RUN $run" +pushd ${QADB}/src/tests +groovy -cp "$JYPATH" test${testname}.groovy $run > ${QADB}/tmp/groovy.${run}.out +popd + +# c++ test +echo "EXECUTE C++ TEST $testname RUN $run" +pushd ${QADB}/srcC/tests +./test${testname}.exe $run > ${QADB}/tmp/cpp.${run}.out +popd + +# compare +echo "DIFFERENCE:" +diff ${QADB}/tmp/{cpp,groovy}.${run}.out +exit $? diff --git a/util/test_LOOPdiffGroovyCpp_2.sh b/util/test_LOOPdiffGroovyCpp_2.sh deleted file mode 100755 index de47bba..0000000 --- a/util/test_LOOPdiffGroovyCpp_2.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# loop over runs in specified dataset - -if [ -z "$QADB" ]; then - echo "ERROR: you must source env.sh first"; exit -fi - -if [ $# -lt 1 ]; then - echo "USAGE: $0 [dataset]"; exit -fi - -dataset=$1 - -${QADB}/bin/makeTables.sh - -grep -E '^RUN: ' ${QADB}/qadb/qa.${dataset}/qaTree.json.table | \ -awk '{print $2}' > ${QADB}/tmp/runlist.${dataset} - -while read run; do - ${QADB}/util/test_diffGroovyCpp.sh 2 $run 1 -done < ${QADB}/tmp/runlist.${dataset} diff --git a/util/test_diffGroovyCpp.sh b/util/test_diffGroovyCpp.sh deleted file mode 100755 index a9d7e68..0000000 --- a/util/test_diffGroovyCpp.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# run test program to cross check groovy and c++ readers - -if [ -z "$QADB" ]; then - echo "ERROR: you must source env.sh first"; exit -fi - -if [ $# -lt 2 ]; then - echo "USAGE: $0 [test num] [argument]" - echo "- test 1 [hipo file]" - echo "- test 2 [run number]" - echo "- provide a 3rd argument to suppress vimdiff (batch test)" - exit -fi - -testnum=$1 -arg=$2 -echo "ARGUMENT = $arg" - -mkdir -p ${QADB}/tmp - -suffix=$arg -if [ $testnum -eq 1 ]; then - suffix=$(echo $arg | sed 's/^.*\///g') -fi - -# groovy test -echo "EXECUTE GROOVY TEST $testnum" -pushd ${QADB}/src/examples -run-groovy test${testnum}.groovy $arg > ${QADB}/tmp/groovy.${suffix}.out -popd - -# c++ test -echo "EXECUTE C++ TEST $testnum" -pushd ${QADB}/srcC/examples -test${testnum}.exe $arg > ${QADB}/tmp/cpp.${suffix}.out -popd - -# vimdiff -if [ $# -lt 3 ]; then - vimdiff ${QADB}/tmp/{cpp,groovy}.${suffix}.out -else - diff ${QADB}/tmp/{cpp,groovy}.${suffix}.out > ${QADB}/tmp/diff.${suffix}.out - echo produced ${QADB}/tmp/diff.${suffix}.out -fi