Skip to content

fix: add file open error handling - #22

Merged
nh13 merged 1 commit into
mainfrom
fix/21-file-open-error
Dec 24, 2025
Merged

fix: add file open error handling#22
nh13 merged 1 commit into
mainfrom
fix/21-file-open-error

Conversation

@nh13

@nh13 nh13 commented Dec 22, 2025

Copy link
Copy Markdown
Owner

Summary

  • Add error handling when opening input files via -i option
  • Exit with descriptive error message if file cannot be opened

Background

From code review (research.md §1.1, MEDIUM severity):
Previously the program would silently process empty input on file errors because there was no is_open() check after opening the file.

Changes

  • src/caller.cpp: Add in.is_open() check after opening file, return error code 1 with message if failed
  • tests/test_caller.cpp: Add tests for file handling (non-existent file, valid file)

Test plan

  • CI workflow passes
  • ./bin/callerpp -i /nonexistent/file returns error with message

Closes #21

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error handling and reporting for missing or unopenable input files, ensuring proper resource cleanup on failure.
  • Tests

    • Added comprehensive test suite for file handling scenarios, including validation of error cases with non-existent files and successful operations with valid input files.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 22, 2025

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This change adds error handling to file opening in src/caller.cpp by checking in.is_open() after opening the input file, with appropriate error messages and cleanup. A comprehensive test suite is introduced to validate both infrastructure and file-handling scenarios, including tests for non-existent and valid files.

Changes

Cohort / File(s) Summary
Error Handling Implementation
src/caller.cpp
Adds in.is_open() checks after file open attempts in both main and alternate code paths; on failure, prints error message to stderr, deallocates opt, and exits with status code 1
Test Suite
tests/test_caller.cpp
Introduces Google Test infrastructure with two test fixtures: InfrastructureTest (validates test framework setup) and FileHandlingTest (validates file error handling with tests for non-existent files and valid FASTA files; includes RAII-based temporary file cleanup)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 A file that won't open, no longer a mystery,
We check, we report, and handle with history,
Error codes flowing like carrots in spring,
Tests all in place—what a wonderful thing! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding file open error handling to handle the case when files cannot be opened.
Linked Issues check ✅ Passed All coding requirements from issue #21 are met: file opening failure detection, is_open() check, error message printing, exit code 1 return, and tests for non-existent and valid files.
Out of Scope Changes check ✅ Passed The PR includes test infrastructure setup (Google Test, Makefile updates, CI workflow) which, while beyond the minimal fix, directly supports the issue requirement to add tests for the error handling.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9af06b4 and b292711.

📒 Files selected for processing (2)
  • src/caller.cpp
  • tests/test_caller.cpp

Comment @coderabbitai help to get the list of available commands and usage tips.

@nh13
nh13 force-pushed the fix/19-test-infrastructure branch from 58b0642 to 0fc4026 Compare December 22, 2025 23:04
@nh13
nh13 force-pushed the fix/21-file-open-error branch from f5be4e9 to 6662c3a Compare December 22, 2025 23:05
@nh13
nh13 force-pushed the fix/19-test-infrastructure branch from 0fc4026 to a0139d2 Compare December 22, 2025 23:10
@nh13
nh13 force-pushed the fix/21-file-open-error branch 2 times, most recently from a691eb5 to f8177ba Compare December 22, 2025 23:11
Base automatically changed from fix/19-test-infrastructure to main December 22, 2025 23:20
@nh13
nh13 force-pushed the fix/21-file-open-error branch from f8177ba to 8eca760 Compare December 22, 2025 23:22
@nh13

nh13 commented Dec 23, 2025

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Dec 23, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_caller.cpp (1)

22-33: Consider verifying temporary file creation.

The test doesn't check if the temporary file was successfully created before running the program. If /tmp is not writable (unlikely but possible), the test could give misleading results.

