From cfefa1b43e2f94360b6d4b2a46e963ef842c7117 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Fri, 1 Jun 2018 17:01:32 -0400 Subject: [PATCH 01/10] revert back to develop --- CMakeLists.txt | 4 ++-- docker/Dockerfile | 2 +- docker/build_test.sh | 2 +- include/SalmonConfig.hpp | 4 ++-- scripts/fetchRapMap.sh | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 592ee0a08..32eebe29d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,10 @@ enable_testing() project (Salmon) -set(CPACK_PACKAGE_VERSION "0.10.1") +set(CPACK_PACKAGE_VERSION "0.10.2") set(CPACK_PACKAGE_VERSION_MAJOR "0") set(CPACK_PACKAGE_VERSION_MINOR "10") -set(CPACK_PACKAGE_VERSION_PATCH "1") +set(CPACK_PACKAGE_VERSION_PATCH "2") set(PROJECT_VERSION ${CPACK_PACKAGE_VERSION}) set(CPACK_GENERATOR "TGZ") set(CPACK_SOURCE_GENERATOR "TGZ") diff --git a/docker/Dockerfile b/docker/Dockerfile index 569b3a894..095f50cb4 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -6,7 +6,7 @@ MAINTAINER salmon.maintainer@gmail.com ENV PACKAGES git gcc make g++ cmake libboost-all-dev liblzma-dev libbz2-dev \ ca-certificates zlib1g-dev curl unzip autoconf -ENV SALMON_VERSION 0.10.1 +ENV SALMON_VERSION 0.10.2 # salmon binary will be installed in /home/salmon/bin/salmon diff --git a/docker/build_test.sh b/docker/build_test.sh index 5a39523ac..37cf2207d 100644 --- a/docker/build_test.sh +++ b/docker/build_test.sh @@ -1,2 +1,2 @@ #! /bin/bash -docker build -t combinelab/salmon:0.10.1 . +docker build -t combinelab/salmon:0.10.2 . diff --git a/include/SalmonConfig.hpp b/include/SalmonConfig.hpp index 2ccb4ece6..3906bd86b 100644 --- a/include/SalmonConfig.hpp +++ b/include/SalmonConfig.hpp @@ -27,8 +27,8 @@ namespace salmon { constexpr char majorVersion[] = "0"; constexpr char minorVersion[] = "10"; -constexpr char patchVersion[] = "1"; -constexpr char version[] = "0.10.1"; +constexpr char patchVersion[] = "2"; +constexpr char version[] = "0.10.2"; constexpr uint32_t indexVersion = 2; constexpr char requiredQuasiIndexVersion[] = "q5"; } // namespace salmon diff --git a/scripts/fetchRapMap.sh b/scripts/fetchRapMap.sh index 4772d1360..606ea5c63 100755 --- a/scripts/fetchRapMap.sh +++ b/scripts/fetchRapMap.sh @@ -22,8 +22,8 @@ if [ -d ${INSTALL_DIR}/src/rapmap ] ; then rm -fr ${INSTALL_DIR}/src/rapmap fi -SVER=salmon-v0.10.1 -#SVER=develop-salmon +#SVER=salmon-v0.10.1 +SVER=develop-salmon mkdir -p ${EXTERNAL_DIR} curl -k -L https://github.com/COMBINE-lab/RapMap/archive/${SVER}.zip -o ${EXTERNAL_DIR}/rapmap.zip @@ -40,8 +40,8 @@ fi if [ -z "${hashcheck-}" ]; then echo "Couldn't find shasum command; can't verify contents of downloaded RapMap"; else - echo "fca1323154b884a81bd61e3bdda06502fd3593f1622ad3b2dc98ef14dc4e6b6a ${EXTERNAL_DIR}/rapmap.zip" | ${hashcheck} -c - || { echo "rapmap.zip did not match expected SHA1! Exiting."; exit 1; } - #echo "not testing sha in develop branch" + #echo "fca1323154b884a81bd61e3bdda06502fd3593f1622ad3b2dc98ef14dc4e6b6a ${EXTERNAL_DIR}/rapmap.zip" | ${hashcheck} -c - || { echo "rapmap.zip did not match expected SHA1! Exiting."; exit 1; } + echo "not testing sha in develop branch" fi From a5a85f6f44bbac45053b9ea77f11f54b9fd559fa Mon Sep 17 00:00:00 2001 From: Peter Hickey Date: Sat, 2 Jun 2018 13:35:02 -0400 Subject: [PATCH 02/10] Add whitespace to args descriptions --- src/ProgramOptionsGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProgramOptionsGenerator.cpp b/src/ProgramOptionsGenerator.cpp index 8cf0d08da..9ac63c026 100644 --- a/src/ProgramOptionsGenerator.cpp +++ b/src/ProgramOptionsGenerator.cpp @@ -293,7 +293,7 @@ namespace salmon { ( "dumpfq", po::bool_switch()->default_value(alevin::defaults::dumpFQ), "Dump barcode modified fastq file for downstream analysis by" - "using coin toss for multi-mapping.") + " using coin toss for multi-mapping.") ( "dumpBfh", po::bool_switch()->default_value(alevin::defaults::dumpBFH), "dump the big hash with all the barcodes and the UMI sequence.") From 71db01988cb47381d0394406c7c759d86d2102a3 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Tue, 5 Jun 2018 17:13:10 -0400 Subject: [PATCH 03/10] Fix for #231 when alignment ends in exhaustive soft clip --- include/AtomicMatrix.hpp | 3 +++ include/SalmonStringUtils.hpp | 3 ++- include/Transcript.hpp | 41 +++++++++++++++--------------- scripts/fetchRapMap.sh | 1 + src/AlignmentModel.cpp | 42 +++++++++--------------------- src/FASTAParser.cpp | 10 +++++++- src/SalmonAlevin.cpp | 30 +++++++++++++++------- src/SalmonQuantify.cpp | 48 +++++++++++++++++++++++++++-------- src/SalmonStringUtils.cpp | 6 +++-- 9 files changed, 110 insertions(+), 74 deletions(-) diff --git a/include/AtomicMatrix.hpp b/include/AtomicMatrix.hpp index ed038ba03..7739f7d08 100644 --- a/include/AtomicMatrix.hpp +++ b/include/AtomicMatrix.hpp @@ -102,6 +102,9 @@ template class AtomicMatrix { } } + size_t nRow() const { return nRow_; } + size_t nCol() const { return nCol_; } + private: std::vector> storage_; std::vector> rowsums_; diff --git a/include/SalmonStringUtils.hpp b/include/SalmonStringUtils.hpp index 3479ff114..b679057aa 100644 --- a/include/SalmonStringUtils.hpp +++ b/include/SalmonStringUtils.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace salmon { @@ -128,7 +129,7 @@ constexpr uint8_t charToSamEncode[] = { 15, 15, 15, 15, 15, 15}; */ -uint8_t* encodeSequenceInSAM(const char* src, size_t len); +std::vector encodeSequenceInSAM(const char* src, size_t len); /** Incomplete: currently only rev for 'ATCG' diff --git a/include/Transcript.hpp b/include/Transcript.hpp index 7e9be1bbb..18dd243c5 100644 --- a/include/Transcript.hpp +++ b/include/Transcript.hpp @@ -9,6 +9,7 @@ #include "SequenceBiasModel.hpp" #include "tbb/atomic.h" #include "stx/string_view.hpp" +#include "IOUtils.hpp" #include #include #include @@ -145,7 +146,11 @@ class Transcript { using salmon::stringtools::encodedRevComp; size_t byte = idx >> 1; size_t nibble = idx & 0x1; - uint8_t* sseq = SAMSequence_.get(); + + // NOTE 10.2 + auto& sseq = SAMSequence_; + //if (byte >= sseq.size()) { std::cerr << "requested index " << byte << " for vector of size " << sseq.size() << " for reference " << RefName << std::endl; return 0;} + switch (dir) { case strand::forward: @@ -473,25 +478,19 @@ class Transcript { } } - // Will *not* delete seq on destruction - void setSAMSequenceBorrowed(uint8_t* seq, bool needGC = false, - bool reduceGCMemory = false) { - SAMSequence_ = std::unique_ptr( - seq, // store seq - [](uint8_t* p) {} // do nothing deleter - ); - if (needGC) { - computeGCContent_(reduceGCMemory); - } - } - // Will delete seq on destruction - void setSAMSequenceOwned(uint8_t* seq, bool needGC = false, + void setSAMSequenceOwned(std::vector&& seq, bool needGC = false, bool reduceGCMemory = false) { - SAMSequence_ = std::unique_ptr( - seq, // store seq - [](uint8_t* p) { delete[] p; } // do nothing deleter - ); + + if ((2*seq.size() < RefLength) or (2*seq.size() > RefLength + 1)) { + std::stringstream errstream; + errstream << "\n\nSAM file says target " << RefName << " has length " << RefLength + << ", but the FASTA file contains a sequence of length [" << seq.size() * 2 << " or " << seq.size() * 2 - 1 << "]\n\n"; + std::cerr << ioutils::SET_RED << errstream.str(); + std::exit(1); + } + + SAMSequence_ = std::move(seq); if (needGC) { computeGCContent_(reduceGCMemory); } @@ -499,7 +498,7 @@ class Transcript { const char* Sequence() const { return Sequence_.get(); } - uint8_t* SAMSequence() const { return SAMSequence_.get(); } + uint8_t* SAMSequence() const { return const_cast(SAMSequence_.data()); } void setCompleteLength(uint32_t completeLengthIn) { CompleteLength = completeLengthIn; @@ -679,8 +678,8 @@ class Transcript { */ } - std::unique_ptr SAMSequence_ = - std::unique_ptr(nullptr, [](uint8_t*) {}); + // NOTE 10.2 + std::vector SAMSequence_; std::unique_ptr Sequence_ = std::unique_ptr(nullptr, diff --git a/scripts/fetchRapMap.sh b/scripts/fetchRapMap.sh index 606ea5c63..2e5bae01c 100755 --- a/scripts/fetchRapMap.sh +++ b/scripts/fetchRapMap.sh @@ -24,6 +24,7 @@ fi #SVER=salmon-v0.10.1 SVER=develop-salmon +#SVER=pe-chaining mkdir -p ${EXTERNAL_DIR} curl -k -L https://github.com/COMBINE-lab/RapMap/archive/${SVER}.zip -o ${EXTERNAL_DIR}/rapmap.zip diff --git a/src/AlignmentModel.cpp b/src/AlignmentModel.cpp index d99a8f4e1..5ffc363dc 100644 --- a/src/AlignmentModel.cpp +++ b/src/AlignmentModel.cpp @@ -133,7 +133,7 @@ inline void AlignmentModel::setBasesFromCIGAROp_(enum cigar_op op, size_t& curReadBase) { switch (op) { case BAM_UNKNOWN: - std::cerr << "ENCOUNTERED UNKNOWN SYMBOL IN CIGAR STRING!\n"; + std::cerr << "ENCOUNTERED UNKNOWN SYMBOL IN CIGAR STRING!" << std::endl; break; case BAM_CMATCH: // do nothing @@ -164,6 +164,9 @@ inline void AlignmentModel::setBasesFromCIGAROp_(enum cigar_op op, curRefBase = ALN_PAD; curReadBase = ALN_PAD; break; + default: + std::cerr << "ENCOUNTERED UNKNOWN (non -1) CIGAR OP : (" << op << ")!" << std::endl; + break; } } @@ -258,8 +261,8 @@ double AlignmentModel::logLikelihood( uint32_t opLen = cigar[cigarIdx] >> BAM_CIGAR_SHIFT; enum cigar_op op = static_cast(cigar[cigarIdx] & BAM_CIGAR_MASK); - size_t curReadBase = samToTwoBit[bam_seqi(qseq, readIdx)]; - size_t curRefBase = samToTwoBit[ref.baseAt(uTranscriptIdx, readStrand)]; + size_t curReadBase = (BAM_CONSUME_SEQ(op)) ? samToTwoBit[bam_seqi(qseq, readIdx)] : 0; + size_t curRefBase = (BAM_CONSUME_REF(op)) ? samToTwoBit[ref.baseAt(uTranscriptIdx, readStrand)] : 0; advanceInRead = false; advanceInReference = false; @@ -418,6 +421,7 @@ void AlignmentModel::update( int32_t readIdx{0}; auto transcriptIdx = bam_pos(read); size_t transcriptLen = ref.RefLength; + // if the read starts before the beginning of the transcript, // only consider the part overlapping the transcript if (transcriptIdx < 0) { @@ -450,9 +454,9 @@ void AlignmentModel::update( uint32_t opLen = cigar[cigarIdx] >> BAM_CIGAR_SHIFT; enum cigar_op op = static_cast(cigar[cigarIdx] & BAM_CIGAR_MASK); - size_t curReadBase = samToTwoBit[bam_seqi(qseq, readIdx)]; - size_t curRefBase = samToTwoBit[ref.baseAt(uTranscriptIdx, readStrand)]; - advanceInRead = false; + + size_t curReadBase = (BAM_CONSUME_SEQ(op)) ? samToTwoBit[bam_seqi(qseq, readIdx)] : 0; + size_t curRefBase = (BAM_CONSUME_REF(op)) ? samToTwoBit[ref.baseAt(uTranscriptIdx, readStrand)] : 0; advanceInReference = false; for (size_t i = 0; i < opLen; ++i) { @@ -475,6 +479,7 @@ void AlignmentModel::update( } return; } + curReadBase = samToTwoBit[bam_seqi(qseq, readIdx)]; readPosBin = static_cast((readIdx * invLen)); advanceInRead = false; @@ -493,6 +498,7 @@ void AlignmentModel::update( } return; } + curRefBase = samToTwoBit[ref.baseAt(uTranscriptIdx, readStrand)]; advanceInReference = false; } @@ -510,30 +516,6 @@ void AlignmentModel::update( if (BAM_CONSUME_SEQ(op)) { ++readIdx; advanceInRead = true; - /* DEBUG -- print what happened - std::cerr << "read name = " << bam_name(read) << "\n"; - std::cerr << "curReadBase = " << readIdx << "\n"; - std::cerr << "readLen = " << bam_seq_len(read) << "\n"; - std::cerr << "ref = "; - for (size_t j = 0; j < - std::min(static_cast(bam_seq_len(read)), - static_cast(transcriptLen - transcriptIdx)); ++j) { - std::cerr << salmon::stringtools::samCodeToChar[ref.baseAt(j, - readStrand)]; - } - std::cerr << "\n"; - std::cerr << "read = "; - for (size_t j = 0; j < bam_seq_len(read); ++j) { - std::cerr << salmon::stringtools::samCodeToChar[bam_seqi(qseq, j)]; - } - std::cerr << "\nCIGAR = "; - for (size_t j = 0; j < cigarLen; ++j) { - uint32_t opLen = cigar[j] >> BAM_CIGAR_SHIFT; - enum cigar_op op = static_cast(cigar[j] & - BAM_CIGAR_MASK); std::cerr << opLen << opToChr(op); - } - std::cerr << "\n"; - */ } if (BAM_CONSUME_REF(op)) { ++uTranscriptIdx; diff --git a/src/FASTAParser.cpp b/src/FASTAParser.cpp index f74fbb0a1..c675337f4 100644 --- a/src/FASTAParser.cpp +++ b/src/FASTAParser.cpp @@ -55,6 +55,15 @@ void FASTAParser::populateTargets(std::vector& refs, for (auto& read : rg) { std::string& header = read.name; std::string name = header.substr(0, header.find_first_of(sepStr)); + + if (fastaNames.find(name) != fastaNames.end()){ + sopt.jointLog->error("Transcript {} appears twice in the transcript FASTA file. " + "Duplicate transcripts or transcripts with the same name are not allowed.", + name); + sopt.jointLog->flush(); + std::exit(1); + } + fastaNames.insert(name); auto it = nameToID.find(name); @@ -64,7 +73,6 @@ void FASTAParser::populateTargets(std::vector& refs, name); } else { - // std::string& seq = j->data[i].seq; std::string& seq = read.seq; size_t readLen = seq.length(); diff --git a/src/SalmonAlevin.cpp b/src/SalmonAlevin.cpp index 50bcb5fe9..0add43f27 100644 --- a/src/SalmonAlevin.cpp +++ b/src/SalmonAlevin.cpp @@ -711,6 +711,13 @@ void processReadsQuasi( size_t readLenRight{0}; SACollector hitCollector(qidx); + rapmap::utils::MappingConfig mc; + mc.consistentHits = consistentHits; + mc.doChaining = false; + + rapmap::hit_manager::HitCollectorInfo> leftHCInfo; + rapmap::hit_manager::HitCollectorInfo> rightHCInfo; + if (salmonOpts.fasterMapping) { hitCollector.enableNIP(); } else { @@ -816,8 +823,9 @@ void processReadsQuasi( if (not isExtractOk or (not inTr and not indOk) or not seqOk) { lh = rh = false; - } - else{ + } else{ + leftHCInfo.clear(); + rightHCInfo.clear(); //corrBarcodeIndex = barcodeMap[barcodeIndex]; jointHitGroup.setBarcode(barcodeIdx); @@ -834,14 +842,18 @@ void processReadsQuasi( jointHitGroup.setUMI(umiIdx.umiWord()); //clearing barcode string to use as false mate barcode.clear(); - lh = tooShortLeft ? false : hitCollector(barcode, - leftHits, saSearcher, - MateStatus::PAIRED_END_LEFT, - consistentHits); - rh = tooShortRight ? false : hitCollector(rp.second.seq, - rightHits, saSearcher, + lh = false; //tooShortLeft ? false : hitCollector(barcode, saSearcher, leftHCInfo); + + rh = tooShortRight ? false : hitCollector(rp.second.seq, saSearcher, rightHCInfo); + + /* + rapmap::hit_manager::hitsToMappingsSimple(*qidx, mc, + MateStatus::PAIRED_END_LEFT, + leftHCInfo, leftHits); + */ + rapmap::hit_manager::hitsToMappingsSimple(*qidx, mc, MateStatus::PAIRED_END_RIGHT, - consistentHits); + rightHCInfo, rightHits); } } //else if (alevinOpts.barcodeEnd == THREE diff --git a/src/SalmonQuantify.cpp b/src/SalmonQuantify.cpp index 99ec26f1f..90e6d6742 100644 --- a/src/SalmonQuantify.cpp +++ b/src/SalmonQuantify.cpp @@ -927,6 +927,13 @@ void processReadsQuasi( size_t readLenRight{0}; SACollector hitCollector(qidx); + rapmap::utils::MappingConfig mc; + mc.consistentHits = consistentHits; + mc.doChaining = salmonOpts.validateMappings; + + rapmap::hit_manager::HitCollectorInfo> leftHCInfo; + rapmap::hit_manager::HitCollectorInfo> rightHCInfo; + if (salmonOpts.fasterMapping) { hitCollector.enableNIP(); } else { @@ -1002,18 +1009,25 @@ void processReadsQuasi( auto& jointHits = jointHitGroup.alignments(); leftHits.clear(); rightHits.clear(); + + leftHCInfo.clear(); + rightHCInfo.clear(); + mapType = salmon::utils::MappingType::UNMAPPED; bool lh = tooShortLeft - ? false - : hitCollector(rp.first.seq, leftHits, saSearcher, - MateStatus::PAIRED_END_LEFT, consistentHits); + ? false : hitCollector(rp.first.seq, saSearcher, leftHCInfo); + + bool rh = tooShortRight + ? false : hitCollector(rp.second.seq, saSearcher, rightHCInfo); + + rapmap::hit_manager::hitsToMappingsSimple(*qidx, mc, + MateStatus::PAIRED_END_LEFT, + leftHCInfo, leftHits); - bool rh = - tooShortRight - ? false - : hitCollector(rp.second.seq, rightHits, saSearcher, - MateStatus::PAIRED_END_RIGHT, consistentHits); + rapmap::hit_manager::hitsToMappingsSimple(*qidx, mc, + MateStatus::PAIRED_END_RIGHT, + rightHCInfo, rightHits); // Consider a read as too short if both ends are too short if (tooShortLeft and tooShortRight) { @@ -1479,6 +1493,12 @@ void processReadsQuasi( bool consistentHits{salmonOpts.consistentHits}; bool quiet{salmonOpts.quiet}; + rapmap::utils::MappingConfig mc; + mc.consistentHits = consistentHits; + mc.doChaining = salmonOpts.validateMappings; + + rapmap::hit_manager::HitCollectorInfo> hcInfo; + SACollector hitCollector(qidx); if (salmonOpts.fasterMapping) { hitCollector.enableNIP(); @@ -1495,6 +1515,9 @@ void processReadsQuasi( hitCollector.enableChainScoring(); } + /** + * Setup related to mapping parameters + **/ SASearcher saSearcher(qidx); rapmap::utils::HitCounters hctr; @@ -1548,10 +1571,15 @@ void processReadsQuasi( auto& jointHitGroup = structureVec[i]; auto& jointHits = jointHitGroup.alignments(); jointHitGroup.clearAlignments(); + hcInfo.clear(); bool lh = tooShort ? false - : hitCollector(rp.seq, jointHits, saSearcher, - MateStatus::SINGLE_END, consistentHits); + : hitCollector(rp.seq, saSearcher, hcInfo); + + rapmap::hit_manager::hitsToMappingsSimple(*qidx, mc, + MateStatus::SINGLE_END, + hcInfo, jointHits); + // If the fragment was too short, record it if (tooShort) { diff --git a/src/SalmonStringUtils.cpp b/src/SalmonStringUtils.cpp index 5c26db013..637ad4521 100644 --- a/src/SalmonStringUtils.cpp +++ b/src/SalmonStringUtils.cpp @@ -2,9 +2,11 @@ #include #include #include +#include -uint8_t* salmon::stringtools::encodeSequenceInSAM(const char* src, size_t len) { - uint8_t* target = new uint8_t[static_cast(ceil(len / 2.0))](); +std::vector salmon::stringtools::encodeSequenceInSAM(const char* src, size_t len) { + //uint8_t* target = new uint8_t[static_cast(ceil(len / 2.0))](); + std::vector target(static_cast(ceil(len / 2.0)), 0); for (size_t i = 0; i < len; ++i) { size_t byte = i >> 1; size_t nibble = i & 0x1; From c4465bae2f5238e75bb92f2e8a127ed968bd765b Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Tue, 5 Jun 2018 21:23:40 -0400 Subject: [PATCH 04/10] update build script to remove librt shared library --- scripts/make-release.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/make-release.sh b/scripts/make-release.sh index 517619efa..6720ac679 100755 --- a/scripts/make-release.sh +++ b/scripts/make-release.sh @@ -51,6 +51,7 @@ cp ${DIR}/../sample_data.tgz ${DIR}/../RELEASES/${betaname}/ echo -e "Copying over other shared library dependencies\n" bash ${DIR}/../scripts/cpld.bash ${DIR}/../bin/salmon ${DIR}/../RELEASES/${betaname}/lib/ echo -e "Removing dangerous dependencies\n" +rm ${DIR}/../RELEASES/${betaname}/lib/librt.so.1 rm ${DIR}/../RELEASES/${betaname}/lib/libc.so.6 rm ${DIR}/../RELEASES/${betaname}/lib/ld-linux-x86-64.so.2 rm ${DIR}/../RELEASES/${betaname}/lib/libdl.so.2 From 505f1d03969af2eef80220ea907fcb5961206717 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Thu, 7 Jun 2018 10:01:03 -0400 Subject: [PATCH 05/10] add issue template to develop [SKIP CI] --- .github/ISSUE_TEMPLATE/bug_report.md | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..cfa550f12 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Is the bug primarily related to salmon (bulk mode) or alevin (single-cell mode)?** + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps and data to reproduce the behavior: + +Specifically, please provide at least the following information: + + * Which version of salmon was used? + * How was salmon installed (compiled, downloaded executable, through bioconda)? + * Which reference (e.g. transcriptome) was used? + * Which read files were used? + * Which which program options were used? + + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots or terminal output to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. Ubuntu Linux, OSX] + - Version [ If you are on OSX, the output of `sw_vers`. If you are on linux the output of `uname -a` and `lsb_release -a`] + +**Additional context** +Add any other context about the problem here. From 757474c6ec480fe63461faf1f2988c01ddebaba4 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Thu, 7 Jun 2018 12:11:20 -0400 Subject: [PATCH 06/10] bump required jemalloc version --- CMakeLists.txt | 30 +++++++++-------------------- cmake/Modules/FindJemalloc.cmake | 33 +++++++++++++++++++------------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32eebe29d..4c13b961a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -652,20 +652,13 @@ set (HAVE_FAST_MALLOC FALSE) # See if we have Jemalloc find_package(Jemalloc) if (Jemalloc_FOUND) + if (NOT (${JEMALLOC_VERSION} VERSION_LESS 5.1.0)) message("Found Jemalloc library --- using this memory allocator") set (FAST_MALLOC_LIB ${JEMALLOC_LIBRARIES}) set (HAVE_FAST_MALLOC TRUE) -endif() - -if (NOT HAVE_FAST_MALLOC) - # See if we have Tcmalloc - find_package(Tcmalloc) - if (Tcmalloc_FOUND) - message("Fount TCMalloc library --- using this memory allocator") - set (TCMALLOC_LIB ${Tcmalloc_LIBRARIES}) - set (FAST_MALLOC_LIB ${TCMALLOC_LIB}) - set (HAVE_FAST_MALLOC TRUE) - endif() + else() + message("Fond Jemalloc version ${JEMALLOC_VERSION}, but require >= 5.1.0. Downloading newer version") + endif() endif() if(CONDA_BUILD) @@ -679,16 +672,11 @@ if (NOT HAVE_FAST_MALLOC) message("==================================================================") ExternalProject_Add(libjemalloc DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external - DOWNLOAD_COMMAND curl -k -L https://github.com/COMBINE-lab/jemalloc/archive/4.5.0.tar.gz -o jemalloc-4.5.0.tar.gz && - ${SHASUM} e885b65b95426945655ee91a30f563c9679770c92946bcdd0795f6b78c06c221 jemalloc-4.5.0.tar.gz && - tar -xzf jemalloc-4.5.0.tar.gz - ## - #URL https://github.com/COMBINE-lab/jemalloc/archive/4.5.0.tar.gz - #DOWNLOAD_NAME jemalloc-4.5.0.tar.gz - #URL_HASH SHA1=ad3a10866ad34b446f8f133e5edcd028be2f3003 - #TLS_VERIFY FALSE - ## - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/jemalloc-4.5.0 + DOWNLOAD_COMMAND curl -k -L https://github.com/COMBINE-lab/jemalloc/archive/5.1.0.tar.gz -o jemalloc-5.1.0.tar.gz && + ${SHASUM} ff28aef89df724bd7b6bd6fde8597695514e0e3404d1afad2f1eb8b55ef378d3 jemalloc-5.1.0.tar.gz && + tar -xzf jemalloc-5.1.0.tar.gz + + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/jemalloc-5.1.0 BUILD_IN_SOURCE TRUE INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/install CONFIGURE_COMMAND sh -c "${JEMALLOC_FLAGS} ./autogen.sh --disable-debug --prefix=" diff --git a/cmake/Modules/FindJemalloc.cmake b/cmake/Modules/FindJemalloc.cmake index 6eaf21465..9141467fe 100644 --- a/cmake/Modules/FindJemalloc.cmake +++ b/cmake/Modules/FindJemalloc.cmake @@ -28,19 +28,26 @@ find_library(JEMALLOC_LIBRARY NAMES jemalloc libjemalloc ${PC_JEMALLOC_LIBRARY_DIRS} PATH_SUFFIXES lib lib64) -set(JEMALLOC_LIBRARIES ${JEMALLOC_LIBRARY}) -set(JEMALLOC_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR}) - -find_package_handle_standard_args(Jemalloc DEFAULT_MSG - JEMALLOC_LIBRARY JEMALLOC_INCLUDE_DIR) - -get_property(_type CACHE JEMALLOC_ROOT PROPERTY TYPE) -if(_type) - set_property(CACHE JEMALLOC_ROOT PROPERTY ADVANCED 1) - if("x${_type}" STREQUAL "xUNINITIALIZED") - set_property(CACHE JEMALLOC_ROOT PROPERTY TYPE PATH) - endif() +if(JEMALLOC_INCLUDE_DIR) + set(_version_regex "^#define[ \t]+JEMALLOC_VERSION[ \t]+\"([^\"]+)\".*") + file(STRINGS "${JEMALLOC_INCLUDE_DIR}/jemalloc/jemalloc.h" + JEMALLOC_VERSION REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" + JEMALLOC_VERSION "${JEMALLOC_VERSION}") + unset(_version_regex) endif() -mark_as_advanced(JEMALLOC_ROOT JEMALLOC_LIBRARY JEMALLOC_INCLUDE_DIR) +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set JEMALLOC_FOUND to TRUE +# if all listed variables are TRUE and the requested version matches. +find_package_handle_standard_args(Jemalloc REQUIRED_VARS + JEMALLOC_LIBRARY JEMALLOC_INCLUDE_DIR + VERSION_VAR JEMALLOC_VERSION) + +if(JEMALLOC_FOUND) + set(JEMALLOC_LIBRARIES ${JEMALLOC_LIBRARY}) + set(JEMALLOC_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR}) +endif() + +mark_as_advanced(JEMALLOC_INCLUDE_DIR JEMALLOC_LIBRARY) From 934934956ab0d040953f2bd68d8de5f2c823f5f7 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Thu, 7 Jun 2018 12:23:26 -0400 Subject: [PATCH 07/10] attempt to fix CI build --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c13b961a..8182342ba 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -662,9 +662,9 @@ if (Jemalloc_FOUND) endif() if(CONDA_BUILD) - set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER} CFLAGS=-fPIC CPPFLAGS=-fPIC") + set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER} CFLAGS=-fPIC CPPFLAGS=-fPIC LIBS=-ldl") else () - set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER}") + set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER} LIBS=-ldl") endif() if (NOT HAVE_FAST_MALLOC) From 7987db87f942eabcd1dca0edb928096c5bcd58b1 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Thu, 7 Jun 2018 12:28:33 -0400 Subject: [PATCH 08/10] try again --- CMakeLists.txt | 4 ++-- src/CMakeLists.txt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8182342ba..4c13b961a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -662,9 +662,9 @@ if (Jemalloc_FOUND) endif() if(CONDA_BUILD) - set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER} CFLAGS=-fPIC CPPFLAGS=-fPIC LIBS=-ldl") + set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER} CFLAGS=-fPIC CPPFLAGS=-fPIC") else () - set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER} LIBS=-ldl") + set (JEMALLOC_FLAGS "CC=${CMAKE_C_COMPILER}") endif() if (NOT HAVE_FAST_MALLOC) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a3afc6dd1..66a688b7a 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -191,6 +191,7 @@ target_link_libraries(salmon ${FAST_MALLOC_LIB} ${LIBRT} ksw2pp + ${CMAKE_DL_LIBS} ) # Link the executable @@ -213,6 +214,7 @@ target_link_libraries(unitTests ${NON_APPLECLANG_LIBS} ${FAST_MALLOC_LIB} ${LIBRT} + ${CMAKE_DL_LIBS} ) add_dependencies(salmon unitTests) From 76b01f5d054f9bc5793fc83ea894abfaad14af11 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Thu, 7 Jun 2018 13:26:50 -0400 Subject: [PATCH 09/10] be less strict about jemalloc version --- only use C++11 for now, until we push everything to 14 --- CMakeLists.txt | 13 +- tests/catch.hpp | 1264 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 909 insertions(+), 368 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c13b961a..5e1697c8d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ if(CONDA_BUILD) set (CXXSTDFLAG "-std=c++11") set (GCCVERSION "4.8.5") else () - set (CXXSTDFLAG "-std=c++14") + set (CXXSTDFLAG "-std=c++11") set (GCCVERSION "5.2") endif() @@ -652,13 +652,16 @@ set (HAVE_FAST_MALLOC FALSE) # See if we have Jemalloc find_package(Jemalloc) if (Jemalloc_FOUND) - if (NOT (${JEMALLOC_VERSION} VERSION_LESS 5.1.0)) + ## + # Don't be so stringent about the version yet + ## + #if (NOT (${JEMALLOC_VERSION} VERSION_LESS 5.1.0)) message("Found Jemalloc library --- using this memory allocator") set (FAST_MALLOC_LIB ${JEMALLOC_LIBRARIES}) set (HAVE_FAST_MALLOC TRUE) - else() - message("Fond Jemalloc version ${JEMALLOC_VERSION}, but require >= 5.1.0. Downloading newer version") - endif() + #else() + # message("Fond Jemalloc version ${JEMALLOC_VERSION}, but require >= 5.1.0. Downloading newer version") + #endif() endif() if(CONDA_BUILD) diff --git a/tests/catch.hpp b/tests/catch.hpp index 160bad074..28448ddb2 100644 --- a/tests/catch.hpp +++ b/tests/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v2.1.1 - * Generated: 2018-01-26 16:04:07.190063 + * Catch v2.2.3 + * Generated: 2018-06-06 23:11:57.601416 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. @@ -13,6 +13,10 @@ // start catch.hpp +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 2 +#define CATCH_VERSION_PATCH 3 + #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ @@ -33,9 +37,9 @@ # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wparentheses" # pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h @@ -68,7 +72,7 @@ #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) # define CATCH_PLATFORM_WINDOWS #endif @@ -116,6 +120,14 @@ namespace Catch { # define CATCH_CPP14_OR_GREATER # endif +# if __cplusplus >= 201703L +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS #endif #ifdef __clang__ @@ -136,13 +148,15 @@ namespace Catch { #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# endif +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ @@ -150,6 +164,18 @@ namespace Catch { # define CATCH_CONFIG_COLOUR_NONE #endif +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ @@ -164,6 +190,10 @@ namespace Catch { // Visual C++ #ifdef _MSC_VER +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) @@ -176,6 +206,13 @@ namespace Catch { //////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// + // Use of __COUNTER__ is suppressed during code analysis in // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly // handled by it. @@ -188,13 +225,25 @@ namespace Catch { #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS @@ -423,16 +472,20 @@ namespace Catch { public: // substrings and searches auto substr( size_type start, size_type size ) const noexcept -> StringRef; + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + private: // ownership queries - may not be consistent between calls auto isOwned() const noexcept -> bool; auto isSubstring() const noexcept -> bool; - auto data() const noexcept -> char const*; }; auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { @@ -464,24 +517,29 @@ auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { } struct NameAndTags { - NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept; + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; StringRef name; StringRef tags; }; struct AutoReg : NonCopyable { - AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; ~AutoReg(); }; } // end namespace Catch +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF + #if defined(CATCH_CONFIG_DISABLE) #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ static void TestName() #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ namespace{ \ - struct TestName : ClassName { \ + struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \ void test(); \ }; \ } \ @@ -509,7 +567,7 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ \ - struct TestName : ClassName{ \ + struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ @@ -733,13 +791,36 @@ namespace Catch { std::string convertUnknownEnumToString( E e ); template - typename std::enable_if::value, std::string>::type convertUnstreamable( T const& ) { + typename std::enable_if< + !std::is_enum::value && !std::is_base_of::value, + std::string>::type convertUnstreamable( T const& ) { return Detail::unprintableString; - }; + } + template + typename std::enable_if< + !std::is_enum::value && std::is_base_of::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + template - typename std::enable_if::value, std::string>::type convertUnstreamable( T const& value ) { + typename std::enable_if< + std::is_enum::value + , std::string>::type convertUnstreamable( T const& value ) { return convertUnknownEnumToString( value ); - }; + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif } // namespace Detail @@ -751,7 +832,9 @@ namespace Catch { typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type convert(const Fake& value) { ReusableStringStream rss; - rss << value; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); return rss.str(); } @@ -759,7 +842,11 @@ namespace Catch { static typename std::enable_if::value, std::string>::type convert( const Fake& value ) { - return Detail::convertUnstreamable( value ); +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif } }; @@ -777,6 +864,13 @@ namespace Catch { return ::Catch::Detail::stringify(static_cast::type>(e)); } +#if defined(_MANAGED) + template + std::string stringify( T^ e ) { + return ::Catch::StringMaker::convert(e); + } +#endif + } // namespace Detail // Some predefined specializations @@ -785,10 +879,12 @@ namespace Catch { struct StringMaker { static std::string convert(const std::string& str); }; +#ifdef CATCH_CONFIG_WCHAR template<> struct StringMaker { static std::string convert(const std::wstring& wstr); }; +#endif template<> struct StringMaker { @@ -798,6 +894,8 @@ namespace Catch { struct StringMaker { static std::string convert(char * str); }; + +#ifdef CATCH_CONFIG_WCHAR template<> struct StringMaker { static std::string convert(wchar_t const * str); @@ -806,23 +904,26 @@ namespace Catch { struct StringMaker { static std::string convert(wchar_t * str); }; +#endif + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? template struct StringMaker { - static std::string convert(const char* str) { + static std::string convert(char const* str) { return ::Catch::Detail::stringify(std::string{ str }); } }; template struct StringMaker { - static std::string convert(const char* str) { - return ::Catch::Detail::stringify(std::string{ str }); + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); } }; template struct StringMaker { - static std::string convert(const char* str) { - return ::Catch::Detail::stringify(std::string{ str }); + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); } }; @@ -906,6 +1007,15 @@ namespace Catch { } }; +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + namespace Detail { template std::string rangeToString(InputIterator first, InputIterator last) { @@ -1034,6 +1144,13 @@ namespace Catch { !std::is_same())), not_this_one>::value; }; +#if defined(_MANAGED) // Managed types are never ranges + template + struct is_range { + static const bool value = false; + }; +#endif + template std::string rangeToString( Range const& range ) { return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); @@ -1057,7 +1174,7 @@ namespace Catch { } template - struct StringMaker::value && !std::is_array::value>::type> { + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { static std::string convert( R const& range ) { return rangeToString( range ); } @@ -1264,7 +1381,7 @@ namespace Catch { // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) template - auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); }; + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } template auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } template @@ -1275,7 +1392,7 @@ namespace Catch { auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } template - auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); }; + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } template auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } template @@ -1719,6 +1836,7 @@ namespace Catch { Totals delta( Totals const& prevTotals ) const; + int error = 0; Counts assertions; Counts testCases; }; @@ -2310,6 +2428,54 @@ namespace Matchers { } // namespace Catch // end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include +#include + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template +class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // infering std::function is hard (but possible) and + // requires a lot of TMP. + template + Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp // start catch_matchers_string.h #include @@ -2676,7 +2842,7 @@ namespace Catch { class TestCase : public TestCaseInfo { public: - TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); TestCase withName( std::string const& _newName ) const; @@ -2693,8 +2859,7 @@ namespace Catch { TestCase makeTestCase( ITestInvoker* testCase, std::string const& className, - std::string const& name, - std::string const& description, + NameAndTags const& nameAndTags, SourceLineInfo const& lineInfo ); } @@ -3124,7 +3289,8 @@ namespace Catch { struct WarnAbout { enum What { Nothing = 0x00, - NoAssertions = 0x01 + NoAssertions = 0x01, + NoTests = 0x02 }; }; struct ShowDurations { enum OrNot { @@ -3161,10 +3327,12 @@ namespace Catch { virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual int benchmarkResolutionMultiple() const = 0; @@ -3219,8 +3387,12 @@ namespace Catch { std::string outputFilename; std::string name; std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER - std::vector reporterNames; std::vector testsOrTags; std::vector sectionsToRun; }; @@ -3240,11 +3412,13 @@ namespace Catch { bool listReporters() const; std::string getProcessName() const; + std::string const& getReporterName() const; - std::vector const& getReporterNames() const; + std::vector const& getTestsOrTags() const; std::vector const& getSectionsToRun() const override; virtual TestSpec const& testSpec() const override; + bool hasTestFilters() const override; bool showHelp() const; @@ -3254,6 +3428,7 @@ namespace Catch { std::string name() const override; bool includeSuccessfulResults() const override; bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; ShowDurations::OrNot showDurations() const override; RunTests::InWhatOrder runOrder() const override; unsigned int rngSeed() const override; @@ -3271,6 +3446,7 @@ namespace Catch { std::unique_ptr m_stream; TestSpec m_testSpec; + bool m_hasTestFilters = false; }; } // end namespace Catch @@ -3594,8 +3770,6 @@ namespace Catch { virtual Listeners const& getListeners() const = 0; }; - void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); - } // end namespace Catch // end catch_interfaces_reporter.h @@ -3603,7 +3777,7 @@ namespace Catch { #include #include #include -#include +#include #include #include @@ -3881,10 +4055,11 @@ namespace Catch { BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, // By intention FileName = LightGrey, - Warning = Yellow, + Warning = BrightYellow, ResultError = BrightRed, ResultSuccess = BrightGreen, ResultExpectedFailure = Warning, @@ -3893,7 +4068,7 @@ namespace Catch { Success = Green, OriginalExpression = Cyan, - ReconstructedExpression = Yellow, + ReconstructedExpression = BrightYellow, SecondaryText = LightGrey, Headers = White @@ -4606,7 +4781,10 @@ namespace Catch { #ifdef CATCH_TRAP #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } #else - #define CATCH_BREAK_INTO_DEBUGGER() (void)0, 0 + namespace Catch { + inline void doNothing() {} + } + #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing() #endif // end catch_debugger.h @@ -4614,9 +4792,6 @@ namespace Catch { // start catch_fatal_condition.h -#include - -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// // start catch_windows_h_proxy.h @@ -4647,16 +4822,7 @@ namespace Catch { #endif // defined(CATCH_PLATFORM_WINDOWS) // end catch_windows_h_proxy.h - -# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) - -namespace Catch { - struct FatalConditionHandler { - void reset(); - }; -} - -# else // CATCH_CONFIG_WINDOWS_SEH is defined +#if defined( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { @@ -4675,19 +4841,7 @@ namespace Catch { } // namespace Catch -# endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) - -namespace Catch { - struct FatalConditionHandler { - void reset(); - }; -} - -# else // CATCH_CONFIG_POSIX_SIGNALS is defined +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) #include @@ -4696,7 +4850,7 @@ namespace Catch { struct FatalConditionHandler { static bool isSet; - static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; + static struct sigaction oldSigActions[]; static stack_t oldSigStack; static char altStackMem[]; @@ -4709,9 +4863,15 @@ namespace Catch { } // namespace Catch -# endif // CATCH_CONFIG_POSIX_SIGNALS +#else -#endif // not Windows +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +#endif // end catch_fatal_condition.h #include @@ -4791,7 +4951,7 @@ namespace Catch { public: // !TBD We need to do this another way! - bool aborting() const override; + bool aborting() const final; private: @@ -4992,9 +5152,9 @@ namespace Catch { expr = m_info.capturedExpression; else { expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); - expr += m_info.macroName.c_str(); + expr += m_info.macroName; expr += "( "; - expr += m_info.capturedExpression.c_str(); + expr += m_info.capturedExpression; expr += " )"; } return expr; @@ -5057,7 +5217,7 @@ namespace Catch { using StringMatcher = Matchers::Impl::MatcherBase; // This is the general overload that takes a any string matcher - // There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers // the Equals matcher (so the header does not mention matchers) void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { std::string exceptionMessage = Catch::translateActiveException(); @@ -5095,7 +5255,7 @@ namespace Catch { // // See https://github.com/philsquared/Clara for more details -// Clara v1.1.1 +// Clara v1.1.4 #ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH @@ -5106,6 +5266,15 @@ namespace Catch { #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH #endif +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + // ----------- #included from clara_textflow.hpp ----------- // TextFlowCpp @@ -5471,11 +5640,9 @@ namespace detail { std::vector m_args; public: - Args( int argc, char *argv[] ) { - m_exeName = argv[0]; - for( int i = 1; i < argc; ++i ) - m_args.push_back( argv[i] ); - } + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} Args( std::initializer_list args ) : m_exeName( *args.begin() ), @@ -5661,15 +5828,13 @@ namespace detail { protected: void enforceOk() const override { - // !TBD: If no exceptions, std::terminate here or something - switch( m_type ) { - case ResultBase::LogicError: - throw std::logic_error( m_errorMessage ); - case ResultBase::RuntimeError: - throw std::runtime_error( m_errorMessage ); - case ResultBase::Ok: - break; - } + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); } std::string m_errorMessage; // Only populated if resultType is an error @@ -5739,6 +5904,16 @@ namespace detail { return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); return ParserResult::ok( ParseResultType::Matched ); } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE struct NonCopyable { NonCopyable() = default; @@ -5751,12 +5926,14 @@ namespace detail { struct BoundRef : NonCopyable { virtual ~BoundRef() = default; virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } }; struct BoundValueRefBase : BoundRef { virtual auto setValue( std::string const &arg ) -> ParserResult = 0; }; struct BoundFlagRefBase : BoundRef { virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } }; template @@ -5987,7 +6164,7 @@ namespace detail { if( token.type != TokenType::Argument ) return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); - assert( dynamic_cast( m_ref.get() ) ); + assert( !m_ref->isFlag() ); auto valueRef = static_cast( m_ref.get() ); auto result = valueRef->setValue( remainingTokens->token ); @@ -6063,14 +6240,14 @@ namespace detail { if( remainingTokens && remainingTokens->type == TokenType::Option ) { auto const &token = *remainingTokens; if( isMatch(token.token ) ) { - if( auto flagRef = dynamic_cast( m_ref.get() ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast( m_ref.get() ); auto result = flagRef->setFlag( true ); if( !result ) return InternalParseResult( result ); if( result.value() == ParseResultType::ShortCircuitAll ) return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); } else { - assert( dynamic_cast( m_ref.get() ) ); auto valueRef = static_cast( m_ref.get() ); ++remainingTokens; if( !remainingTokens ) @@ -6340,9 +6517,19 @@ namespace Catch { using namespace clara; auto const setWarning = [&]( std::string const& warning ) { - if( warning != "NoAssertions" ) + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; + + if ( warning == "NoTests" ) + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); - config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + config.warnings = static_cast( config.warnings | warningSet ); return ParserResult::ok( ParseResultType::Matched ); }; auto const loadTestNamesFromFile = [&]( std::string const& filename ) { @@ -6440,7 +6627,7 @@ namespace Catch { | Opt( config.outputFilename, "filename" ) ["-o"]["--out"] ( "output filename" ) - | Opt( config.reporterNames, "name" ) + | Opt( config.reporterName, "name" ) ["-r"]["--reporter"] ( "reporter to use (defaults to console)" ) | Opt( config.name, "name" ) @@ -6543,10 +6730,9 @@ namespace Catch { // start catch_enforce.h #include -#include #define CATCH_PREPARE_EXCEPTION( type, msg ) \ - type( static_cast( Catch::ReusableStringStream().get() << msg ).str() ) + type( ( Catch::ReusableStringStream() << msg ).str() ) #define CATCH_INTERNAL_ERROR( msg ) \ throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); #define CATCH_ERROR( msg ) \ @@ -6561,12 +6747,16 @@ namespace Catch { : m_data( data ), m_stream( openStream() ) { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser( ITagAliasRegistry::get() ); + TestSpecParser parser(ITagAliasRegistry::get()); + if (data.testsOrTags.empty()) { + parser.parse("~[.]"); // All not hidden tests + } + else { + m_hasTestFilters = true; for( auto const& testOrTags : data.testsOrTags ) parser.parse( testOrTags ); - m_testSpec = parser.testSpec(); } + m_testSpec = parser.testSpec(); } std::string const& Config::getFilename() const { @@ -6579,11 +6769,13 @@ namespace Catch { bool Config::listReporters() const { return m_data.listReporters; } std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } - std::vector const& Config::getReporterNames() const { return m_data.reporterNames; } + std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } TestSpec const& Config::testSpec() const { return m_testSpec; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } bool Config::showHelp() const { return m_data.showHelp; } @@ -6592,7 +6784,8 @@ namespace Catch { std::ostream& Config::stream() const { return m_stream->stream(); } std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } unsigned int Config::rngSeed() const { return m_data.rngSeed; } @@ -6691,8 +6884,12 @@ namespace { case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); } } @@ -6750,8 +6947,10 @@ namespace { case Colour::BrightRed: return setColour( "[1;31m" ); case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); } } static IColourImpl* instance() { @@ -6770,7 +6969,12 @@ namespace { #ifdef CATCH_PLATFORM_MAC !isDebuggerActive() && #endif - isatty(STDOUT_FILENO); +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; } IColourImpl* platformColourInstance() { ErrnoGuard guard; @@ -6904,13 +7108,16 @@ namespace Catch { ::OutputDebugStringA( text.c_str() ); } } + #else + namespace Catch { void writeToDebugConsole( std::string const& text ) { // !TBD: Need a version for Mac/ XCode and other IDEs Catch::cout() << text; } } + #endif // Platform // end catch_debug_console.cpp // start catch_debugger.cpp @@ -7133,6 +7340,8 @@ namespace Catch { # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + namespace { // Report the error condition void reportFatal( char const * const message ) { @@ -7140,15 +7349,9 @@ namespace { } } -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +#endif // signals/SEH handling -# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) - -namespace Catch { - void FatalConditionHandler::reset() {} -} - -# else // CATCH_CONFIG_WINDOWS_SEH is defined +#if defined( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { struct SignalDefs { DWORD id; const char* name; }; @@ -7188,7 +7391,6 @@ namespace Catch { void FatalConditionHandler::reset() { if (isSet) { - // Unregister handler and restore the old guarantee RemoveVectoredExceptionHandler(exceptionHandlerHandle); SetThreadStackGuarantee(&guaranteeSize); exceptionHandlerHandle = nullptr; @@ -7206,19 +7408,7 @@ PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; } // namespace Catch -# endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) - -namespace Catch { - void FatalConditionHandler::reset() {} -} - -# else // CATCH_CONFIG_POSIX_SIGNALS is defined - -#include +#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) namespace Catch { @@ -7226,6 +7416,11 @@ namespace Catch { int id; const char* name; }; + + // 32kb for the alternate stack seems to be sufficient. However, this value + // is experimentally determined, so that's not guaranteed. + constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; + static SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGILL, "SIGILL - Illegal instruction signal" }, @@ -7252,7 +7447,7 @@ namespace Catch { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; - sigStack.ss_size = SIGSTKSZ; + sigStack.ss_size = sigStackSize; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = { }; @@ -7283,13 +7478,17 @@ namespace Catch { bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + char FatalConditionHandler::altStackMem[sigStackSize] = {}; } // namespace Catch -# endif // CATCH_CONFIG_POSIX_SIGNALS +#else + +namespace Catch { + void FatalConditionHandler::reset() {} +} -#endif // not Windows +#endif // signals/SEH handling #if defined(__GNUC__) # pragma GCC diagnostic pop @@ -7323,16 +7522,18 @@ namespace Catch { // end catch_interfaces_registry_hub.cpp // start catch_interfaces_reporter.cpp -// start catch_reporter_multi.h +// start catch_reporter_listening.h namespace Catch { - class MultipleReporters : public IStreamingReporter { + class ListeningReporter : public IStreamingReporter { using Reporters = std::vector; - Reporters m_reporters; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; public: - void add( IStreamingReporterPtr&& reporter ); + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); public: // IStreamingReporter @@ -7365,7 +7566,7 @@ namespace Catch { } // end namespace Catch -// end catch_reporter_multi.h +// end catch_reporter_listening.h namespace Catch { ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) @@ -7466,27 +7667,6 @@ namespace Catch { IReporterFactory::~IReporterFactory() = default; IReporterRegistry::~IReporterRegistry() = default; - void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { - - if( !existingReporter ) { - existingReporter = std::move( additionalReporter ); - return; - } - - MultipleReporters* multi = nullptr; - - if( existingReporter->isMulti() ) { - multi = static_cast( existingReporter.get() ); - } - else { - auto newMulti = std::unique_ptr( new MultipleReporters ); - newMulti->add( std::move( existingReporter ) ); - multi = newMulti.get(); - existingReporter = std::move( newMulti ); - } - multi->add( std::move( additionalReporter ) ); - } - } // end namespace Catch // end catch_interfaces_reporter.cpp // start catch_interfaces_runner.cpp @@ -7571,11 +7751,10 @@ namespace Catch { std::size_t listTests( Config const& config ) { TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) + if( config.hasTestFilters() ) Catch::cout() << "Matching test cases:\n"; else { Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); @@ -7597,7 +7776,7 @@ namespace Catch { Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; } - if( !config.testSpec().hasFilters() ) + if( !config.hasTestFilters() ) Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; else Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; @@ -7606,8 +7785,6 @@ namespace Catch { std::size_t listTestsNamesOnly( Config const& config ) { TestSpec testSpec = config.testSpec(); - if( !config.testSpec().hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); std::size_t matchedTests = 0; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( auto const& testCaseInfo : matchedTestCases ) { @@ -7637,11 +7814,10 @@ namespace Catch { std::size_t listTags( Config const& config ) { TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) + if( config.hasTestFilters() ) Catch::cout() << "Tags for matching test cases:\n"; else { Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::map tagCounts; @@ -7732,6 +7908,24 @@ using Matchers::Impl::MatcherBase; // end catch_matchers.cpp // start catch_matchers_floating.cpp +// start catch_to_string.hpp + +#include + +namespace Catch { + template + std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp #include #include #include @@ -7811,7 +8005,7 @@ namespace Floating { // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison bool WithinAbsMatcher::match(double const& matchee) const { - return (matchee + m_margin >= m_target) && (m_target + m_margin >= m_margin); + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); } std::string WithinAbsMatcher::describe() const { @@ -7837,7 +8031,7 @@ namespace Floating { } std::string WithinUlpsMatcher::describe() const { - return "is within " + std::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); } }// namespace Floating @@ -7858,6 +8052,16 @@ Floating::WithinAbsMatcher WithinAbs(double target, double margin) { } // namespace Catch // end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp // start catch_matchers_string.cpp #include @@ -7962,6 +8166,13 @@ namespace Matchers { // end catch_matchers_string.cpp // start catch_message.cpp +// start catch_uncaught_exceptions.h + +namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h namespace Catch { MessageInfo::MessageInfo( std::string const& _macroName, @@ -8000,21 +8211,220 @@ namespace Catch { getResultCapture().pushScopedMessage( m_info ); } -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 -#endif ScopedMessage::~ScopedMessage() { - if ( !std::uncaught_exception() ){ + if ( !uncaught_exceptions() ){ getResultCapture().popScopedMessage(m_info); } } +} // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include +#include +#include + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#include //_dup and _dup2 +#define dup _dup +#define dup2 _dup2 +#define fileno _fileno +#else +#include // dup and dup2 +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + throw std::runtime_error("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + throw std::runtime_error("Could not translate errno to string"); + } + throw std::runtime_error("Could not open the temp file: " + std::string(m_buffer) + buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + throw std::runtime_error("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted #if defined(_MSC_VER) -#pragma warning(pop) + std::remove(m_buffer); #endif + } -} // end namespace Catch -// end catch_message.cpp + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +} // namespace Catch + +#if defined(_MSC_VER) +#undef dup +#undef dup2 +#undef fileno +#endif +// end catch_output_redirect.cpp // start catch_random_number_generator.cpp // start catch_random_number_generator.h @@ -8118,7 +8528,7 @@ namespace Catch { void invoke() const override; }; - std::string extractClassName( std::string const& classOrQualifiedMethodName ); + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); /////////////////////////////////////////////////////////////////////////// @@ -8343,53 +8753,12 @@ namespace Catch { namespace Catch { - class RedirectedStream { - std::ostream& m_originalStream; - std::ostream& m_redirectionStream; - std::streambuf* m_prevBuf; - - public: - RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) - : m_originalStream( originalStream ), - m_redirectionStream( redirectionStream ), - m_prevBuf( m_originalStream.rdbuf() ) - { - m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); - } - ~RedirectedStream() { - m_originalStream.rdbuf( m_prevBuf ); - } - }; - - class RedirectedStdOut { - ReusableStringStream m_rss; - RedirectedStream m_cout; - public: - RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} - auto str() const -> std::string { return m_rss.str(); } - }; - - // StdErr has two constituent streams in C++, std::cerr and std::clog - // This means that we need to redirect 2 streams into 1 to keep proper - // order of writes - class RedirectedStdErr { - ReusableStringStream m_rss; - RedirectedStream m_cerr; - RedirectedStream m_clog; - public: - RedirectedStdErr() - : m_cerr( Catch::cerr(), m_rss.get() ), - m_clog( Catch::clog(), m_rss.get() ) - {} - auto str() const -> std::string { return m_rss.str(); } - }; - RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) : m_runInfo(_config->name()), m_context(getCurrentMutableContext()), m_config(_config), m_reporter(std::move(reporter)), - m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal }, + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, m_includeSuccessfulResults( m_config->includeSuccessfulResults() ) { m_context.setRunner(this); @@ -8416,7 +8785,7 @@ namespace Catch { std::string redirectedCout; std::string redirectedCerr; - TestCaseInfo testInfo = testCase.getTestCaseInfo(); + auto const& testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting(testInfo); @@ -8623,20 +8992,26 @@ namespace Catch { Counts prevAssertions = m_totals.assertions; double duration = 0; m_shouldReportUnexpected = true; - m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; seedRng(*m_config); Timer timer; try { if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) RedirectedStdOut redirectedStdOut; RedirectedStdErr redirectedStdErr; + timer.start(); invokeActiveTestCase(); redirectedCout += redirectedStdOut.str(); redirectedCerr += redirectedStdErr.str(); - +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif } else { timer.start(); invokeActiveTestCase(); @@ -8652,12 +9027,13 @@ namespace Catch { handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); } } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); m_reporter->sectionEnded(testCaseSectionStats); } @@ -8804,22 +9180,15 @@ namespace Catch { m_timer.start(); } -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 -#endif Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); - if( std::uncaught_exception() ) + if( uncaught_exceptions() ) getResultCapture().sectionEndedEarly( endInfo ); else getResultCapture().sectionEnded( endInfo ); } } -#if defined(_MSC_VER) -#pragma warning(pop) -#endif // This indicates whether the section should be executed or not Section::operator bool() const { @@ -8864,12 +9233,12 @@ namespace Catch { void showHelp() const; void libIdentify(); - int applyCommandLine( int argc, char* argv[] ); + int applyCommandLine( int argc, char const * const * argv ); void useConfigData( ConfigData const& configData ); int run( int argc, char* argv[] ); - #if defined(WIN32) && defined(UNICODE) + #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) int run( int argc, wchar_t* const argv[] ); #endif int run(); @@ -8936,32 +9305,25 @@ namespace Catch { return reporter; } -#ifndef CATCH_CONFIG_DEFAULT_REPORTER -#define CATCH_CONFIG_DEFAULT_REPORTER "console" -#endif - IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { - auto const& reporterNames = config->getReporterNames(); - if (reporterNames.empty()) - return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); - - IStreamingReporterPtr reporter; - for (auto const& name : reporterNames) - addReporter(reporter, createReporter(name, config)); - return reporter; - } + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } -#undef CATCH_CONFIG_DEFAULT_REPORTER + auto multi = std::unique_ptr(new ListeningReporter); - void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); - for (auto const& listener : listeners) - addReporter(reporters, listener->create(Catch::ReporterConfig(config))); + for (auto const& listener : listeners) { + multi->addListener(listener->create(Catch::ReporterConfig(config))); + } + multi->addReporter(createReporter(config->getReporterName(), config)); + return std::move(multi); } Catch::Totals runTests(std::shared_ptr const& config) { - IStreamingReporterPtr reporter = makeReporter(config); - addListeners(reporter, config); + // FixMe: Add listeners in order first, then add reporters. + + auto reporter = makeReporter(config); RunContext context(config, std::move(reporter)); @@ -8970,8 +9332,6 @@ namespace Catch { context.testGroupStarting(config->name(), 1, 1); TestSpec testSpec = config->testSpec(); - if (!testSpec.hasFilters()) - testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests auto const& allTestCases = getAllTestCasesSorted(*config); for (auto const& testCase : allTestCases) { @@ -8981,6 +9341,20 @@ namespace Catch { context.reporter().skipTest(testCase); } + if (config->warnAboutNoTests() && totals.testCases.total() == 0) { + ReusableStringStream testConfig; + + bool first = true; + for (const auto& input : config->getTestsOrTags()) { + if (!first) { testConfig << ' '; } + first = false; + testConfig << input; + } + + context.reporter().noMatchingTestCases(testConfig.str()); + totals.error = -1; + } + context.testGroupEnded(config->name(), totals, 1, 1); return totals; } @@ -9020,7 +9394,7 @@ namespace Catch { if ( !exceptions.empty() ) { m_startupExceptions = true; Colour colourGuard( Colour::Red ); - Catch::cerr() << "Errors occured during startup!" << '\n'; + Catch::cerr() << "Errors occurred during startup!" << '\n'; // iterate over all exceptions and notify user for ( const auto& ex_ptr : exceptions ) { try { @@ -9052,7 +9426,7 @@ namespace Catch { << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; } - int Session::applyCommandLine( int argc, char* argv[] ) { + int Session::applyCommandLine( int argc, char const * const * argv ) { if( m_startupExceptions ) return 1; @@ -9089,7 +9463,7 @@ namespace Catch { return returnCode; } -#if defined(WIN32) && defined(UNICODE) +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) int Session::run( int argc, wchar_t* const argv[] ) { char **utf8Argv = new char *[ argc ]; @@ -9160,10 +9534,11 @@ namespace Catch { if( Option listed = list( config() ) ) return static_cast( *listed ); + auto totals = runTests( m_config ); // Note that on unices only the lower 8 bits are usually used, clamping // the return value to 255 prevents false negative when some multiple // of 256 tests has failed - return (std::min)( MaxExitCode, static_cast( runTests( m_config ).assertions.failed ) ); + return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast(totals.assertions.failed))); } catch( std::exception& ex ) { Catch::cerr() << ex.what() << std::endl; @@ -9470,6 +9845,13 @@ namespace Catch { #include #include +#include + +namespace { + const uint32_t byte_2_lead = 0xC0; + const uint32_t byte_3_lead = 0xE0; + const uint32_t byte_4_lead = 0xF0; +} namespace Catch { StringRef::StringRef( char const* rawChars ) noexcept @@ -9491,7 +9873,7 @@ namespace Catch { const_cast( this )->takeOwnership(); return m_start; } - auto StringRef::data() const noexcept -> char const* { + auto StringRef::currentData() const noexcept -> char const* { return m_start; } @@ -9534,13 +9916,12 @@ namespace Catch { // Make adjustments for uft encodings for( size_type i=0; i < m_size; ++i ) { char c = m_start[i]; - if( ( c & 0b11000000 ) == 0b11000000 ) { - if( ( c & 0b11100000 ) == 0b11000000 ) + if( ( c & byte_2_lead ) == byte_2_lead ) { + noChars--; + if (( c & byte_3_lead ) == byte_3_lead ) + noChars--; + if( ( c & byte_4_lead ) == byte_4_lead ) noChars--; - else if( ( c & 0b11110000 ) == 0b11100000 ) - noChars-=2; - else if( ( c & 0b11111000 ) == 0b11110000 ) - noChars-=3; } } return noChars; @@ -9561,7 +9942,12 @@ namespace Catch { } auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { - return os << str.c_str(); + return os.write(str.currentData(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.currentData(), rhs.size()); + return lhs; } } // namespace Catch @@ -9665,7 +10051,7 @@ namespace Catch { return TestCaseInfo::None; } bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); } void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { CATCH_ENFORCE( !isReservedTag(tag), @@ -9676,8 +10062,7 @@ namespace Catch { TestCase makeTestCase( ITestInvoker* _testCase, std::string const& _className, - std::string const& _name, - std::string const& _descOrTags, + NameAndTags const& nameAndTags, SourceLineInfo const& _lineInfo ) { bool isHidden = false; @@ -9686,6 +10071,7 @@ namespace Catch { std::vector tags; std::string desc, tag; bool inTag = false; + std::string _descOrTags = nameAndTags.tags; for (char c : _descOrTags) { if( !inTag ) { if( c == '[' ) @@ -9713,8 +10099,8 @@ namespace Catch { tags.push_back( "." ); } - TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); - return TestCase( _testCase, info ); + TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); } void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { @@ -9774,7 +10160,7 @@ namespace Catch { return ret; } - TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} TestCase TestCase::withName( std::string const& _newName ) const { TestCase other( *this ); @@ -9885,7 +10271,7 @@ namespace Catch { m_testAsFunction(); } - std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; if( startsWith( className, '&' ) ) { @@ -9903,7 +10289,7 @@ namespace Catch { // start catch_test_case_tracker.cpp #include -#include +#include #include #include #include @@ -10182,17 +10568,16 @@ namespace Catch { return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); } - NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} - AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { try { getMutableRegistryHub() .registerTest( makeTestCase( invoker, extractClassName( classOrMethod ), - nameAndTags.name, - nameAndTags.tags, + nameAndTags, lineInfo)); } catch (...) { // Do not throw when constructing global objects, instead register the exception to be processed later @@ -10340,6 +10725,8 @@ namespace Catch { #include +static const uint64_t nanosecondsInSecond = 1000000000; + namespace Catch { auto getCurrentNanosecondsSinceEpoch() -> uint64_t { @@ -10350,17 +10737,25 @@ namespace Catch { uint64_t sum = 0; static const uint64_t iterations = 1000000; + auto startTime = getCurrentNanosecondsSinceEpoch(); + for( std::size_t i = 0; i < iterations; ++i ) { uint64_t ticks; uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); do { ticks = getCurrentNanosecondsSinceEpoch(); - } - while( ticks == baseTicks ); + } while( ticks == baseTicks ); auto delta = ticks - baseTicks; sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / i; + } } // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers @@ -10496,6 +10891,7 @@ std::string StringMaker::convert(const std::string& str) { return s; } +#ifdef CATCH_CONFIG_WCHAR std::string StringMaker::convert(const std::wstring& wstr) { std::string s; s.reserve(wstr.size()); @@ -10504,6 +10900,7 @@ std::string StringMaker::convert(const std::wstring& wstr) { } return ::Catch::Detail::stringify(s); } +#endif std::string StringMaker::convert(char const* str) { if (str) { @@ -10519,6 +10916,7 @@ std::string StringMaker::convert(char* str) { return{ "{null string}" }; } } +#ifdef CATCH_CONFIG_WCHAR std::string StringMaker::convert(wchar_t const * str) { if (str) { return ::Catch::Detail::stringify(std::wstring{ str }); @@ -10533,6 +10931,7 @@ std::string StringMaker::convert(wchar_t * str) { return{ "{null string}" }; } } +#endif std::string StringMaker::convert(int value) { return ::Catch::Detail::stringify(static_cast(value)); @@ -10672,6 +11071,20 @@ namespace Catch { } // end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +#include + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp // start catch_version.cpp #include @@ -10704,7 +11117,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 1, 1, "", 0 ); + static Version version( 2, 2, 3, "", 0 ); return version; } @@ -10755,49 +11168,141 @@ namespace Catch { #include +using uchar = unsigned char; + namespace Catch { +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + } + +} // anonymous namespace + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) : m_str( str ), m_forWhat( forWhat ) {} void XmlEncode::encodeTo( std::ostream& os ) const { - // Apostrophe escaping not necessary if we always use " to write attributes // (see: http://www.w3.org/TR/xml/#syntax) - for( std::size_t i = 0; i < m_str.size(); ++ i ) { - char c = m_str[i]; - switch( c ) { - case '<': os << "<"; break; - case '&': os << "&"; break; + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); break; + } - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - else - os << c; + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); break; + } - default: - // Escape control chars - based on contribution by @espenalb in PR #465 and - // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast( c ); - } - else - os << c; + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; } } } @@ -10939,7 +11444,7 @@ namespace Catch { #include #include #include -#include +#include #include namespace Catch { @@ -11882,7 +12387,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter) // end catch_reporter_console.cpp // start catch_reporter_junit.cpp -#include +#include #include #include #include @@ -11933,7 +12438,7 @@ namespace Catch { m_reporterPrefs.shouldRedirectStdOut = true; } - JunitReporter::~JunitReporter() {}; + JunitReporter::~JunitReporter() {} std::string JunitReporter::getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; @@ -12115,100 +12620,133 @@ namespace Catch { } // end namespace Catch // end catch_reporter_junit.cpp -// start catch_reporter_multi.cpp +// start catch_reporter_listening.cpp + +#include namespace Catch { - void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { - m_reporters.push_back( std::move( reporter ) ); + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } + + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); } - ReporterPreferences MultipleReporters::getPreferences() const { - return m_reporters[0]->getPreferences(); + ReporterPreferences ListeningReporter::getPreferences() const { + return m_reporter->getPreferences(); } - std::set MultipleReporters::getSupportedVerbosities() { + std::set ListeningReporter::getSupportedVerbosities() { return std::set{ }; } - void MultipleReporters::noMatchingTestCases( std::string const& spec ) { - for( auto const& reporter : m_reporters ) - reporter->noMatchingTestCases( spec ); + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); + } + m_reporter->noMatchingTestCases( spec ); } - void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { - for( auto const& reporter : m_reporters ) - reporter->benchmarkStarting( benchmarkInfo ); + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); } - void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { - for( auto const& reporter : m_reporters ) - reporter->benchmarkEnded( benchmarkStats ); + void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); } - void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { - for( auto const& reporter : m_reporters ) - reporter->testRunStarting( testRunInfo ); + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); + } + m_reporter->testRunStarting( testRunInfo ); } - void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { - for( auto const& reporter : m_reporters ) - reporter->testGroupStarting( groupInfo ); + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); + } + m_reporter->testGroupStarting( groupInfo ); } - void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { - for( auto const& reporter : m_reporters ) - reporter->testCaseStarting( testInfo ); + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); + } + m_reporter->testCaseStarting( testInfo ); } - void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { - for( auto const& reporter : m_reporters ) - reporter->sectionStarting( sectionInfo ); + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); } - void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { - for( auto const& reporter : m_reporters ) - reporter->assertionStarting( assertionInfo ); + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); } // The return value indicates if the messages buffer should be cleared: - bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { - bool clearBuffer = false; - for( auto const& reporter : m_reporters ) - clearBuffer |= reporter->assertionEnded( assertionStats ); - return clearBuffer; + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); } - void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { - for( auto const& reporter : m_reporters ) - reporter->sectionEnded( sectionStats ); + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); + } + m_reporter->sectionEnded( sectionStats ); } - void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { - for( auto const& reporter : m_reporters ) - reporter->testCaseEnded( testCaseStats ); + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); + } + m_reporter->testCaseEnded( testCaseStats ); } - void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { - for( auto const& reporter : m_reporters ) - reporter->testGroupEnded( testGroupStats ); + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); + } + m_reporter->testGroupEnded( testGroupStats ); } - void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { - for( auto const& reporter : m_reporters ) - reporter->testRunEnded( testRunStats ); + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); + } + m_reporter->testRunEnded( testRunStats ); } - void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { - for( auto const& reporter : m_reporters ) - reporter->skipTest( testInfo ); + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); } - bool MultipleReporters::isMulti() const { + bool ListeningReporter::isMulti() const { return true; } } // end namespace Catch -// end catch_reporter_multi.cpp +// end catch_reporter_listening.cpp // start catch_reporter_xml.cpp #if defined(_MSC_VER) @@ -12438,7 +12976,7 @@ namespace Catch { #ifndef __OBJC__ -#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) // Standard C/C++ Win32 Unicode wmain entry point extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { #else From ff9b7826741f5aa437f1e20df025d0ffa3daf7e3 Mon Sep 17 00:00:00 2001 From: Rob Patro Date: Thu, 7 Jun 2018 16:36:26 -0400 Subject: [PATCH 10/10] cosmetic enhancements; improvements to fetching script --- scripts/fetchRapMap.sh | 13 +++++++++---- src/SalmonAlevin.cpp | 14 ++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/scripts/fetchRapMap.sh b/scripts/fetchRapMap.sh index 2e5bae01c..234b81c80 100755 --- a/scripts/fetchRapMap.sh +++ b/scripts/fetchRapMap.sh @@ -22,10 +22,12 @@ if [ -d ${INSTALL_DIR}/src/rapmap ] ; then rm -fr ${INSTALL_DIR}/src/rapmap fi -#SVER=salmon-v0.10.1 -SVER=develop-salmon +SVER=salmon-v0.10.2 +#SVER=develop-salmon #SVER=pe-chaining +EXPECTED_SHA256=c4ca27de299bee395b404d117d9d98ad9f0d6ee256ea1deb2890ea402893e688 + mkdir -p ${EXTERNAL_DIR} curl -k -L https://github.com/COMBINE-lab/RapMap/archive/${SVER}.zip -o ${EXTERNAL_DIR}/rapmap.zip @@ -41,8 +43,11 @@ fi if [ -z "${hashcheck-}" ]; then echo "Couldn't find shasum command; can't verify contents of downloaded RapMap"; else - #echo "fca1323154b884a81bd61e3bdda06502fd3593f1622ad3b2dc98ef14dc4e6b6a ${EXTERNAL_DIR}/rapmap.zip" | ${hashcheck} -c - || { echo "rapmap.zip did not match expected SHA1! Exiting."; exit 1; } - echo "not testing sha in develop branch" + if [[ $SVER != develop-salmon ]]; then + echo "${EXPECTED_SHA256} ${EXTERNAL_DIR}/rapmap.zip" | ${hashcheck} -c - || { echo "rapmap.zip did not match expected SHA1! Exiting."; exit 1; } + else + echo "not testing sha since pulling from develop-salmon" + fi fi diff --git a/src/SalmonAlevin.cpp b/src/SalmonAlevin.cpp index 0add43f27..b0dd362c0 100644 --- a/src/SalmonAlevin.cpp +++ b/src/SalmonAlevin.cpp @@ -766,6 +766,8 @@ void processReadsQuasi( auto& jointHits = jointHitGroup.alignments(); leftHits.clear(); rightHits.clear(); + leftHCInfo.clear(); + rightHCInfo.clear(); mapType = salmon::utils::MappingType::UNMAPPED; ////////////////////////////////////////////////////////////// @@ -824,8 +826,6 @@ void processReadsQuasi( if (not isExtractOk or (not inTr and not indOk) or not seqOk) { lh = rh = false; } else{ - leftHCInfo.clear(); - rightHCInfo.clear(); //corrBarcodeIndex = barcodeMap[barcodeIndex]; jointHitGroup.setBarcode(barcodeIdx); @@ -842,15 +842,9 @@ void processReadsQuasi( jointHitGroup.setUMI(umiIdx.umiWord()); //clearing barcode string to use as false mate barcode.clear(); - lh = false; //tooShortLeft ? false : hitCollector(barcode, saSearcher, leftHCInfo); - + // There is no point in trying to map the barcode + lh = false; rh = tooShortRight ? false : hitCollector(rp.second.seq, saSearcher, rightHCInfo); - - /* - rapmap::hit_manager::hitsToMappingsSimple(*qidx, mc, - MateStatus::PAIRED_END_LEFT, - leftHCInfo, leftHits); - */ rapmap::hit_manager::hitsToMappingsSimple(*qidx, mc, MateStatus::PAIRED_END_RIGHT, rightHCInfo, rightHits);