diff --git a/.gitignore b/.gitignore index a19bdb3..f0efd84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .bitrise* .gows.user.yml +.DS_Store +.envstore.yml +_temp/ \ No newline at end of file diff --git a/README.md b/README.md index 93692d1..ba63ebe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ # JaCoCo Report Parser -Fill description with what step does, services and tools used, configuration info, inputs, and troubleshooting. +This step parses a JaCoCo generated XML report in the `jacoco_report_path` and outputs the coverage percentages in a String format to environment / output variables to be used in other steps. + +It basically gets the top-level counter information for each coverage type and calculates de percentage by doing `covered` / `covered` + `missed`. + +Input: +`jacoco_report_path` receives the reported file path (it must be an xml) + +Outputs: +`JACOCO_INSTRUCTION_COVERAGE` contains the instruction coverage percentage calculated from the report +`JACOCO_BRANCH_COVERAGE` contains the branch coverage percentage calculated from the report +`JACOCO_LINE_COVERAGE` contains the line coverage percentage calculated from the report +`JACOCO_COMPLEXITY_COVERAGE` contains the complexity coverage percentage calculated from the report +`JACOCO_METHOD_COVERAGE` contains the method coverage percentage calculated from the report +`JACOCO_CLASS_COVERAGE` contains the class coverage percentage calculated from the report ## How to use this Step diff --git a/bitrise.yml b/bitrise.yml index d1b9f23..7d99192 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3,23 +3,15 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git app: envs: - # An example secret param, define it (A_SECRET_PARAM) in .bitrise.secrets.yml - - A_SECRET_PARAM: $A_SECRET_PARAM # If you want to share this step into a StepLib - BITRISE_STEP_ID: jacoco-report-parser - - BITRISE_STEP_VERSION: "0.0.1" + - BITRISE_STEP_VERSION: "1.0.0" - BITRISE_STEP_GIT_CLONE_URL: https://github.com/thomashorta/bitrise-step-jacoco-report-parser.git - MY_STEPLIB_REPO_FORK_GIT_URL: $MY_STEPLIB_REPO_FORK_GIT_URL workflows: test: steps: - - script: - inputs: - - content: | - #!/bin/bash - echo "Just an example 'secrets' print." - echo "The value of 'A_SECRET_PARAM' is: $A_SECRET_PARAM" - change-workdir: title: Switch working dir to test / _tmp dir description: |- @@ -34,18 +26,23 @@ workflows: - path::./: title: Step Test description: |- - The example input has a default value, - you can overwrite it if you want to, just like we did below, - but the step would use the default value specified in the `step.yml` - file if you would not specify another value. + Test the Jacoco Report Parser Step. + + The sample jacoco.xml used here is from the sample repository + at https://github.com/thomashorta/newsapi_sample_android run_if: true inputs: - - example_step_input: Example Step Input's value + - jacoco_report_path: $BITRISE_STEP_SOURCE_DIR/reports/jacoco.xml - script: inputs: - content: | #!/bin/bash - echo "This output was generated by the Step (EXAMPLE_STEP_OUTPUT): $EXAMPLE_STEP_OUTPUT" + echo "(JACOCO_INSTRUCTION_COVERAGE): $JACOCO_INSTRUCTION_COVERAGE" + echo "(JACOCO_BRANCH_COVERAGE): $JACOCO_BRANCH_COVERAGE" + echo "(JACOCO_LINE_COVERAGE): $JACOCO_LINE_COVERAGE" + echo "(JACOCO_COMPLEXITY_COVERAGE): $JACOCO_COMPLEXITY_COVERAGE" + echo "(JACOCO_METHOD_COVERAGE): $JACOCO_METHOD_COVERAGE" + echo "(JACOCO_CLASS_COVERAGE): $JACOCO_CLASS_COVERAGE" # ---------------------------------------------------------------- diff --git a/main.go b/main.go index 5da5e2b..3b5ad6f 100644 --- a/main.go +++ b/main.go @@ -1,31 +1,127 @@ package main import ( + "encoding/xml" "fmt" + "io/ioutil" "os" "os/exec" + "strings" ) +// Bitrise XML structs +type Report struct { + XMLName xml.Name `xml:"report"` + Name string `xml:"name,attr"` + Counters []Counter `xml:"counter"` +} + +type Counter struct { + XMLName xml.Name `xml:"counter"` + Type string `xml:"type,attr"` + Missed int `xml:"missed,attr"` + Covered int `xml:"covered,attr"` +} + +// JaCoCo counter types +const Instruction = "INSTRUCTION" +const Branch = "BRANCH" +const Line = "LINE" +const Complexity = "COMPLEXITY" +const Method = "METHOD" +const Class = "CLASS" + +// Step outputs +const OutputInstructionCoverage = "JACOCO_INSTRUCTION_COVERAGE" +const OutputBranchCoverage = "JACOCO_BRANCH_COVERAGE" +const OutputLineCoverage = "JACOCO_LINE_COVERAGE" +const OutputComplexityCoverage = "JACOCO_COMPLEXITY_COVERAGE" +const OutputMethodCoverage = "JACOCO_METHOD_COVERAGE" +const OutputClassCoverage = "JACOCO_CLASS_COVERAGE" + +var OutputKeys = [...]string{ + OutputInstructionCoverage, + OutputBranchCoverage, + OutputLineCoverage, + OutputComplexityCoverage, + OutputMethodCoverage, + OutputClassCoverage, +} + func main() { - fmt.Println("This is the value specified for the input 'example_step_input':", os.Getenv("example_step_input")) - - // - // --- Step Outputs: Export Environment Variables for other Steps: - // You can export Environment Variables for other Steps with - // envman, which is automatically installed by `bitrise setup`. - // A very simple example: - cmdLog, err := exec.Command("bitrise", "envman", "add", "--key", "EXAMPLE_STEP_OUTPUT", "--value", "the value you want to share").CombinedOutput() + reportPath := os.Getenv("jacoco_report_path") + reportFormat := reportPath[strings.LastIndex(reportPath, ".")+1:] + + if reportFormat != "xml" { + fmt.Println("Only xml is supported at the moment.") + os.Exit(1) + } + + // Open xml file + xmlFile, err := os.Open(reportPath) + if err != nil { - fmt.Printf("Failed to expose output with envman, error: %#v | output: %s", err, cmdLog) + fmt.Printf("Failed to open specified file %s\n", reportPath) os.Exit(1) } - // You can find more usage examples on envman's GitHub page - // at: https://github.com/bitrise-io/envman - - // - // --- Exit codes: - // The exit code of your Step is very important. If you return - // with a 0 exit code `bitrise` will register your Step as "successful". - // Any non zero exit code will be registered as "failed" by `bitrise`. + + fmt.Printf("Opened %s\n", reportPath) + + defer xmlFile.Close() + + fmt.Println("Parsing the report file") + + byteValue, _ := ioutil.ReadAll(xmlFile) + + var report Report + + xml.Unmarshal(byteValue, &report) + + coverageMap := map[string]string{} + for _, counter := range report.Counters { + coverage := CalcCoverage(counter.Missed, counter.Covered) + switch counter.Type { + case Instruction: + coverageMap[OutputInstructionCoverage] = coverage + case Branch: + coverageMap[OutputBranchCoverage] = coverage + case Line: + coverageMap[OutputLineCoverage] = coverage + case Complexity: + coverageMap[OutputComplexityCoverage] = coverage + case Method: + coverageMap[OutputMethodCoverage] = coverage + case Class: + coverageMap[OutputClassCoverage] = coverage + } + } + + // output all coverages or N/A for the ones not available + for _, key := range OutputKeys { + val, ok := coverageMap[key] + if !ok { + val = "N/A" + } + SetOutput(key, val) + } + + fmt.Println("JaCoCo report file parsed and coverage exported") os.Exit(0) } + +// returns the formatted coverage string, e.g.: 4.50% +func CalcCoverage(missed int, covered int) string { + total := missed + covered + coverage := 100.0 * float64(covered) / float64(total) + return fmt.Sprintf("%.2f%%", coverage) +} + +// outputs the desired text to the environment variable +func SetOutput(key string, value string) { + fmt.Printf("Outputing key %s with value %s\n", key, value) + cmdLog, err := exec.Command("bitrise", "envman", "add", "--key", key, "--value", value).CombinedOutput() + if err != nil { + fmt.Printf("Failed to expose output with envman, error: %#v | output: %s\n", err, cmdLog) + os.Exit(1) + } +} diff --git a/reports/jacoco.xml b/reports/jacoco.xml new file mode 100644 index 0000000..6e220df --- /dev/null +++ b/reports/jacoco.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/step.yml b/step.yml index abcec15..11a5872 100644 --- a/step.yml +++ b/step.yml @@ -10,9 +10,24 @@ title: |- JaCoCo Report Parser summary: | - Parses a JaCoCo generated report and outputs the code coverage percentages to be used by other steps. + Parse a JaCoCo generated report and output the code coverage percentages to be used by other steps. description: | - Fill description with what step does, services and tools used, configuration info, inputs, and troubleshooting. + This step parses a JaCoCo generated XML report in the `jacoco_report_path` and outputs the coverage + percentages in a String format to environment / output variables to be used in other steps. + + It basically gets the top-level counter information for each coverage type and calculates de percentage + by doing `covered` / `covered` + `missed`. + + Input: + `jacoco_report_path` receives the reported file path (it must be an xml) + + Outputs: + `JACOCO_INSTRUCTION_COVERAGE` contains the instruction coverage percentage calculated from the report + `JACOCO_BRANCH_COVERAGE` contains the branch coverage percentage calculated from the report + `JACOCO_LINE_COVERAGE` contains the line coverage percentage calculated from the report + `JACOCO_COMPLEXITY_COVERAGE` contains the complexity coverage percentage calculated from the report + `JACOCO_METHOD_COVERAGE` contains the method coverage percentage calculated from the report + `JACOCO_CLASS_COVERAGE` contains the class coverage percentage calculated from the report website: https://github.com/thomashorta/bitrise-step-jacoco-report-parser source_code_url: https://github.com/thomashorta/bitrise-step-jacoco-report-parser support_url: https://github.com/thomashorta/bitrise-step-jacoco-report-parser/issues @@ -29,25 +44,24 @@ host_os_tags: # You can find more information about project type tags in the Step Development Guideline: # https://github.com/bitrise-io/bitrise/blob/master/_docs/step-development-guideline.md # -# project_type_tags: -# - ios -# - macos -# - android -# - xamarin -# - react-native -# - cordova -# - ionic +project_type_tags: + - android + - xamarin + - react-native + - cordova + - ionic + - flutter # Type tags are used for categorizing steps, for easier step discovery in Step Libraries. # You can find more information about type tags in the Step Development Guideline: # https://github.com/bitrise-io/bitrise/blob/master/_docs/step-development-guideline.md type_tags: - test + - utility -is_requires_admin_user: true +is_requires_admin_user: false is_always_run: false is_skippable: false -run_if: "" deps: brew: @@ -64,24 +78,74 @@ toolkit: inputs: - - example_step_input: Default Value - you can leave this empty if you want to + - jacoco_report_path: $BITRISE_SOURCE_DIR/build/reports/jacoco.xml opts: - title: "Example Step Input" - summary: Summary. No more than 2-3 sentences. + is_required: true + title: "The JaCoCo report file path" + summary: Exact path location to the JaCoCo report file (usually points to a jacoco.xml file). description: | - Description of this input. + The report will be parsed from this input to run this step. - Can be Markdown formatted text. - is_expand: true - is_required: true - value_options: [] + Default value: `$BITRISE_SOURCE_DIR/build/reports/jacoco.xml` outputs: - - EXAMPLE_STEP_OUTPUT: + - JACOCO_INSTRUCTION_COVERAGE: + opts: + title: "Instruction coverage percentage" + summary: Total instruction coverage percentage parsed from the report. + description: | + This is the total instruciton coverage percentage parsed from the report + taking into consideration the report summary for the whole run. + + This is calculated by taking the `covered` amount divided by the `covered` + `missed` amount. + It returns "N/A" if the coverage information was not found. + - JACOCO_BRANCH_COVERAGE: + opts: + title: "Branch coverage percentage" + summary: Total branch coverage percentage parsed from the report. + description: | + This is the total branch coverage percentage parsed from the report + taking into consideration the report summary for the whole run. + + This is calculated by taking the `covered` amount divided by the `covered` + `missed` amount. + It returns "N/A" if the coverage information was not found. + - JACOCO_LINE_COVERAGE: + opts: + title: "Line coverage percentage" + summary: Total line coverage percentage parsed from the report. + description: | + This is the total line coverage percentage parsed from the report + taking into consideration the report summary for the whole run. + + This is calculated by taking the `covered` amount divided by the `covered` + `missed` amount. + It returns "N/A" if the coverage information was not found. + - JACOCO_COMPLEXITY_COVERAGE: + opts: + title: "Complexity coverage percentage" + summary: Total complexity coverage percentage parsed from the report. + description: | + This is the total complexity coverage percentage parsed from the report + taking into consideration the report summary for the whole run. + + This is calculated by taking the `covered` amount divided by the `covered` + `missed` amount. + It returns "N/A" if the coverage information was not found. + - JACOCO_METHOD_COVERAGE: + opts: + title: "Method coverage percentage" + summary: Total method coverage percentage parsed from the report. + description: | + This is the total method coverage percentage parsed from the report + taking into consideration the report summary for the whole run. + + This is calculated by taking the `covered` amount divided by the `covered` + `missed` amount. + It returns "N/A" if the coverage information was not found. + - JACOCO_CLASS_COVERAGE: opts: - title: "Example Step Output" - summary: Summary. No more than 2-3 sentences. + title: "Class coverage percentage" + summary: Total class coverage percentage parsed from the report. description: | - Description of this output. + This is the total class coverage percentage parsed from the report + taking into consideration the report summary for the whole run. - Can be Markdown formatted text. + This is calculated by taking the `covered` amount divided by the `covered` + `missed` amount. + It returns "N/A" if the coverage information was not found.