Skip to content

Commit

Permalink
Merge pull request #49 from a7ex/feature/48-support-multiple-configur…
Browse files Browse the repository at this point in the history
…ations-in-test-planxcresult

48 - Add 'configuration' property to test export
  • Loading branch information
a7ex authored Jan 13, 2025
2 parents 942bc70 + 4d22c4c commit 344eeaf
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,8 @@ iOSInjectionProject/

# Compiled app, ready for notarization
product/


.scannerwork/
sonarqube-coverage.xml
sonarqube-testresults.xml
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Version 1.8.4 - 2025-01-12
### CHANGES:
- Add 'configuration' property to test export
- Add support for sonarqube on sonarcloud.io (in order to verify, that this change doesn't break the sonarqube scan)

## Version 1.8.3 - 2024-12-15
### CHANGES:
- Add .mm files to grep filter to resolve junit class names to files
Expand Down
2 changes: 1 addition & 1 deletion CommandlineTool/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ArgumentParser
import Foundation
import XcresultparserLib

private let marketingVersion = "1.8.3"
private let marketingVersion = "1.8.4"

struct xcresultparser: ParsableCommand {
static let configuration = CommandConfiguration(
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ You should see the tool respond like this:
```
Error: Missing expected argument '<xcresult-file>'
OVERVIEW: xcresultparser 1.8.3
OVERVIEW: xcresultparser 1.8.4
Interpret binary .xcresult files and print summary in different formats: txt,
xml, html or colored cli output.
Expand Down
47 changes: 37 additions & 10 deletions Sources/xcresultparser/JunitXML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,12 @@ public struct JunitXML: XmlSerializable {
for thisSummary in testPlanRunSummaries {
for thisTestableSummary in thisSummary.testableSummaries {
for group in thisTestableSummary.tests {
for testsuite in createTestSuite(group, failureSummaries: failureSummaries) {
let groupTestsuites = createTestSuite(
group,
failureSummaries: failureSummaries,
configurationName: thisSummary.name ?? "Unnamed configuration"
)
for testsuite in groupTestsuites {
testsuites.addChild(testsuite)
}
}
Expand Down Expand Up @@ -184,26 +189,40 @@ public struct JunitXML: XmlSerializable {
private func createTestSuite(
_ group: ActionTestSummaryGroup,
failureSummaries: [TestFailureIssueSummary],
configurationName: String,
testDirectory: String = ""
) -> [XMLElement] {
guard group.identifierString.hasSuffix(".xctest") || group.subtestGroups.isEmpty else {
return group.subtestGroups.reduce([XMLElement]()) { rslt, subGroup in
return group.subtestGroups.reduce([XMLElement]()) {
rslt,
subGroup in
return rslt + createTestSuite(
subGroup, failureSummaries: failureSummaries, testDirectory: subGroup.identifierString
subGroup,
failureSummaries: failureSummaries,
configurationName: configurationName,
testDirectory: subGroup.identifierString
)
}
}
if group.subtestGroups.isEmpty {
return [
createTestSuiteFinally(
group, tests: group.subtests, failureSummaries: failureSummaries, testDirectory: testDirectory
group,
tests: group.subtests,
failureSummaries: failureSummaries,
testDirectory: testDirectory,
configurationName: configurationName
)
]
} else {
if testReportFormat == .sonar {
var nodes = [XMLElement]()
for subGroup in group.subtestGroups {
let node = subGroup.sonarFileXML(projectRoot: projectRoot, relativePathNames: relativePathNames)
let node = subGroup.sonarFileXML(
projectRoot: projectRoot,
configurationName: configurationName,
relativePathNames: relativePathNames
)
let testcases = createTestCases(
for: subGroup.nameString, tests: subGroup.subtests, failureSummaries: failureSummaries
)
Expand All @@ -228,11 +247,15 @@ public struct JunitXML: XmlSerializable {
_ group: ActionTestSummaryGroup,
tests: [ActionTestMetadata],
failureSummaries: [TestFailureIssueSummary],
testDirectory: String = ""
testDirectory: String = "",
configurationName: String
) -> XMLElement {
let node = testReportFormat == .sonar ?
group.sonarFileXML(projectRoot: projectRoot, relativePathNames: relativePathNames) :
group.testSuiteXML(numFormatter: numFormatter)
group.sonarFileXML(
projectRoot: projectRoot,
configurationName: configurationName,
relativePathNames: relativePathNames
) : group.testSuiteXML(numFormatter: numFormatter)

for thisTest in tests {
let testcase = thisTest.xmlNode(
Expand Down Expand Up @@ -353,9 +376,13 @@ private extension ActionTestSummaryGroup {
return testsuite
}

func sonarFileXML(projectRoot: URL?, relativePathNames: Bool = true) -> XMLElement {
func sonarFileXML(projectRoot: URL?, configurationName: String, relativePathNames: Bool = true) -> XMLElement {
let testsuite = XMLElement(name: "file")
testsuite.addAttribute(name: "path", stringValue: classPath(in: projectRoot, relativePathNames: relativePathNames))
testsuite.addAttribute(
name: "path",
stringValue: classPath(in: projectRoot, relativePathNames: relativePathNames)
)
testsuite.addAttribute(name: "configuration", stringValue: configurationName)
return testsuite
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<testExecutions version="1">
<file path="XcresultparserTests">
<file path="XcresultparserTests" configuration="Test Scheme Action">
<testCase name="testCLIResultFormatter()" duration="307"/>
<testCase name="testCoverageConverter()" duration="2818">
<failure message="short">failed - Unable to create CoverageConverter from /Users/fhaeser/Library/Developer/Xcode/DerivedData/xcresultparser-ebyquorsyljyyuchjpndxzpxmxvo/Build/Products/Debug/XcresultparserTests.xctest/Contents/Resources/Xcresultparser_XcresultparserTests.bundle/Contents/Resources/test.xcresult (/Users/fhaeser/code/xcresultparser/Tests/XcresultparserTests/XcresultparserTests.swift:108)</failure>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<testExecutions version="1">
<file path="/Users/actual/project/Tests/XcresultparserTests.swift">
<file path="/Users/actual/project/Tests/XcresultparserTests.swift" configuration="Test Scheme Action">
<testCase name="testCLIResultFormatter()" duration="307"/>
<testCase name="testCoverageConverter()" duration="2818">
<failure message="short">failed - Unable to create CoverageConverter from /Users/fhaeser/Library/Developer/Xcode/DerivedData/xcresultparser-ebyquorsyljyyuchjpndxzpxmxvo/Build/Products/Debug/XcresultparserTests.xctest/Contents/Resources/Xcresultparser_XcresultparserTests.bundle/Contents/Resources/test.xcresult (/Users/fhaeser/code/xcresultparser/Tests/XcresultparserTests/XcresultparserTests.swift:108)</failure>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<testExecutions version="1">
<file path="Tests/XcresultparserTests.swift">
<file path="Tests/XcresultparserTests.swift" configuration="Test Scheme Action">
<testCase name="testCLIResultFormatter()" duration="307"/>
<testCase name="testCoverageConverter()" duration="2818">
<failure message="short">failed - Unable to create CoverageConverter from /Users/fhaeser/Library/Developer/Xcode/DerivedData/xcresultparser-ebyquorsyljyyuchjpndxzpxmxvo/Build/Products/Debug/XcresultparserTests.xctest/Contents/Resources/Xcresultparser_XcresultparserTests.bundle/Contents/Resources/test.xcresult (/Users/fhaeser/code/xcresultparser/Tests/XcresultparserTests/XcresultparserTests.swift:108)</failure>
Expand Down
156 changes: 156 additions & 0 deletions runSonar.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#!/bin/sh

usage()
{
echo ""
echo "NAME: $0"
echo ""
echo "SYNOPSIS:"
echo "$0 [-t <teamId>] [-n <productName>] [-p profileName]"
echo ""
echo "DESCRIPTION:"
echo " -- Script to create a SonarQube run and publish results"
echo ""
echo " The options are as follows:"
echo " -s | --sonarqube-host Host of the sonarqube instance."
echo " -p | --sonarqube-login-token The credentials to access the sonarqube instance."
echo " -k | --sonarqube-project-key The key which identifies the project in sonar."
echo " -n | --sonarqube-project-name The key of the project in sonar."
echo " -v | --app-version The version of the project."
echo " -r | --path-to-xcresult The xcresult bundle with data about tests and coverage."
echo " -h | --help This help."
echo ""
}

sourcesPath=Sources
testsPath=Tests

while [ "$1" != "" ]; do
case $1 in
-s | --sonarqube-host ) shift
sonarqube_host_url="$1"
;;
-p | --sonarqube-login-token ) shift
sonarqube_login_token="$1"
;;
-k | --sonarqube-project-key ) shift
sonarqube_project_key="$1"
;;
-n | --sonarqube-project-name ) shift
sonarqube_project_name="$1"
;;
-o | --sonarqube-organization ) shift
sonarqube_organization="$1"
;;
-v | --app-version ) shift
app_project_version="$1"
;;
-r | --path-to-xcresult ) shift
xcresultPath="$1"
;;
-h | --help ) usage
exit
;;
esac
shift
done

root_path=`git rev-parse --show-toplevel`
if [ ! -z "$root_path" ]
then
cd "$root_path"
else
root_path="$(pwd)"
fi


sonarPath=$(which sonar-scanner)
if [ ! -x "$sonarPath" ]
then
sonarPath="/opt/homebrew/bin/sonar-scanner"
if [ ! -x "$sonarPath" ]
then
brewPath=$(which brew)
if [ ! -x "$brewPath" ]
then
brewPath="/usr/local/bin/brew"
if [ ! -x "$brewPath" ]
then
echo -e "Need brew to install the 'sonar-scanner' binary"
exit 1
fi
fi
echo "Installing 'sonar-scanner' binary"
"$brewPath" update && "$brewPath" install sonar-scanner
sonarPath=$(which sonar-scanner)
fi
fi

if [ ! -x "$sonarPath" ]
then
echo -e "The 'sonar-scanner' binary is not executble at: $sonarPath"
exit 1
fi

# We can either provide 'xcresultparser' as tool in the project's repository,
resultparserPath=xcresultparser
if [ ! -x "$resultparserPath" ]
then
# ...or we have installed it for all jobs on the agent:
resultparserPath=$(which xcresultparser)
if [ ! -x "$resultparserPath" ]
then
resultparserPath="/opt/homebrew/bin/xcresultparser"
fi
fi

echo "-------------------\nStarting sonar scan for app_project_version (Build): $app_project_version\n-------------------"

if [ ! -z "$xcresultPath" -a -d "$xcresultPath" -a -x "$resultparserPath" ]
then
echo "Convert xcresult to sonarqube compatible coverage xml. Path to xcresult file: $xcresultPath"
"$resultparserPath" -cq -o xml "$xcresultPath" > sonarqube-coverage.xml

echo "Convert xcresult to sonarqube compatible Junit xml."
"$resultparserPath" -q -o xml -p "$root_path" "$xcresultPath" > sonarqube-testresults.xml
fi

if [ -s sonarqube-coverage.xml ]
then
echo "Run sonar scanner with coverage now for (sonarqube_project_name) ${sonarqube_project_name} with $sonarPath"
# "$sonarPath" -X // with DEBUG messages
"$sonarPath" \
-Dsonar.host.url="${sonarqube_host_url}" \
-Dsonar.token="${sonarqube_login_token}" \
-Dsonar.projectKey="${sonarqube_project_key}" \
-Dsonar.projectName="${sonarqube_project_name}" \
-Dsonar.projectVersion="${app_project_version}" \
-Dsonar.organization="${sonarqube_organization}" \
-Dsonar.sources="$(pwd)/${sourcesPath}" \
-Dsonar.exclusions=**/*.html \
-Dsonar.coverageReportPaths="sonarqube-coverage.xml" \
-Dsonar.testExecutionReportPaths="sonarqube-testresults.xml" \
-Dsonar.tests="${testsPath}" \
-Dsonar.c.file.suffixes=- \
-Dsonar.cpp.file.suffixes=- \
-Dsonar.objc.file.suffixes=-
else
echo "Run sonar scanner without coverage for (sonarqube_project_name) ${sonarqube_project_name} with $sonarPath"
"$sonarPath" \
-Dsonar.host.url="${sonarqube_host_url}" \
-Dsonar.token="${sonarqube_login_token}" \
-Dsonar.projectKey="${sonarqube_project_key}" \
-Dsonar.projectName="${sonarqube_project_name}" \
-Dsonar.projectVersion="${app_project_version}" \
-Dsonar.organization="${sonarqube_organization}" \
-Dsonar.sources="$sourcesPath" \
-Dsonar.exclusions=**/*.html
fi
if [ $? -ne 0 ]
then
echo "-------------------\nSonar scan completed with error!\n-------------------"
exit 1
else
echo "-------------------\nSonar scan completed!\n-------------------"
exit 0
fi
75 changes: 75 additions & 0 deletions testAndRunSonar.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/bin/sh

usage()
{
echo ""
echo "NAME: $0"
echo ""
echo "SYNOPSIS:"
echo "$0 [-t <access token>]"
echo ""
echo "DESCRIPTION:"
echo " -- Run the tests and send the results to sonar."
echo " Here we define the project specific variables, which are required for the runSonar.sh script."
echo " Note that one single value, which is required is not hard coded into this file for obvious reasons:"
echo " The sonar API key needs to be sent to this script as parameter."
echo ""
echo " The options are as follows:"
echo " -t | --sonarqube-login-token Access token to connect to the sonar server."
echo " -h | --help This help"
echo ""
}

## Default values for this app, so I can invoke this script with only one parameter for the token, which shall not be stored in the public repository.
sonarqube_host_url="https://sonarcloud.io"
sonarqube_project_key="a7ex_xcresultparser"
sonarqube_project_name="xcresultparser"
sonarqube_organization="a7ex"
skip_build=false

while [ "$1" != "" ]; do
case $1 in
-t | --sonarqube-login-token ) shift
sonarqube_login_token="$1"
;;
-s | --skip-build ) skip_build=true
;;
-h | --help ) usage
exit
;;
esac
shift
done

if [ -z "$sonarqube_login_token" ]
then
echo "Error: Please provide the api key (access token) for the sonar account!"
exit 1
fi

# build the project for M1 and Intel:

path_to_xcresults="$(pwd)/product/xcresultparser.xcresult"
if [ -d "$path_to_xcresults" ]; then
if [ "$skip_build" != true ]; then
rm -r "$path_to_xcresults"
fi
fi

if [ ! -d "$path_to_xcresults" ]; then
/usr/bin/xcrun xcodebuild clean test -workspace .swiftpm/xcode/package.xcworkspace -scheme xcresultparser -destination "platform=macOS" -resultBundlePath "$path_to_xcresults"
fi

app_project_version=$(grep 'marketingVersion =' CommandlineTool/main.swift | cut -d "=" -f2 | xargs)

if [ -d "$path_to_xcresults" ]
then
sh runSonar.sh \
-s $sonarqube_host_url \
-p $sonarqube_login_token \
-k $sonarqube_project_key \
-n "$sonarqube_project_name" \
-o "$sonarqube_organization" \
-v "$app_project_version" \
-r "$path_to_xcresults"
fi

0 comments on commit 344eeaf

Please sign in to comment.