🔎 Suggested enhancement
 TEST(FileHandlingTest, ValidFileWorks) {
     // Create a temporary test file
     std::ofstream testfile("/tmp/test_callerpp.fa");
+    ASSERT_TRUE(testfile.is_open()) << "Failed to create temporary test file";
     testfile << ">test\nACGT\nACGT\n";
     testfile.close();

Optional improvements:

  • Use std::tmpfile() or a unique filename (e.g., with process ID) to avoid potential race conditions if tests run concurrently.
  • Consider using a cross-platform temporary directory approach for better portability.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50c42dc and 8eca760.

📒 Files selected for processing (2)
  • src/caller.cpp
  • tests/test_caller.cpp
🔇 Additional comments (2)
src/caller.cpp (1)

328-332: LGTM! Error handling is correct.

The implementation properly detects file open failures, provides a descriptive error message, cleans up allocated resources (free(opt)), and returns an appropriate non-zero exit code.

tests/test_caller.cpp (1)

2-3: LGTM! Necessary headers added.

The <cstdlib> header is required for std::system() and the <fstream> header for std::ofstream.

Comment thread tests/test_caller.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/test_caller.cpp (1)

25-39: Consider using unique temp filenames and ensuring cleanup on assertion failures.

The test creates a temporary file at a hardcoded path /tmp/test_callerpp.fa. This has two potential issues:

  1. Race condition: If tests run in parallel, multiple instances could conflict over the same filename.
  2. Incomplete cleanup: The std::remove() at line 38 only executes if all assertions pass. If any ASSERT_* fails, the test aborts and the temporary file remains in /tmp.
🔎 Suggested improvements

Option 1: Use a unique filename incorporating process ID

 TEST(FileHandlingTest, ValidFileWorks) {
     // Create a temporary test file
-    std::ofstream testfile("/tmp/test_callerpp.fa");
+    std::string temp_path = "/tmp/test_callerpp_" + std::to_string(getpid()) + ".fa";
+    std::ofstream testfile(temp_path);
     ASSERT_TRUE(testfile.is_open()) << "Failed to create temporary test file";
     testfile << ">test\nACGT\nACGT\n";
     testfile.close();
 
-    int result = std::system("./bin/callerpp -i /tmp/test_callerpp.fa >/dev/null 2>&1");
+    std::string cmd = "./bin/callerpp -i " + temp_path + " >/dev/null 2>&1";
+    int result = std::system(cmd.c_str());
     ASSERT_NE(result, -1) << "std::system() failed to execute";
     ASSERT_TRUE(WIFEXITED(result)) << "Process did not exit normally";
     EXPECT_EQ(WEXITSTATUS(result), 0);
 
     // Cleanup
-    std::remove("/tmp/test_callerpp.fa");
+    std::remove(temp_path.c_str());
 }

Option 2: Use RAII for automatic cleanup (requires <unistd.h> for getpid())

Create a helper class or use a scope guard pattern to ensure cleanup happens even if assertions fail. For example:

#include <unistd.h>

TEST(FileHandlingTest, ValidFileWorks) {
    std::string temp_path = "/tmp/test_callerpp_" + std::to_string(getpid()) + ".fa";
    
    // Cleanup guard
    struct FileGuard {
        std::string path;
        ~FileGuard() { std::remove(path.c_str()); }
    } guard{temp_path};
    
    std::ofstream testfile(temp_path);
    ASSERT_TRUE(testfile.is_open()) << "Failed to create temporary test file";
    testfile << ">test\nACGT\nACGT\n";
    testfile.close();
    
    std::string cmd = "./bin/callerpp -i " + temp_path + " >/dev/null 2>&1";
    int result = std::system(cmd.c_str());
    ASSERT_NE(result, -1) << "std::system() failed to execute";
    ASSERT_TRUE(WIFEXITED(result)) << "Process did not exit normally";
    EXPECT_EQ(WEXITSTATUS(result), 0);
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8eca760 and 3a8ba44.

📒 Files selected for processing (1)
  • tests/test_caller.cpp
🔇 Additional comments (2)
tests/test_caller.cpp (2)

2-4: LGTM!

The added includes are appropriate for the new test functionality: <cstdlib> for std::system(), <fstream> for file I/O, and <sys/wait.h> for POSIX exit status macros.


17-23: Excellent fix for system() return value handling!

This test now correctly handles std::system() return values on POSIX systems by checking for execution failure (-1), verifying normal process exit (WIFEXITED), and extracting the actual exit code (WEXITSTATUS). This directly addresses the previous review comment and ensures robust test behavior.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/test_caller.cpp (1)

18-24: Excellent fix for std::system() handling; consider making the test path unique.

The test now properly uses WIFEXITED and WEXITSTATUS, which addresses the previous portability concern. The error-checking logic is correct.

As an optional improvement, consider making the non-existent path more unique to avoid any theoretical collision:

🔎 Optional: use a unique non-existent path
-    int result = std::system("./bin/callerpp -i /nonexistent/path/file.fa 2>/dev/null");
+    std::string nonexistent = "/tmp/nonexistent_" + std::to_string(getpid()) + "_" + std::to_string(time(nullptr)) + ".fa";
+    std::string cmd = "./bin/callerpp -i " + nonexistent + " 2>/dev/null";
+    int result = std::system(cmd.c_str());

Note: This requires #include <ctime> for time().

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a8ba44 and 8899868.

📒 Files selected for processing (1)
  • tests/test_caller.cpp
🔇 Additional comments (2)
tests/test_caller.cpp (2)

2-5: LGTM! Necessary includes for the new tests.

All added headers are properly utilized: <cstdlib> for std::system, <fstream> for file I/O, <sys/wait.h> for POSIX wait macros, and <unistd.h> for getpid().


26-47: Excellent test design with robust cleanup and race-condition prevention!

This test demonstrates several best practices:

  • Unique filename with getpid() prevents race conditions in concurrent test runs.
  • RAII FileGuard ensures cleanup even if assertions fail.
  • Properly verifies file creation before proceeding.
  • Closes the file before invoking the program under test.
  • Correct use of WIFEXITED and WEXITSTATUS for portable exit-code checking.

The test effectively validates the successful execution path.

@nh13
nh13 force-pushed the fix/21-file-open-error branch 2 times, most recently from 39f4525 to 9af06b4 Compare December 24, 2025 16:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/test_caller.cpp (1)

21-21: Consider extracting unique filename generation to a helper function.

Both tests generate unique filenames independently. While the current duplication is minimal, a helper function could improve consistency and maintainability if more file-based tests are added in the future.

🔎 Example helper function
// Helper to generate unique temp filenames
std::string make_unique_temp_path(const std::string& prefix) {
    return "/tmp/" + prefix + "_" + std::to_string(getpid()) + "_" + 
           std::to_string(time(nullptr)) + ".fa";
}

Also applies to: 31-31

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8899868 and 39f4525.

📒 Files selected for processing (1)
  • tests/test_caller.cpp
🔇 Additional comments (3)
tests/test_caller.cpp (3)

2-6: LGTM!

The includes are all necessary for the test functionality and properly support the POSIX-compliant return value handling.


20-27: Excellent POSIX return value handling!

The test properly addresses the past review feedback by checking for std::system() failure (-1), verifying normal exit with WIFEXITED(), and extracting the exit code with WEXITSTATUS().


34-37: Well-structured RAII cleanup pattern!

The FileGuard ensures the temporary file is removed even if assertions fail, preventing test pollution.

- Add error handling for file open failures in caller.cpp
- Add Google Test framework and test infrastructure
- Add tests for file handling (non-existent and valid files)
- Use unique temp filenames and RAII cleanup in tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@nh13
nh13 force-pushed the fix/21-file-open-error branch from 9af06b4 to b292711 Compare December 24, 2025 16:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/test_caller.cpp (1)

1-7: Document POSIX-specific test implementation.

The tests use POSIX-specific headers (<sys/wait.h>, <unistd.h>) and rely on /tmp directory conventions. While this is appropriate for the Ubuntu CI environment, consider adding a comment noting these tests are POSIX-specific and won't compile on Windows.

🔎 Suggested documentation addition

Add a comment at the top of the file:

 #include <gtest/gtest.h>
 #include <cstdlib>
 #include <ctime>
 #include <fstream>
+// POSIX-specific headers for process control and file system operations
+// These tests are designed for POSIX-compliant systems (Linux, macOS)
 #include <sys/wait.h>
 #include <unistd.h>

Also applies to: 34-36

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39f4525 and 9af06b4.

📒 Files selected for processing (5)
  • .github/workflows/check.yml
  • Makefile
  • src/caller.cpp
  • tests/test_caller.cpp
  • tests/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/caller.cpp
🔇 Additional comments (8)
tests/test_main.cpp (1)

1-6: LGTM! Standard GoogleTest entry point.

This is the standard boilerplate for a GoogleTest main function and is correctly implemented.

.github/workflows/check.yml (2)

21-26: LGTM! Correct dependency installation.

The libgtest-dev package installation is necessary for building the GoogleTest-based tests and is correctly placed before the build step.


45-55: LGTM! Good separation of test concerns.

Splitting unit tests (make test) from CLI integration tests provides better clarity in CI output and makes it easier to identify which type of test failed.

Makefile (2)

4-4: LGTM! Test infrastructure correctly configured.

The test configuration properly:

  • Defines test directories and object files
  • Links against GoogleTest libraries (-lgtest -lpthread)
  • Provides compilation rules for test sources
  • Creates a test target that builds and runs the test executable

The tests are pure CLI integration tests (no main program objects linked), which is appropriate for this PR's scope.

Also applies to: 12-16, 25-27, 33-35, 41-43


6-6: C++14 upgrade is required for GoogleTest 1.14.0 compatibility.

The CI environment on Ubuntu 24.04 provides GoogleTest 1.14.0, which requires C++14. The upgrade from C++11 to C++14 is necessary and cannot be reverted to C++11.

Likely an incorrect or invalid review comment.

tests/test_caller.cpp (3)

8-17: LGTM! Placeholder tests verify framework.

These simple tests confirm the GoogleTest infrastructure is working correctly before adding more complex tests.


20-27: LGTM! Robust error handling test.

The test correctly:

  • Generates a unique nonexistent path to avoid conflicts
  • Uses WIFEXITED and WEXITSTATUS for proper exit status checking
  • Redirects stderr to keep test output clean
  • Verifies the program returns a non-zero exit code on file open failure

29-50: The minimal FASTA input is sufficient for callerpp. The code requires only a valid header and at least one sequence (lines 345–349 in src/caller.cpp return an error only if the sequence vector is empty), with no minimum length or sequence count restrictions beyond that. The test's 2 sequences of 4bp each satisfy all requirements, and the default alignment parameters work correctly for generating a consensus. The test is appropriately scoped for verifying file opening and basic program execution.

@nh13
nh13 merged commit d42ed8c into main Dec 24, 2025
1 of 2 checks passed
@nh13
nh13 deleted the fix/21-file-open-error branch December 24, 2025 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add file open error handling

1 participant