├── 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 | [![Release](https://img.shields.io/github/release/asadmansr/android-test-report-action.svg)](https://github.com/asadmansr/android-test-report-action/releases) 4 | [![Marketplace](https://img.shields.io/badge/GitHub-Marketplace-orange.svg)](https://github.com/marketplace/actions/android-test-report-action) 5 | 6 | GitHub Action that prints Android test xml reports. 7 | 8 | ![action](./images/promo.png) 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 | ![action](./images/output.png) 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 --------------------------------------------------------------------------------