├── Dockerfile
├── README.md
├── action.yml
├── entrypoint.sh
├── extractReport.py
└── images
├── output.png
└── promo.png
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04
2 |
3 | RUN apt-get update && apt-get install -qy python
4 |
5 | COPY entrypoint.sh /entrypoint.sh
6 | COPY extractReport.py /usr/bin/extractReport.py
7 |
8 | ENTRYPOINT ["/entrypoint.sh"]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android Test Report Action
2 |
3 | [](https://github.com/asadmansr/android-test-report-action/releases)
4 | [](https://github.com/marketplace/actions/android-test-report-action)
5 |
6 | GitHub Action that prints Android test xml reports.
7 |
8 | 
9 |
10 |
11 |
12 | ## Getting Started
13 |
14 | Add the following action to your GitHub Actions workflow.
15 |
16 | ```yml
17 | - name: Android Test Report
18 | uses: asadmansr/android-test-report-action@v1.2.0
19 | ```
20 |
21 |
22 |
23 | ## Usage
24 |
25 | ### Basic
26 |
27 | Once the test command has been executed, the Android Test Report action will parse all of the XML reports and output the results in a structured way.
28 |
29 | ```yml
30 | name: Android CI
31 | on: [push]
32 |
33 | jobs:
34 | test:
35 | runs-on: ubuntu-latest
36 | steps:
37 | - uses: actions/checkout@v1
38 |
39 | - name: set up JDK 1.8
40 | uses: actions/setup-java@v1
41 | with:
42 | java-version: 1.8
43 |
44 | # Execute unit tests
45 | - name: Unit Test
46 | run: ./gradlew testDebugUnitTest
47 |
48 | - name: Android Test Report
49 | uses: asadmansr/android-test-report-action@v1.2.0
50 | if: ${{ always() }} # IMPORTANT: run Android Test Report regardless
51 | ```
52 | #### Note
53 | The workflow must contain the unit test job prior to running the Android Test Report action. **The action will automatically pass or fail the job depending on the test results.**
54 |
55 |
56 |
57 | ### Alternate
58 |
59 | If the basic usage fails to meet your requirement (running on MacOS machine or anything else), split the test and report into two jobs. The test job will run the tests and save the reports as artifacts. The report job will use the Android Test Report action to parse and print the results. Consider the following example below.
60 |
61 | ```yml
62 | jobs:
63 | test:
64 | runs-on: macos-latest # or any other machine
65 | steps:
66 | ...
67 | - name: Unit Test
68 | run: ./gradlew testDebugUnitTest
69 |
70 | - name: Upload Test Reports Folder
71 | uses: actions/upload-artifact@v2
72 | if: ${{ always() }} # IMPORTANT: Upload reports regardless of status
73 | with:
74 | name: reports
75 | path: app/build/test-results # path to where the xml test results are stored
76 |
77 | report:
78 | runs-on: ubuntu-latest
79 | needs: test # The report job will run after test job
80 | if: ${{ always() }} # IMPORTANT: Execute report job regardless of status
81 | steps:
82 | - name: Download Test Reports Folder
83 | uses: actions/download-artifact@v2
84 | with:
85 | name: reports
86 |
87 | - name: Android Test Report
88 | uses: asadmansr/android-test-report-action@v1.2.0
89 | ```
90 |
91 |
92 |
93 | ## Output
94 |
95 | 
96 |
97 |
98 |
99 | ## Sample Project
100 |
101 | To learn how to use this action in an Android application, check out the following example repository:
102 | https://github.com/asadmansr/android-test-report-action-example
103 |
104 | - master branch: Passed pipeline as all the tests passed
105 | - failed-pipeline branch: Failed pipeline as some of the tests failed
106 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Android Test Report Action'
2 | description: 'GitHub Action that prints Android test reports'
3 | author: 'Asad Mansoor'
4 |
5 | runs:
6 | using: 'docker'
7 | image: 'Dockerfile'
8 |
9 | branding:
10 | icon: 'file-text'
11 | color: 'red'
--------------------------------------------------------------------------------
/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | status_code=0
4 | num_files=0
5 | log_file='extractReport_status.log'
6 | status_message='Android Test Report Action executed successfully.'
7 |
8 | echo ''
9 | echo '------------------------------------------------'
10 | echo '--- Android Test Report Action ---'
11 | echo '------------------------------------------------'
12 | echo ''
13 | echo ''
14 |
15 | touch $log_file
16 |
17 | for i in `find . -name "TEST-*.xml" -type f`; do
18 | let num_files=num_files+1
19 | python /usr/bin/extractReport.py "$i"
20 | echo ''
21 | done
22 |
23 | if [ "$num_files" -eq "0" ]; then
24 | status_code=1
25 | status_message='No test reports found. Please verify the tests were executed successfully. Android Test Report Action failed the job.'
26 | fi
27 |
28 | if grep -q 'error' "$log_file"; then
29 | status_code=1
30 | status_message='There were failing tests. Android Test Report Action failed the job.'
31 | fi
32 |
33 | rm $log_file
34 |
35 | echo $status_message
36 | echo ''
37 | exit $status_code
38 |
--------------------------------------------------------------------------------
/extractReport.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import xml.etree.ElementTree as ET
3 |
4 | def parseXML(xmlfile):
5 | hasSeenFailure = False
6 | message = ""
7 | tree = ET.parse(xmlfile)
8 | root = tree.getroot()
9 | attributes = root.attrib
10 |
11 | for k,v in attributes.items():
12 | cs = len(k)
13 | sp = 16-cs
14 | print(k.capitalize() + " "*sp + v)
15 | if ((k == "failures") and (int(v) > 0)) or ((k == "errors") and (int(v) > 0)):
16 | f = open("extractReport_status.log", "w")
17 | f.write("error")
18 | f.close()
19 |
20 | for elem in root:
21 | if (elem.tag == "testcase"):
22 | elem_attrib = elem.attrib
23 | try:
24 | failure_message = (elem.find('failure').attrib)['message']
25 | hasSeenFailure = True
26 | message += "\n"
27 | message += printFormatter("testcase", elem_attrib['name']) + "\n"
28 | message += printFormatter("message", failure_message) + "\n"
29 | message += printFormatter("time", elem_attrib['time']) + "\n"
30 | except AttributeError:
31 | pass
32 |
33 | if (hasSeenFailure):
34 | print("")
35 | print("Failed Test Cases:")
36 | print("------------------")
37 | print(message)
38 | print("")
39 |
40 | def printFormatter(key, msg):
41 | keyLen = len(key)
42 | space = 12-keyLen
43 | return(key.capitalize() + " "*space + msg)
44 |
45 | def main():
46 | path = sys.argv[1]
47 | print(path + "\n")
48 | parseXML(path)
49 |
50 | if __name__ == "__main__":
51 | main()
--------------------------------------------------------------------------------
/images/output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asadmansr/android-test-report-action/b4f13494c5a25bb3045ff911b3f1479d9da27619/images/output.png
--------------------------------------------------------------------------------
/images/promo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asadmansr/android-test-report-action/b4f13494c5a25bb3045ff911b3f1479d9da27619/images/promo.png
--------------------------------------------------------------------------------