Skip to content
Closed
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
7 changes: 4 additions & 3 deletions cucumber_cpp/library/formatter/JunitXmlFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,15 @@ namespace cucumber_cpp::library::formatter
};
}

std::string FormatStep(const cucumber::messages::step& gherkinStep, const cucumber::messages::pickle_step& pickleStep, cucumber::messages::test_step_result_status status)
std::string FormatStep(const cucumber::messages::step& gherkinStep, const cucumber::messages::pickle_step& pickleStep, cucumber::messages::test_step_result_status status, std::chrono::milliseconds duration)
{
Comment on lines +83 to 84
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

This file now names std::chrono::milliseconds in the FormatStep signature, but it doesn’t include <chrono> directly (it currently relies on transitive inclusion via util/Duration.hpp). Consider adding an explicit #include <chrono> here to follow include hygiene / IWYU and avoid brittle builds if headers change.

Copilot uses AI. Check for mistakes.
auto statusString = std::string{ cucumber::messages::to_string(status) };
std::transform(statusString.begin(), statusString.end(), statusString.begin(), [](unsigned char c)
{
return std::tolower(c);
});

return fmt::format("{:.<76}{}", util::Trim(gherkinStep.keyword) + " " + util::Trim(pickleStep.text), statusString);
return fmt::format("{:.<76}{} ({} ms)", util::Trim(gherkinStep.keyword) + " " + util::Trim(pickleStep.text), statusString, duration.count());
}

std::string MakeOutput(query::Query& query, const cucumber::messages::test_case_started& testCaseStarted)
Expand All @@ -105,7 +105,8 @@ namespace cucumber_cpp::library::formatter
const auto [testStepFinished, testStep] = pair;
const auto& pickleStep = *query.FindPickleStepBy(*testStep);
const auto& gherkinStep = query.FindStepBy(pickleStep);
return FormatStep(gherkinStep, pickleStep, testStepFinished->test_step_result.status);
const auto durationMs = util::DurationToMilliseconds(query.FindTestStepDurationByTestStepId(testStepFinished->test_step_id));
return FormatStep(gherkinStep, pickleStep, testStepFinished->test_step_result.status, durationMs);
Comment on lines +108 to +109
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

MakeOutput() now calls Query::FindTestStepDurationByTestStepId() unconditionally. That query method uses .at() on testStepStartedByTestStepId/testStepFinishedByTestStepId and will throw std::out_of_range if a test_step_started event was never recorded (e.g., for SKIPPED / UNDEFINED / AMBIGUOUS steps). Consider guarding duration lookup based on status (similar to UsageFormatter's HasExecuted()), or falling back to 0 ms / n/a when the duration can’t be computed, to avoid formatter crashes when generating JUnit XML.

Copilot uses AI. Check for mistakes.
});

return fmt::format("\n{}\n", fmt::join(outputView, "\n"));
Expand Down
Loading