Set Up
First, I created CI.yml to add a CI workflow to my project.I realized that GitHub's recommended CI file wouldn't help much. For that reason, I planned to write my own version supporting Linux, Windows, and macOS. This decision led to significant struggles while making a PR to test my naive CI implementation. At the same time, I added comprehensive tests by creating git_info_test to test git_info module and test CI , basically killing two birds with one stone.
The vcpkg Removal Decision
After creating a PR from my add-more-tests branch, CI failed. I checked the error and figured out I needed to remove one line for Windows:
- name: Setup vcpkg and install libgit2 (Windows)
if: runner.os == 'Windows'
run: |
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg install libgit2:x64-windows # This is redundant!
I realized this manual install was unnecessary because my project uses vcpkg.json with:
{
"name": "repoctx",
"version": "0.1.0",
"dependencies": [
"libgit2"
]
}
When vcpkg sees this file, it automatically installs dependencies,no manual install needed in CI. I corrected my mistake on GitHub's UI, but problems arose afterward. Back on my local branch, I continued working on my tests and made changes to fix Windows-specific issues. Without pulling the remote changes first, I was forced to use git push origin add-more-tests --force-with-lease, which I don't recommend to anyone. It can destroy work. While it's a safer version of git push --force (especially when two people work on the same branch, as it won't allow the force push), it's still bad practice and not a good standard, especially when contributing to open source projects, most of which prohibit force pushing.
Windows Testing Nightmare
Writing tests for Git operations seemed simple, create a temporary repo, run some git commands, verify the output. It worked perfectly on my Mac. Then I pushed to CI and watched Windows fail spectacularly.
My Original Test Code
void createTestGitRepo() {
system("git init");
system("git config user.email 'test@gmail.com'");
system("git config user.name 'Test User'");
system("git commit -m 'Initial Commit'");
}
Can you see the errors that aren't suitable for Windows? I removed single quotes and spaces. At this moment, I truly understood the benefit of CI workflows, something I'm used to seeing in open source projects, which keeps our master or main branch safer.
The Working Solution
system("git init");
system("git config user.email test@gmail.com");
system("git config user.name TestUser");
system("git commit -m InitialCommit");
I thought it was time to relax, but CI failed again. I wasn't handling cleanup gracefully for Windows. By passing std::error_code, the function stores the error instead of throwing an exception. I also gave each test its own directory, which prevents test contamination, works around Windows file locking, and is required for reliable cross-platform tests.
Graceful Error Handling and Different Directories for Tests
void SetUp() override {
auto testInfo = ::testing::UnitTest::GetInstance()->current_test_info();
std::string testName = std::string(testInfo->name());
testRepoPath = std::filesystem::temp_directory_path() / ("test_git_repo_" + testName);
if(std::filesystem::exists(testRepoPath)) {
std::error_code ec;
std::filesystem::remove_all(testRepoPath, ec);
}
std::filesystem::create_directories(testRepoPath);
}
void TearDown() override {
if(std::filesystem::exists(testRepoPath)) {
std::error_code ec;
std::filesystem::remove_all(testRepoPath, ec);
}
}
Contribution
After all, I made a PR to my groupmate's project. The project structure, modules, and setup were clear, so I didn't struggle much. I kept each test independent to avoid interference. Each test creates its own spy on process.stderr.write to capture the output, ensuring correct messages are written. Finally, it cleans up by restoring the original function.
Usage
it('handles verbose mode correctly', () => {
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
logVerbose('Debug info', true);
expect(stderrSpy).toHaveBeenCalledWith('Debug info\n');
stderrSpy.mockClear();
logVerbose('Hidden info', false);
expect(stderrSpy).not.toHaveBeenCalled();
stderrSpy.mockRestore();
});
Gained Knowledge
I learned the importance of being more precise and paying attention to every detail before making a PR. I already knew this, but CI was a great lesson in precision. I'm also becoming more comfortable writing tests and understanding test semantics.
Top comments (0)