Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
^CLAUDE\.md$
^\.claude$
^vignettes/articles$
^catch2-update$
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ Authors@R: c(
person("Hadley", "Wickham", , "hadley@posit.co", role = c("aut", "cre")),
person("Posit Software, PBC", role = c("cph", "fnd")),
person("R Core team", role = "ctb",
comment = "Implementation of utils::recover()")
comment = "Implementation of utils::recover()"),
person("Mauricio", "Vargas Sepulveda", role = "ctb",
comment = "Compatibility with C++23 standard")
)
Description: Software testing is important, but, in part because it is
frustrating and boring, many of us avoid it. 'testthat' is a testing
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

## Other new features

* Updated vendored Catch framework from v1.9.6 (2017) to Catch2 v2.13.10 (2022). This fixes compilation issues with the C++23 standard. @pachadotdev
* New `local_mocked_s3_method()`, `local_mocked_s4_method()`, and `local_mocked_r6_class()` allow you to mock S3 and S4 methods and R6 classes (#1892, #1916)
* New `local_on_cran(TRUE)` allows you to simulate how your tests will run on CRAN (#2112).
* `test_dir()`, `test_file()`, `test_package()`, `test_check()`, `test_local()`, `source_file()` gain a `shuffle` argument that uses `sample()` to randomly reorder the top-level expressions in each test file (#1942). This random reordering surfaces dependencies between tests and code outside of any test, as well as dependencies between tests. This helps you find and eliminate unintentional dependencies.
Expand Down
22 changes: 22 additions & 0 deletions catch2-update/01-download.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

cd ~/Documents/testthat/
cp inst/include/testthat/vendor/catch.h catch2-update/catch.h.v1.9.6.backup
rm inst/include/testthat/vendor/catch.h

# Download Catch2 v2.13.10 single header
echo "Downloading Catch2 v2.13.10..."
wget -O catch2-update/catch.hpp \
https://github.com/catchorg/Catch2/releases/download/v2.13.10/catch.hpp

# Verify download
if [ -f catch2-update/catch.hpp ]; then
echo "Download successful!"
echo "File size: $(wc -c < catch2-update/catch.hpp) bytes"

# Check the header to verify it's Catch2 v2.13.10
head -n 10 catch2-update/catch.hpp
else
echo "ERROR: Download failed!"
exit 1
fi
29 changes: 29 additions & 0 deletions catch2-update/02-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/sh

cd ~/Documents/testthat/

# Verify we have the downloaded file
if [ ! -f catch2-update/catch.hpp ]; then
echo "ERROR: catch.hpp not found. Run 01-download.sh first."
exit 1
fi

# Install the new Catch2 header
echo "Installing Catch2 v2.13.10..."
mv catch2-update/catch.hpp inst/include/testthat/vendor/catch.hpp

# Change: #include "vendor/catch.h" to #include "vendor/catch.hpp"
sed 's|#include "vendor/catch\.h"|#include "vendor/catch.hpp"|g' \
inst/include/testthat/testthat.h

if [ $? -eq 0 ]; then
echo "Verifying change..."
grep -n 'catch\.hpp' inst/include/testthat/testthat.h
else
echo "ERROR: Failed to update testthat.h"
exit 1
fi

echo "Catch2 v2.13.10 installed to inst/include/testthat/vendor/catch.hpp"
echo ""
echo "inst/include/testthat/testthat.h now uses catch.hpp instead of catch.h"
79 changes: 79 additions & 0 deletions catch2-update/03-patch-r-compat.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/sh

# Patch Catch2 to be compatible with R CMD check requirements
# Fixes warnings about abort, sprintf, and srand

cd ~/Documents/testthat/

CATCH_FILE="inst/include/testthat/vendor/catch.hpp"

if [ ! -f "$CATCH_FILE" ]; then
echo "ERROR: $CATCH_FILE not found. Run 02-install.sh first."
exit 1
fi

echo "Creating backup of catch.hpp..."
cp "$CATCH_FILE" "$CATCH_FILE.backup"

echo "Patching Catch2 for R compatibility..."

# First, add R header includes after the initial header guard
echo " - Adding R headers..."
sed -i '/#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED/a\
#ifdef __cplusplus\
extern "C" {\
#endif\
#include <R_ext/Error.h>\
#ifdef __cplusplus\
}\
#endif' "$CATCH_FILE"

# Fix 1: Replace std::abort() with Rf_error()
# This uses R's native error mechanism instead of terminating the process
echo " - Replacing abort() with Rf_error()..."
sed -i 's/std::abort();/Rf_error("Fatal error in test framework");/g' "$CATCH_FILE"

# Fix 2: Replace std::sprintf() with std::snprintf() for safety
# snprintf is safer and avoids R CMD check warnings
echo " - Replacing sprintf() with snprintf()..."
sed -i 's/std::sprintf(buffer, "%.3f", duration);/std::snprintf(buffer, sizeof(buffer), "%.3f", duration);/g' "$CATCH_FILE"

# Fix 3: Comment out std::srand() - R uses its own RNG
# R packages should use R's RNG via GetRNGstate()/PutRNGstate()
echo " - Commenting out srand()..."
sed -i 's/std::srand(config.rngSeed());/\/\/ std::srand(config.rngSeed()); \/\/ Disabled for R compatibility/g' "$CATCH_FILE"

echo ""
echo "Verifying patches..."
echo ""

echo "1. Checking abort() was replaced:"
if grep -q "std::abort()" "$CATCH_FILE"; then
echo " WARNING: std::abort() still found!"
else
echo " No abort() calls found"
fi

echo ""
echo "2. Checking sprintf() was replaced:"
if grep -q "std::sprintf(buffer" "$CATCH_FILE"; then
echo " WARNING: std::sprintf() still found!"
else
echo " No sprintf() calls found"
fi

echo ""
echo "3. Checking srand() was commented out:"
if grep -q "^[[:space:]]*std::srand(config.rngSeed());" "$CATCH_FILE"; then
echo " WARNING: Uncommented std::srand() still found!"
else
echo " srand() has been disabled"
fi

echo ""
echo "Patch complete!"
echo ""
echo "To test: R CMD INSTALL --preclean . && R CMD check --as-cran ."
echo ""
echo "If you need to restore the original, run:"
echo " mv $CATCH_FILE.backup $CATCH_FILE"
86 changes: 86 additions & 0 deletions catch2-update/04-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/bin/sh

cd ~/Documents/testthat/

echo "======================================"
echo "Testing Catch2 v2.13.10 Installation"
echo "======================================"
echo ""

# Test 1: Check if files exist
echo "[1/5] Checking files..."
if [ -f inst/include/testthat/vendor/catch.hpp ]; then
echo " catch.hpp exists"
else
echo " catch.hpp missing"
exit 1
fi

# Test 2: Verify testthat.h includes catch.hpp
echo "[2/5] Checking testthat.h includes catch.hpp..."
if grep -q 'catch\.hpp' inst/include/testthat/testthat.h; then
echo " testthat.h includes catch.hpp"
else
echo " testthat.h still includes catch.h"
exit 1
fi

# Test 3: Test C++11 compilation
echo "[3/5] Testing C++11 compilation..."
cat > /tmp/test_catch2_cxx11.cpp << 'EOF'
#define TESTTHAT_TEST_RUNNER
#include "testthat.h"
EOF

g++ -std=gnu++11 \
-I inst/include \
-I /usr/include/R \
-c /tmp/test_catch2_cxx11.cpp \
-o /tmp/test_catch2_cxx11.o 2>&1 | head -20

if [ $? -eq 0 ]; then
echo " C++11 compilation successful"
rm -f /tmp/test_catch2_cxx11.o
else
echo " C++11 compilation failed"
fi

# Test 4: Test C++23 compilation
echo "[4/5] Testing C++23 compilation..."
cat > /tmp/test_catch2_cxx23.cpp << 'EOF'
#define TESTTHAT_TEST_RUNNER
#include "testthat.h"
EOF

g++ -std=gnu++23 \
-I inst/include \
-I /usr/include/R \
-c /tmp/test_catch2_cxx23.cpp \
-o /tmp/test_catch2_cxx23.o 2>&1 | head -20

if [ $? -eq 0 ]; then
echo " C++23 compilation successful - THIS IS THE FIX!"
rm -f /tmp/test_catch2_cxx23.o
else
echo " C++23 compilation failed"
exit 1
fi

# Test 5: Build testthat package
echo "[5/5] Building testthat package..."
R CMD INSTALL --preclean . > /tmp/testthat_install.log 2>&1

if [ $? -eq 0 ]; then
echo " Package build successful"
echo ""
echo "======================================"
echo "All tests passed!"
echo "======================================"
else
echo " Package build failed"
echo "See /tmp/testthat_install.log for details"
exit 1
fi

# Cleanup
rm -f /tmp/test_catch2_*.cpp /tmp/test_catch2_*.o
9 changes: 8 additions & 1 deletion inst/include/testthat/testthat.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ inline int rand() { return 42; }
inline void exit(int) throw() {}

}
# include "vendor/catch.h"
# include "vendor/catch.hpp"

// Implement an output stream that avoids writing to stdout / stderr.
extern "C" void Rprintf(const char*, ...);
Expand Down Expand Up @@ -158,6 +158,13 @@ inline std::ostream& cerr()
return instance;
}

TESTTHAT_ATTRIBUTE_HIDDEN
inline std::ostream& clog()
{
static testthat::r_ostream instance;
return instance;
}

} // namespace Catch

# ifdef TESTTHAT_TEST_RUNNER
Expand Down
Loading