mirror of
https://github.com/game-ci/unity-test-runner.git
synced 2026-02-04 10:59:06 +08:00
Compare commits
100 Commits
v1.1
...
v2.0-alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
694c315276 | ||
|
|
fffbf11b2a | ||
|
|
a195213baa | ||
|
|
8033066291 | ||
|
|
215f660c06 | ||
|
|
43d90c252f | ||
|
|
345f4c64bd | ||
|
|
d45ca4403f | ||
|
|
7dd059111b | ||
|
|
5edc17bb8a | ||
|
|
23b6b8f5f3 | ||
|
|
6e30d4827d | ||
|
|
50e6471ee4 | ||
|
|
6a039cc828 | ||
|
|
2656f4e108 | ||
|
|
e1be8325cd | ||
|
|
26807aaf05 | ||
|
|
a9d07b742d | ||
|
|
0c3e710069 | ||
|
|
a067c3d5ab | ||
|
|
3fca186a7b | ||
|
|
a8b9742ecd | ||
|
|
31cd755121 | ||
|
|
679222c549 | ||
|
|
29899d84e8 | ||
|
|
7d26e264b9 | ||
|
|
bac0f97d2f | ||
|
|
c4e44617e2 | ||
|
|
25e14f52b8 | ||
|
|
3a40ffcf5f | ||
|
|
3277792c11 | ||
|
|
ac2e8a74d0 | ||
|
|
8107d46ab7 | ||
|
|
a4e8475a2f | ||
|
|
f02586c1c9 | ||
|
|
31ae95179d | ||
|
|
cd86c7302b | ||
|
|
21831e61ad | ||
|
|
7c1cba9a39 | ||
|
|
29e179f50b | ||
|
|
2fa69494ad | ||
|
|
4e717f6c82 | ||
|
|
173716a8a9 | ||
|
|
3c915726f4 | ||
|
|
5cfdb7ee17 | ||
|
|
51648a5093 | ||
|
|
2297daa673 | ||
|
|
7711f454ba | ||
|
|
b518514f36 | ||
|
|
7e50ed34dd | ||
|
|
c85e37ca1f | ||
|
|
58f580c801 | ||
|
|
4ba71aefa9 | ||
|
|
bed834cb73 | ||
|
|
eb51009c09 | ||
|
|
da74d9ff8c | ||
|
|
c8a0e2865a | ||
|
|
627cf8f914 | ||
|
|
e9b4db003e | ||
|
|
45a1728dfb | ||
|
|
f854b54cb0 | ||
|
|
6948da8f6a | ||
|
|
922dcd8b1a | ||
|
|
c8909e4d5e | ||
|
|
060d1856e7 | ||
|
|
6bacc4484e | ||
|
|
892b3b8279 | ||
|
|
00afc45f14 | ||
|
|
868c383d7f | ||
|
|
54d8d414ea | ||
|
|
057deedeb0 | ||
|
|
d64e5a8b19 | ||
|
|
711a3ee644 | ||
|
|
39da97ffa3 | ||
|
|
1473c8f431 | ||
|
|
c210544758 | ||
|
|
f863c717a4 | ||
|
|
5423e61ad7 | ||
|
|
377244a880 | ||
|
|
6f1c3ded0c | ||
|
|
b181c9cdbe | ||
|
|
d5a5335100 | ||
|
|
c937b9ed6c | ||
|
|
56b8c04f36 | ||
|
|
a18b837919 | ||
|
|
033a0990cb | ||
|
|
24fe88806a | ||
|
|
e2f2badc29 | ||
|
|
3ccd77fd44 | ||
|
|
686f633329 | ||
|
|
28154ea36e | ||
|
|
2ca91788af | ||
|
|
2d11601618 | ||
|
|
545600f75b | ||
|
|
d6c937fe37 | ||
|
|
d105f8c891 | ||
|
|
048c75a66e | ||
|
|
a08a398026 | ||
|
|
311b685893 | ||
|
|
3d7f5a853b |
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
max_line_length = 100
|
||||||
|
tab_width = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
max_line_length = off
|
||||||
|
|
||||||
|
[COMMIT_EDITMSG]
|
||||||
|
max_line_length = off
|
||||||
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
**/node_modules/**
|
||||||
|
**/action/**
|
||||||
22
.eslintrc.json
Normal file
22
.eslintrc.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true,
|
||||||
|
"jest": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"impliedStrict": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extends": ["airbnb", "plugin:unicorn/recommended", "prettier"],
|
||||||
|
"plugins": ["react", "jsx-a11y", "import", "prettier", "flowtype", "unicorn"],
|
||||||
|
"settings": { "react": { "version": "latest" } },
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"import/no-extraneous-dependencies": 0,
|
||||||
|
"no-underscore-dangle": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
9
.github/pull_request_template.md
vendored
Normal file
9
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#### Changes
|
||||||
|
|
||||||
|
- ...
|
||||||
|
|
||||||
|
#### Checklist
|
||||||
|
|
||||||
|
- [x] Read the contribution [guide](../CONTRIBUTING.md) and accept the [code](../CODE_OF_CONDUCT.md) of conduct
|
||||||
|
- [ ] Readme (updated or not needed)
|
||||||
|
- [ ] Tests (added, updated or not needed)
|
||||||
16
.github/workflows/cats.yml
vendored
Normal file
16
.github/workflows/cats.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: Cats 😺
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
aCatForCreatingThePullRequest:
|
||||||
|
name: A cat for your effort!
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: ruairidhwm/action-cats@1.0.1
|
||||||
|
with:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
251
.github/workflows/main.yml
vendored
251
.github/workflows/main.yml
vendored
@@ -1,132 +1,265 @@
|
|||||||
name: Actions 😎
|
name: Actions 😎
|
||||||
on:
|
on:
|
||||||
pull_request: {}
|
push: { branches: [main] }
|
||||||
push: { branches: [master] }
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\" Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\" Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
tests:
|
||||||
|
name: Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 12.x
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn lint
|
||||||
|
- run: yarn test
|
||||||
|
- run: yarn build || { echo "build command should always succeed" ; exit 61; }
|
||||||
|
- run: yarn build --quiet && git diff --quiet action || { echo "ERROR - index.js is different from repository version. Forgot to run `yarn build`?" ; exit 62; }
|
||||||
|
|
||||||
|
testAllModesLikeInTheReadme:
|
||||||
|
name: Test in ${{ matrix.testMode }} on version ${{ matrix.unityVersion }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
projectPath:
|
||||||
|
- unity-project-with-correct-tests
|
||||||
|
unityVersion:
|
||||||
|
- 2019.2.11f1
|
||||||
|
testMode:
|
||||||
|
- playmode
|
||||||
|
- editmode
|
||||||
|
steps:
|
||||||
|
###########################
|
||||||
|
# Checkout #
|
||||||
|
###########################
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Cache #
|
||||||
|
###########################
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-
|
||||||
|
- uses: ./
|
||||||
|
id: tests
|
||||||
|
with:
|
||||||
|
projectPath: ${{ matrix.projectPath }}
|
||||||
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
|
testMode: ${{ matrix.testMode }}
|
||||||
|
artifactsPath: ${{ matrix.testMode }}-artifacts
|
||||||
|
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Test results for ${{ matrix.testMode }}
|
||||||
|
path: ${{ steps.tests.outputs.artifactsPath }}
|
||||||
|
retention-days: 14
|
||||||
|
|
||||||
testRunnerInAllModes:
|
testRunnerInAllModes:
|
||||||
name: Test all modes ✨
|
name: Test all modes ✨
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
projectPath:
|
||||||
|
- unity-project-with-correct-tests
|
||||||
|
unityVersion:
|
||||||
|
- 2019.2.11f1
|
||||||
steps:
|
steps:
|
||||||
# Checkout repository (required to test local actions)
|
###########################
|
||||||
- name: Checkout repository
|
# Checkout #
|
||||||
uses: actions/checkout@v1
|
###########################
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
# Activate license
|
###########################
|
||||||
- name: Activate Unity
|
# Cache #
|
||||||
uses: webbertakken/unity-activate@v1
|
###########################
|
||||||
env:
|
- uses: actions/cache@v1.1.0
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}-${{ matrix.targetPlatform }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-${{ matrix.projectPath }}-
|
||||||
|
Library-
|
||||||
|
|
||||||
# Configure test runner
|
# Configure test runner
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
id: allTests
|
id: allTests
|
||||||
uses: ./
|
uses: ./
|
||||||
env:
|
with:
|
||||||
TEST_MODE: all
|
projectPath: ${{ matrix.projectPath }}
|
||||||
PROJECT_PATH: unity-project-with-correct-tests
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
# Test implicit ARTIFACTS_PATH, by not setting it
|
testMode: all
|
||||||
|
# Test implicit artifactsPath, by not setting it
|
||||||
|
|
||||||
# Upload artifacts
|
# Upload artifacts
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Test results (all)
|
name: Test results (all)
|
||||||
path: ${{ steps.allTests.outputs.artifactsPath }}
|
path: ${{ steps.allTests.outputs.artifactsPath }}
|
||||||
|
retention-days: 14
|
||||||
|
|
||||||
testRunnerInEditMode:
|
testRunnerInEditMode:
|
||||||
name: Test edit mode 📝
|
name: Test edit mode 📝
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
unityVersion:
|
||||||
|
- 2019.2.11f1
|
||||||
|
projectPath:
|
||||||
|
- unity-project-with-correct-tests
|
||||||
steps:
|
steps:
|
||||||
# Checkout repository (required to test local actions)
|
###########################
|
||||||
- name: Checkout repository
|
# Checkout #
|
||||||
uses: actions/checkout@v1
|
###########################
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
# Activate license
|
###########################
|
||||||
- name: Activate Unity
|
# Cache #
|
||||||
uses: webbertakken/unity-activate@v1
|
###########################
|
||||||
env:
|
- uses: actions/cache@v1.1.0
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}-${{ matrix.targetPlatform }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-${{ matrix.projectPath }}-
|
||||||
|
Library-
|
||||||
|
|
||||||
# Configure test runner
|
# Configure test runner
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
id: editMode
|
id: editMode
|
||||||
uses: ./
|
uses: ./
|
||||||
env:
|
with:
|
||||||
TEST_MODE: editmode
|
projectPath: ${{ matrix.projectPath }}
|
||||||
PROJECT_PATH: unity-project-with-correct-tests
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
ARTIFACTS_PATH: artifacts/editmode
|
testMode: editmode
|
||||||
|
artifactsPath: artifacts/editmode
|
||||||
|
|
||||||
# Upload artifacts
|
# Upload artifacts
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Test results (edit mode)
|
name: Test results (edit mode)
|
||||||
path: ${{ steps.editMode.outputs.artifactsPath }}
|
path: ${{ steps.editMode.outputs.artifactsPath }}
|
||||||
|
retention-days: 14
|
||||||
|
|
||||||
testRunnerInPlayMode:
|
testRunnerInPlayMode:
|
||||||
name: Test play mode 📺
|
name: Test play mode 📺
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
projectPath:
|
||||||
|
- unity-project-with-correct-tests
|
||||||
|
unityVersion:
|
||||||
|
- 2019.2.11f1
|
||||||
steps:
|
steps:
|
||||||
# Checkout repository (required to test local actions)
|
###########################
|
||||||
- name: Checkout repository
|
# Checkout #
|
||||||
uses: actions/checkout@v1
|
###########################
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
# Activate license
|
###########################
|
||||||
- name: Activate Unity
|
# Cache #
|
||||||
uses: webbertakken/unity-activate@v1
|
###########################
|
||||||
env:
|
- uses: actions/cache@v1.1.0
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}-${{ matrix.targetPlatform }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-${{ matrix.projectPath }}-
|
||||||
|
Library-
|
||||||
|
|
||||||
# Configure test runner
|
# Configure test runner
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
id: playMode
|
id: playMode
|
||||||
uses: ./
|
uses: ./
|
||||||
env:
|
with:
|
||||||
TEST_MODE: playmode
|
projectPath: ${{ matrix.projectPath }}
|
||||||
PROJECT_PATH: unity-project-with-correct-tests
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
ARTIFACTS_PATH: artifacts/editmode
|
testMode: playmode
|
||||||
|
artifactsPath: artifacts/playmode
|
||||||
|
|
||||||
# Upload artifacts
|
# Upload artifacts
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Test results (play mode)
|
name: Test results (play mode)
|
||||||
path: ${{ steps.playMode.outputs.artifactsPath }}
|
path: ${{ steps.playMode.outputs.artifactsPath }}
|
||||||
|
retention-days: 14
|
||||||
|
|
||||||
testRunnerInEachModeSeparately:
|
testEachModeSequentially:
|
||||||
name: Test each mode sequentially 👩👩👧👦 # don't try this at home (it's much slower)
|
name: Test each mode sequentially 👩👩👧👦 # don't try this at home (it's much slower)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
unityVersion:
|
||||||
|
- 2019.2.11f1
|
||||||
|
projectPath:
|
||||||
|
- unity-project-with-correct-tests
|
||||||
steps:
|
steps:
|
||||||
# Checkout repository (required to test local actions)
|
###########################
|
||||||
- name: Checkout repository
|
# Checkout #
|
||||||
uses: actions/checkout@v1
|
###########################
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
# Activate license
|
###########################
|
||||||
- name: Activate Unity
|
# Cache #
|
||||||
uses: webbertakken/unity-activate@v1
|
###########################
|
||||||
env:
|
- uses: actions/cache@v1.1.0
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}-${{ matrix.targetPlatform }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-${{ matrix.projectPath }}-
|
||||||
|
Library-
|
||||||
|
|
||||||
# Configure first test runner
|
# Configure first test runner
|
||||||
- name: Tests in editmode 📝
|
- name: Tests in editmode 📝
|
||||||
uses: ./
|
uses: ./
|
||||||
env:
|
with:
|
||||||
TEST_MODE: editmode
|
projectPath: ${{ matrix.projectPath }}
|
||||||
PROJECT_PATH: unity-project-with-correct-tests
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
ARTIFACTS_PATH: artifacts/editmode
|
testMode: editmode
|
||||||
|
artifactsPath: artifacts/editmode
|
||||||
|
|
||||||
# Configure second test runner
|
# Configure second test runner
|
||||||
- name: Tests in playmode 📺
|
- name: Tests in playmode 📺
|
||||||
uses: ./
|
uses: ./
|
||||||
env:
|
with:
|
||||||
TEST_MODE: playmode
|
projectPath: ${{ matrix.projectPath }}
|
||||||
PROJECT_PATH: unity-project-with-correct-tests
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
ARTIFACTS_PATH: artifacts/playmode
|
testMode: playmode
|
||||||
|
artifactsPath: artifacts/playmode
|
||||||
|
|
||||||
# Upload combined artifacts
|
# Upload combined artifacts
|
||||||
- name: Upload combined test results
|
- name: Upload combined test results
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Test results (combined)
|
name: Test results (combined)
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
|
retention-days: 14
|
||||||
|
|||||||
14
.github/workflows/versioning.yml
vendored
Normal file
14
.github/workflows/versioning.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
name: Versioning
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published, edited]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
updateMajorTag:
|
||||||
|
name: Update major tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: Actions-R-Us/actions-tagger@v2
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
.idea
|
.idea
|
||||||
|
node_modules
|
||||||
|
|||||||
2
.prettierignore
Normal file
2
.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
**/node_modules/**
|
||||||
|
**/action/**
|
||||||
7
.prettierrc.json
Normal file
7
.prettierrc.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"printWidth": 100,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
3
.yarnrc
Normal file
3
.yarnrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
save-prefix ""
|
||||||
|
--install.audit true
|
||||||
|
--add.audit true
|
||||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
- Using welcoming and inclusive language
|
||||||
|
- Being respectful of differing viewpoints and experiences
|
||||||
|
- Gracefully accepting constructive criticism
|
||||||
|
- Focusing on what is best for the community
|
||||||
|
- Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at webber@takken.io. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
||||||
39
CONTRIBUTING.md
Normal file
39
CONTRIBUTING.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
#### Code of Conduct
|
||||||
|
|
||||||
|
This repository has adopted the Contributor Covenant as it's
|
||||||
|
Code of Conduct. It is expected that participants adhere to it.
|
||||||
|
|
||||||
|
#### Proposing a Change
|
||||||
|
|
||||||
|
If you are unsure about whether or not a change is desired,
|
||||||
|
you can create an issue. This is useful because it creates
|
||||||
|
the possibility for a discussion that's visible to everyone.
|
||||||
|
|
||||||
|
When fixing a bug it is fine to submit a pull request right away.
|
||||||
|
|
||||||
|
#### Sending a Pull Request
|
||||||
|
|
||||||
|
Steps to be performed to submit a pull request:
|
||||||
|
|
||||||
|
1. Fork the repository and create your branch from `master`.
|
||||||
|
2. Run `yarn` in the repository root.
|
||||||
|
3. If you've fixed a bug or added code that should be tested, add tests!
|
||||||
|
4. Fill out the description, link any related issues and submit your pull request.
|
||||||
|
|
||||||
|
#### Pull Request Prerequisites
|
||||||
|
|
||||||
|
You have [Node](https://nodejs.org/) installed at v12.2.0+ and [Yarn](https://yarnpkg.com/) at v1.18.0+.
|
||||||
|
|
||||||
|
Please note that commit hooks will run automatically to perform some tasks;
|
||||||
|
|
||||||
|
- format your code
|
||||||
|
- run tests & lint - `yarn lint && yarn test`
|
||||||
|
- build distributable files - `yarn build`
|
||||||
|
|
||||||
|
#### License
|
||||||
|
|
||||||
|
By contributing to this repository, you agree that your contributions will be licensed under its MIT license.
|
||||||
83
README.md
83
README.md
@@ -1,80 +1,43 @@
|
|||||||
# Unity - Test runner
|
# Unity - Test runner
|
||||||
[](https://github.com/webbertakken/unity-test-runner/actions?query=branch%3Amaster+workflow%3A%22Actions+%F0%9F%98%8E%22)
|
|
||||||
|
|
||||||
---
|
(Not affiliated with Unity Technologies)
|
||||||
|
|
||||||
GitHub Action to
|
GitHub Action to
|
||||||
[run tests](https://github.com/marketplace/actions/unity-test-runner)
|
[run tests](https://github.com/marketplace/actions/unity-test-runner)
|
||||||
for any Unity project.
|
for any Unity project.
|
||||||
|
|
||||||
Part of the
|
Part of the <a href="https://game.ci">GameCI</a> open source project.
|
||||||
[Unity Actions](https://github.com/webbertakken/unity-actions)
|
<br />
|
||||||
collection.
|
<br />
|
||||||
|
|
||||||
---
|
[](https://github.com/game-ci/unity-test-runner/actions?query=workflow%3A%22Actions+%F0%9F%98%8E%22)
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
This is a recommended step to prepare your pipeline for using the
|
## How to use
|
||||||
[Build](https://github.com/webbertakken/unity-actions#build)
|
|
||||||
action. This action also requires the [activation](https://github.com/marketplace/actions/unity-activate) step.
|
|
||||||
|
|
||||||
## Documentation
|
Find the
|
||||||
|
[docs](https://game.ci/docs/github/test-runner)
|
||||||
|
on the GameCI
|
||||||
|
[documentation website](https://game.ci/docs).
|
||||||
|
|
||||||
See the
|
## Related actions
|
||||||
[Unity Actions](https://github.com/webbertakken/unity-actions)
|
|
||||||
collection repository for workflow documentation and reference implementation.
|
|
||||||
|
|
||||||
## Usage
|
Visit the
|
||||||
|
GameCI <a href="https://github.com/game-ci/unity-actions">Unity Actions</a>
|
||||||
|
status repository for related Actions.
|
||||||
|
|
||||||
Create or edit the file called `.github/workflows/main.yml` and add a job to it.
|
## Community
|
||||||
|
|
||||||
```yaml
|
Feel free to join us on
|
||||||
name: Test project
|
<a href="http://game.ci/discord"><img height="30" src="media/Discord-Logo.svg" alt="Discord" /></a>
|
||||||
on: [push]
|
and engage with the community.
|
||||||
jobs:
|
|
||||||
testRunnerInAllModes:
|
|
||||||
name: Test all modes ✨
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
```
|
|
||||||
|
|
||||||
Configure the test runner as follows:
|
## Contributing
|
||||||
|
|
||||||
```yaml
|
To help improve the documentation, please find the docs [repository](https://github.com/game-ci/documentation).
|
||||||
# Configure test runner
|
|
||||||
- name: Run tests
|
|
||||||
id: myTestStep
|
|
||||||
uses: webbertakken/unity-test-runner@v1.1
|
|
||||||
env:
|
|
||||||
# Choose: "all", "playmode", "editmode"
|
|
||||||
TEST_MODE: all
|
|
||||||
|
|
||||||
# Optional: Path to your project, leave blank for "./"
|
To contribute to this project, kindly read the [contribution guide](./CONTRIBUTING.md).
|
||||||
PROJECT_PATH: relative/path/to/your/project
|
|
||||||
|
|
||||||
# Optional: Artifacts path, leave blank for "artifacts"
|
|
||||||
ARTIFACTS_PATH: store/artifacts/here
|
|
||||||
```
|
|
||||||
|
|
||||||
You use the id to **upload the artifacts** like so:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Upload artifacts
|
|
||||||
- name: Upload test results
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Test results
|
|
||||||
path: ${{ steps.myTestStep.outputs.artifactsPath }}
|
|
||||||
```
|
|
||||||
|
|
||||||
Commit and push your workflow definition.
|
|
||||||
|
|
||||||
## More actions
|
|
||||||
|
|
||||||
Visit
|
|
||||||
[Unity Actions](https://github.com/webbertakken/unity-actions)
|
|
||||||
to find related actions for Unity.
|
|
||||||
|
|
||||||
Feel free to contribute.
|
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
|
|||||||
42
action.yml
42
action.yml
@@ -1,13 +1,47 @@
|
|||||||
name: 'Unity - Test runner'
|
name: 'Unity - Test runner'
|
||||||
author: Webber Takken <webber@takken.io>
|
author: Webber Takken <webber@takken.io>
|
||||||
description: 'Run tests for any Unity project.'
|
description: 'Run tests for any Unity project.'
|
||||||
inputs: {}
|
inputs:
|
||||||
|
unityVersion:
|
||||||
|
required: false
|
||||||
|
default: 'auto'
|
||||||
|
description: 'Version of unity to use for testing the project. Use "auto" to get from your ProjectSettings/ProjectVersion.txt'
|
||||||
|
customImage:
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
description: 'Specific docker image that should be used for testing the project'
|
||||||
|
projectPath:
|
||||||
|
required: false
|
||||||
|
description: 'Path to the Unity project to be tested.'
|
||||||
|
testMode:
|
||||||
|
required: false
|
||||||
|
default: 'all'
|
||||||
|
description: 'The type of tests to be run by the test runner.'
|
||||||
|
artifactsPath:
|
||||||
|
required: false
|
||||||
|
default: 'artifacts'
|
||||||
|
description: 'Path where test artifacts should be stored.'
|
||||||
|
useNetworkHost:
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
description: 'Initialises Docker using the hosts network.'
|
||||||
|
customParameters:
|
||||||
|
required: false
|
||||||
|
description: 'Extra parameters to configure the Unity editor run.'
|
||||||
|
githubToken:
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
description: 'Token to authorize access to the GitHub REST API. If provided, a check run will be created with the test results.'
|
||||||
|
checkName:
|
||||||
|
required: false
|
||||||
|
default: 'Test Results'
|
||||||
|
description: 'Name for the check run that is created when a github token is provided.'
|
||||||
outputs:
|
outputs:
|
||||||
artifactsPath:
|
artifactsPath:
|
||||||
description: 'Path where the artifacts are stored'
|
description: 'Path where the artifacts are stored'
|
||||||
runs:
|
|
||||||
using: 'docker'
|
|
||||||
image: 'Dockerfile'
|
|
||||||
branding:
|
branding:
|
||||||
icon: 'box'
|
icon: 'box'
|
||||||
color: 'gray-dark'
|
color: 'gray-dark'
|
||||||
|
runs:
|
||||||
|
using: 'node12'
|
||||||
|
main: 'action/index.js'
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
FROM gableroux/unity3d:2019.2.11f1
|
ARG IMAGE
|
||||||
|
FROM $IMAGE
|
||||||
|
|
||||||
LABEL "com.github.actions.name"="Unity - Test runner"
|
LABEL "com.github.actions.name"="Unity - Test runner"
|
||||||
LABEL "com.github.actions.description"="Run tests for any Unity project."
|
LABEL "com.github.actions.description"="Run tests for any Unity project."
|
||||||
@@ -9,6 +10,8 @@ LABEL "repository"="http://github.com/webbertakken/unity-actions"
|
|||||||
LABEL "homepage"="http://github.com/webbertakken/unity-actions"
|
LABEL "homepage"="http://github.com/webbertakken/unity-actions"
|
||||||
LABEL "maintainer"="Webber Takken <webber@takken.io>"
|
LABEL "maintainer"="Webber Takken <webber@takken.io>"
|
||||||
|
|
||||||
|
ADD steps /steps
|
||||||
|
RUN chmod -R +x /steps
|
||||||
ADD entrypoint.sh /entrypoint.sh
|
ADD entrypoint.sh /entrypoint.sh
|
||||||
RUN chmod +x /entrypoint.sh
|
RUN chmod +x /entrypoint.sh
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
47
action/entrypoint.sh
Normal file
47
action/entrypoint.sh
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Create directory for license activation
|
||||||
|
#
|
||||||
|
|
||||||
|
ACTIVATE_LICENSE_PATH="$GITHUB_WORKSPACE/_activate-license"
|
||||||
|
mkdir -p "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Run steps
|
||||||
|
#
|
||||||
|
|
||||||
|
source /steps/activate.sh
|
||||||
|
source /steps/run_tests.sh
|
||||||
|
source /steps/return_license.sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Remove license activation directory
|
||||||
|
#
|
||||||
|
|
||||||
|
rm -r "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Instructions for debugging
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ $TEST_RUNNER_EXIT_CODE -gt 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "###########################"
|
||||||
|
echo "# Failure #"
|
||||||
|
echo "###########################"
|
||||||
|
echo ""
|
||||||
|
echo "Please note that the exit code is not very descriptive."
|
||||||
|
echo "Most likely it will not help you solve the issue."
|
||||||
|
echo ""
|
||||||
|
echo "To find the reason for failure: please search for errors in the log above."
|
||||||
|
echo ""
|
||||||
|
fi;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Exit with code from the build step.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ $USE_EXIT_CODE = true ]; then
|
||||||
|
exit $TEST_RUNNER_EXIT_CODE
|
||||||
|
fi;
|
||||||
1
action/index.js
Normal file
1
action/index.js
Normal file
File diff suppressed because one or more lines are too long
18
action/results-check-details.hbs
Normal file
18
action/results-check-details.hbs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{{#runs}}
|
||||||
|
|
||||||
|
<details><summary>{{summary}}</summary>
|
||||||
|
|
||||||
|
{{#suites}}
|
||||||
|
* {{summary}}
|
||||||
|
{{#tests}}
|
||||||
|
* {{summary}}
|
||||||
|
{{#if annotation}}
|
||||||
|
{{indent annotation.message}}
|
||||||
|
{{indent annotation.raw_details}}
|
||||||
|
{{/if}}
|
||||||
|
{{/tests}}
|
||||||
|
{{/suites}}
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
{{/runs}}
|
||||||
3
action/results-check-summary.hbs
Normal file
3
action/results-check-summary.hbs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{{#runs}}
|
||||||
|
### {{summary}}
|
||||||
|
{{/runs}}
|
||||||
111
action/steps/activate.sh
Normal file
111
action/steps/activate.sh
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Run in ACTIVATE_LICENSE_PATH directory
|
||||||
|
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
|
||||||
|
pushd "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
|
if [[ -n "$UNITY_LICENSE" ]] || [[ -n "$UNITY_LICENSE_FILE" ]]; then
|
||||||
|
#
|
||||||
|
# PERSONAL LICENSE MODE
|
||||||
|
#
|
||||||
|
# This will activate Unity, using a license file
|
||||||
|
#
|
||||||
|
# Note that this is the ONLY WAY for PERSONAL LICENSES in 2020.
|
||||||
|
# * See for more details: https://gitlab.com/gableroux/unity3d-gitlab-ci-example/issues/5#note_72815478
|
||||||
|
#
|
||||||
|
# The license file can be acquired using `game-ci/request-manual-activation-file` action.
|
||||||
|
echo "Requesting activation (personal license)"
|
||||||
|
|
||||||
|
# Set the license file path
|
||||||
|
FILE_PATH=UnityLicenseFile.ulf
|
||||||
|
|
||||||
|
if [[ -n "$UNITY_LICENSE" ]]; then
|
||||||
|
# Copy license file from Github variables
|
||||||
|
echo "$UNITY_LICENSE" | tr -d '\r' > $FILE_PATH
|
||||||
|
elif [[ -n "$UNITY_LICENSE_FILE" ]]; then
|
||||||
|
# Copy license file from file system
|
||||||
|
cat "$UNITY_LICENSE_FILE" | tr -d '\r' > $FILE_PATH
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate license
|
||||||
|
ACTIVATION_OUTPUT=$(unity-editor \
|
||||||
|
-batchmode \
|
||||||
|
-nographics \
|
||||||
|
-logFile /dev/stdout \
|
||||||
|
-quit \
|
||||||
|
-manualLicenseFile $FILE_PATH)
|
||||||
|
|
||||||
|
# Store the exit code from the verify command
|
||||||
|
UNITY_EXIT_CODE=$?
|
||||||
|
|
||||||
|
# The exit code for personal activation is always 1;
|
||||||
|
# Determine whether activation was successful.
|
||||||
|
#
|
||||||
|
# Successful output should include the following:
|
||||||
|
#
|
||||||
|
# "LICENSE SYSTEM [2020120 18:51:20] Next license update check is after 2019-11-25T18:23:38"
|
||||||
|
#
|
||||||
|
ACTIVATION_SUCCESSFUL=$(echo $ACTIVATION_OUTPUT | grep 'Next license update check is after' | wc -l)
|
||||||
|
|
||||||
|
# Set exit code to 0 if activation was successful
|
||||||
|
if [[ $ACTIVATION_SUCCESSFUL -eq 1 ]]; then
|
||||||
|
UNITY_EXIT_CODE=0
|
||||||
|
fi;
|
||||||
|
|
||||||
|
# Remove license file
|
||||||
|
rm -f $FILE_PATH
|
||||||
|
|
||||||
|
elif [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
|
||||||
|
#
|
||||||
|
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||||
|
#
|
||||||
|
# This will activate unity, using the activating process.
|
||||||
|
#
|
||||||
|
# Note: This is the preferred way for PROFESSIONAL LICENSES.
|
||||||
|
#
|
||||||
|
echo "Requesting activation (professional license)"
|
||||||
|
|
||||||
|
# Activate license
|
||||||
|
unity-editor \
|
||||||
|
-batchmode \
|
||||||
|
-nographics \
|
||||||
|
-logFile /dev/stdout \
|
||||||
|
-quit \
|
||||||
|
-serial "$UNITY_SERIAL" \
|
||||||
|
-username "$UNITY_EMAIL" \
|
||||||
|
-password "$UNITY_PASSWORD"
|
||||||
|
|
||||||
|
# Store the exit code from the verify command
|
||||||
|
UNITY_EXIT_CODE=$?
|
||||||
|
|
||||||
|
else
|
||||||
|
#
|
||||||
|
# NO LICENSE ACTIVATION STRATEGY MATCHED
|
||||||
|
#
|
||||||
|
# This will exit since no activation strategies could be matched.
|
||||||
|
#
|
||||||
|
echo "License activation strategy could not be determined."
|
||||||
|
echo ""
|
||||||
|
echo "Visit https://github.com/webbertakken/unity-builder#usage for more"
|
||||||
|
echo "details on how to set up one of the possible activation strategies."
|
||||||
|
|
||||||
|
# Immediately exit as no UNITY_EXIT_CODE can be derived.
|
||||||
|
exit 1;
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Display information about the result
|
||||||
|
#
|
||||||
|
if [ $UNITY_EXIT_CODE -eq 0 ]; then
|
||||||
|
# Activation was a success
|
||||||
|
echo "Activation complete."
|
||||||
|
else
|
||||||
|
# Activation failed so exit with the code from the license verification step
|
||||||
|
echo "Unclassified error occured while trying to activate license."
|
||||||
|
echo "Exit code was: $UNITY_EXIT_CODE"
|
||||||
|
exit $UNITY_EXIT_CODE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Return to previous working directory
|
||||||
|
popd
|
||||||
22
action/steps/return_license.sh
Normal file
22
action/steps/return_license.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Run in ACTIVATE_LICENSE_PATH directory
|
||||||
|
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
|
||||||
|
pushd "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
|
if [[ -n "$UNITY_SERIAL" ]]; then
|
||||||
|
#
|
||||||
|
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||||
|
#
|
||||||
|
# This will return the license that is currently in use.
|
||||||
|
#
|
||||||
|
unity-editor \
|
||||||
|
-batchmode \
|
||||||
|
-nographics \
|
||||||
|
-logFile /dev/stdout \
|
||||||
|
-quit \
|
||||||
|
-returnlicense
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Return to previous working directory
|
||||||
|
popd
|
||||||
@@ -1,14 +1,24 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Set the license file path
|
#
|
||||||
UNITY_PROJECT_PATH=$GITHUB_WORKSPACE/$PROJECT_PATH
|
# Set and display project path
|
||||||
|
#
|
||||||
|
|
||||||
# Set the artifacts path
|
UNITY_PROJECT_PATH="$GITHUB_WORKSPACE/$PROJECT_PATH"
|
||||||
if [ -z "$ARTIFACTS_PATH" ]; then
|
echo "Using project path \"$UNITY_PROJECT_PATH\"."
|
||||||
ARTIFACTS_PATH=artifacts
|
|
||||||
fi
|
#
|
||||||
|
# Set and display the artifacts path
|
||||||
|
#
|
||||||
|
|
||||||
|
echo "Using artifacts path \"$ARTIFACTS_PATH\" to save test results."
|
||||||
FULL_ARTIFACTS_PATH=$GITHUB_WORKSPACE/$ARTIFACTS_PATH
|
FULL_ARTIFACTS_PATH=$GITHUB_WORKSPACE/$ARTIFACTS_PATH
|
||||||
|
|
||||||
|
#
|
||||||
|
# Display custom parameters
|
||||||
|
#
|
||||||
|
echo "Using custom parameters $CUSTOM_PARAMETERS."
|
||||||
|
|
||||||
# Set the modes for testing
|
# Set the modes for testing
|
||||||
case $TEST_MODE in
|
case $TEST_MODE in
|
||||||
editmode)
|
editmode)
|
||||||
@@ -29,6 +39,12 @@ esac
|
|||||||
# The following tests are 2019 mode (requires Unity 2019.2.11f1 or later)
|
# The following tests are 2019 mode (requires Unity 2019.2.11f1 or later)
|
||||||
# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
|
# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
|
||||||
|
|
||||||
|
#
|
||||||
|
# Display the unity version
|
||||||
|
#
|
||||||
|
|
||||||
|
echo "Using Unity version \"$UNITY_VERSION\" to test."
|
||||||
|
|
||||||
#
|
#
|
||||||
# Overall info
|
# Overall info
|
||||||
#
|
#
|
||||||
@@ -58,18 +74,21 @@ if [ $EDIT_MODE = true ]; then
|
|||||||
echo "# Testing in EditMode #"
|
echo "# Testing in EditMode #"
|
||||||
echo "###########################"
|
echo "###########################"
|
||||||
echo ""
|
echo ""
|
||||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
unity-editor \
|
||||||
/opt/Unity/Editor/Unity \
|
-batchmode \
|
||||||
-batchmode \
|
-logFile "$FULL_ARTIFACTS_PATH/editmode.log" \
|
||||||
-logfile /dev/stdout \
|
-projectPath "$UNITY_PROJECT_PATH" \
|
||||||
-projectPath "$UNITY_PROJECT_PATH" \
|
-runTests \
|
||||||
-runTests \
|
-testPlatform editmode \
|
||||||
-testPlatform editmode \
|
-testResults "$FULL_ARTIFACTS_PATH/editmode-results.xml" \
|
||||||
-testResults "$FULL_ARTIFACTS_PATH/editmode-results.xml"
|
$CUSTOM_PARAMETERS
|
||||||
|
|
||||||
# Catch exit code
|
# Catch exit code
|
||||||
EDIT_MODE_EXIT_CODE=$?
|
EDIT_MODE_EXIT_CODE=$?
|
||||||
|
|
||||||
|
# Print unity log output
|
||||||
|
cat "$FULL_ARTIFACTS_PATH/editmode.log"
|
||||||
|
|
||||||
# Display results
|
# Display results
|
||||||
if [ $EDIT_MODE_EXIT_CODE -eq 0 ]; then
|
if [ $EDIT_MODE_EXIT_CODE -eq 0 ]; then
|
||||||
echo "Run succeeded, no failures occurred";
|
echo "Run succeeded, no failures occurred";
|
||||||
@@ -92,18 +111,21 @@ if [ $PLAY_MODE = true ]; then
|
|||||||
echo "# Testing in PlayMode #"
|
echo "# Testing in PlayMode #"
|
||||||
echo "###########################"
|
echo "###########################"
|
||||||
echo ""
|
echo ""
|
||||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
unity-editor \
|
||||||
/opt/Unity/Editor/Unity \
|
-batchmode \
|
||||||
-batchmode \
|
-logFile "$FULL_ARTIFACTS_PATH/playmode.log" \
|
||||||
-logfile /dev/stdout \
|
-projectPath "$UNITY_PROJECT_PATH" \
|
||||||
-projectPath "$UNITY_PROJECT_PATH" \
|
-runTests \
|
||||||
-runTests \
|
-testPlatform playmode \
|
||||||
-testPlatform playmode \
|
-testResults "$FULL_ARTIFACTS_PATH/playmode-results.xml" \
|
||||||
-testResults "$FULL_ARTIFACTS_PATH/playmode-results.xml"
|
$CUSTOM_PARAMETERS
|
||||||
|
|
||||||
# Catch exit code
|
# Catch exit code
|
||||||
PLAY_MODE_EXIT_CODE=$?
|
PLAY_MODE_EXIT_CODE=$?
|
||||||
|
|
||||||
|
# Print unity log output
|
||||||
|
cat "$FULL_ARTIFACTS_PATH/playmode.log"
|
||||||
|
|
||||||
# Display results
|
# Display results
|
||||||
if [ $PLAY_MODE_EXIT_CODE -eq 0 ]; then
|
if [ $PLAY_MODE_EXIT_CODE -eq 0 ]; then
|
||||||
echo "Run succeeded, no failures occurred";
|
echo "Run succeeded, no failures occurred";
|
||||||
@@ -140,28 +162,21 @@ fi
|
|||||||
if [ $PLAY_MODE = true ]; then
|
if [ $PLAY_MODE = true ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "###########################"
|
echo "###########################"
|
||||||
echo "# Edit Mode Results #"
|
echo "# Play Mode Results #"
|
||||||
echo "###########################"
|
echo "###########################"
|
||||||
echo ""
|
echo ""
|
||||||
cat "$FULL_ARTIFACTS_PATH/playmode-results.xml"
|
cat "$FULL_ARTIFACTS_PATH/playmode-results.xml"
|
||||||
cat "$FULL_ARTIFACTS_PATH/playmode-results.xml" | grep test-run | grep Passed
|
cat "$FULL_ARTIFACTS_PATH/playmode-results.xml" | grep test-run | grep Passed
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
|
||||||
# Output variables
|
|
||||||
#
|
|
||||||
|
|
||||||
# Set resulting name as output variable
|
|
||||||
echo ::set-output name=artifactsPath::$ARTIFACTS_PATH
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Exit
|
# Exit
|
||||||
#
|
#
|
||||||
|
|
||||||
if [ $EDIT_MODE_EXIT_CODE -gt 0 ]; then
|
if [ $EDIT_MODE_EXIT_CODE -gt 0 ]; then
|
||||||
exit $EDIT_MODE_EXIT_CODE
|
TEST_RUNNER_EXIT_CODE=$EDIT_MODE_EXIT_CODE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $PLAY_MODE_EXIT_CODE -gt 0 ]; then
|
if [ $PLAY_MODE_EXIT_CODE -gt 0 ]; then
|
||||||
exit $PLAY_MODE_EXIT_CODE
|
TEST_RUNNER_EXIT_CODE=$PLAY_MODE_EXIT_CODE
|
||||||
fi
|
fi
|
||||||
81
artifacts/editmode-results.xml
Normal file
81
artifacts/editmode-results.xml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<test-run id="2" testcasecount="6" result="Failed(Child)" total="6" passed="2" failed="2" inconclusive="0" skipped="2" asserts="0" engine-version="3.5.0.0" clr-version="4.0.30319.42000" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.1168037">
|
||||||
|
<test-suite type="TestSuite" id="1012" name="sample-project" fullname="sample-project" runstate="Runnable" testcasecount="6" result="Failed" site="Child" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.116804" total="6" passed="2" failed="2" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-suite type="Assembly" id="1020" name="Editor.dll" fullname="/github/workspace/unity-project/Library/ScriptAssemblies/Editor.dll" runstate="Runnable" testcasecount="6" result="Failed" site="Child" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.087946" total="6" passed="2" failed="2" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="78" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
</properties>
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-suite type="TestSuite" id="1021" name="Editor" fullname="Editor" runstate="Runnable" testcasecount="6" result="Failed" site="Child" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.086212" total="6" passed="2" failed="2" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-suite type="TestFixture" id="1013" name="EditorModeTest" fullname="Editor.EditorModeTest" classname="Editor.EditorModeTest" runstate="Runnable" testcasecount="6" result="Failed" site="Child" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.076416" total="6" passed="2" failed="2" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<output><![CDATA[You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all
|
||||||
|
]]></output>
|
||||||
|
<test-case id="1016" name="FailedTest" fullname="Editor.EditorModeTest.FailedTest" methodname="FailedTest" classname="Editor.EditorModeTest" runstate="Runnable" seed="330279882" result="Failed" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.023093" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[ Expected: True
|
||||||
|
But was: False
|
||||||
|
]]></message>
|
||||||
|
<stack-trace><![CDATA[at Editor.EditorModeTest.FailedTest () [0x00000] in /github/workspace/unity-project/Assets/Editor/EditorModeTest.cs:21
|
||||||
|
]]></stack-trace>
|
||||||
|
</failure>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1019" name="FailedUnityTest" fullname="Editor.EditorModeTest.FailedUnityTest" methodname="FailedUnityTest" classname="Editor.EditorModeTest" runstate="Runnable" seed="347277877" result="Failed" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.014203" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
</properties>
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[ Expected: True
|
||||||
|
But was: False
|
||||||
|
]]></message>
|
||||||
|
<stack-trace><![CDATA[at Editor.EditorModeTest+<FailedUnityTest>d__5.MoveNext () [0x0002e] in /github/workspace/unity-project/Assets/Editor/EditorModeTest.cs:40
|
||||||
|
at UnityEngine.TestTools.TestEnumerator+<Execute>d__6.MoveNext () [0x00038] in /github/workspace/unity-project/Library/PackageCache/com.unity.test-framework@1.1.19/UnityEngine.TestRunner/NUnitExtensions/Attributes/TestEnumerator.cs:36
|
||||||
|
]]></stack-trace>
|
||||||
|
</failure>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1015" name="IgnoredTest" fullname="Editor.EditorModeTest.IgnoredTest" methodname="IgnoredTest" classname="Editor.EditorModeTest" runstate="Ignored" seed="1319288303" result="Skipped" label="Ignored" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.000218" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_SKIPREASON" value="ignore" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[ignore]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1018" name="IgnoredUnityTest" fullname="Editor.EditorModeTest.IgnoredUnityTest" methodname="IgnoredUnityTest" classname="Editor.EditorModeTest" runstate="Ignored" seed="2034877647" result="Skipped" label="Ignored" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.000004" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="_SKIPREASON" value="ignore" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[ignore]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1014" name="PassedTest" fullname="Editor.EditorModeTest.PassedTest" methodname="PassedTest" classname="Editor.EditorModeTest" runstate="Runnable" seed="387558551" result="Passed" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.000366" asserts="0">
|
||||||
|
<properties />
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1017" name="PassedUnityTest" fullname="Editor.EditorModeTest.PassedUnityTest" methodname="PassedUnityTest" classname="Editor.EditorModeTest" runstate="Runnable" seed="1069930397" result="Passed" start-time="2021-01-19 20:51:34Z" end-time="2021-01-19 20:51:34Z" duration="0.007903" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-run>
|
||||||
111
artifacts/playmode-results.xml
Normal file
111
artifacts/playmode-results.xml
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<test-run id="2" testcasecount="8" result="Failed(Child)" total="8" passed="2" failed="4" inconclusive="0" skipped="2" asserts="0" engine-version="3.5.0.0" clr-version="4.0.30319.42000" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.1055695">
|
||||||
|
<test-suite type="TestSuite" id="1000" name="sample-project" fullname="sample-project" runstate="Runnable" testcasecount="8" result="Failed" site="Child" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.105570" total="8" passed="2" failed="4" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-suite type="Assembly" id="1012" name="Assembly-CSharp.dll" fullname="/github/workspace/unity-project/Library/ScriptAssemblies/Assembly-CSharp.dll" runstate="Runnable" testcasecount="8" result="Failed" site="Child" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.088008" total="8" passed="2" failed="4" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="474" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="PlayMode" />
|
||||||
|
</properties>
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-suite type="TestSuite" id="1013" name="Tests" fullname="Tests" runstate="Runnable" testcasecount="8" result="Failed" site="Child" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.084864" total="8" passed="2" failed="4" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-suite type="TestFixture" id="1001" name="PlayModeTest" fullname="Tests.PlayModeTest" classname="Tests.PlayModeTest" runstate="Runnable" testcasecount="6" result="Failed" site="Child" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.074407" total="6" passed="2" failed="2" inconclusive="0" skipped="2" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-case id="1004" name="FailedTest" fullname="Tests.PlayModeTest.FailedTest" methodname="FailedTest" classname="Tests.PlayModeTest" runstate="Runnable" seed="1067965392" result="Failed" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.034377" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[ Expected: True
|
||||||
|
But was: False
|
||||||
|
]]></message>
|
||||||
|
<stack-trace><![CDATA[at Tests.PlayModeTest.FailedTest () [0x00000] in /github/workspace/unity-project/Assets/Tests/PlayModeTest.cs:20
|
||||||
|
]]></stack-trace>
|
||||||
|
</failure>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1007" name="FailedUnityTest" fullname="Tests.PlayModeTest.FailedUnityTest" methodname="FailedUnityTest" classname="Tests.PlayModeTest" runstate="Runnable" seed="318345342" result="Failed" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.009524" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
</properties>
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[ Expected: True
|
||||||
|
But was: False
|
||||||
|
]]></message>
|
||||||
|
<stack-trace><![CDATA[at Tests.PlayModeTest+<FailedUnityTest>d__5.MoveNext () [0x0002e] in /github/workspace/unity-project/Assets/Tests/PlayModeTest.cs:39
|
||||||
|
at UnityEngine.TestTools.TestEnumerator+<Execute>d__6.MoveNext () [0x00038] in /github/workspace/unity-project/Library/PackageCache/com.unity.test-framework@1.1.19/UnityEngine.TestRunner/NUnitExtensions/Attributes/TestEnumerator.cs:36
|
||||||
|
]]></stack-trace>
|
||||||
|
</failure>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1003" name="IgnoredTest" fullname="Tests.PlayModeTest.IgnoredTest" methodname="IgnoredTest" classname="Tests.PlayModeTest" runstate="Ignored" seed="1914466070" result="Skipped" label="Ignored" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.000211" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_SKIPREASON" value="ignore" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[ignore]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1006" name="IgnoredUnityTest" fullname="Tests.PlayModeTest.IgnoredUnityTest" methodname="IgnoredUnityTest" classname="Tests.PlayModeTest" runstate="Ignored" seed="475291067" result="Skipped" label="Ignored" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.000004" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="_SKIPREASON" value="ignore" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[ignore]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1002" name="PassedTest" fullname="Tests.PlayModeTest.PassedTest" methodname="PassedTest" classname="Tests.PlayModeTest" runstate="Runnable" seed="635988114" result="Passed" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.000400" asserts="0">
|
||||||
|
<properties />
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1005" name="PassedUnityTest" fullname="Tests.PlayModeTest.PassedUnityTest" methodname="PassedUnityTest" classname="Tests.PlayModeTest" runstate="Runnable" seed="881217608" result="Passed" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.000736" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1008" name="SetupFailedTest" fullname="Tests.SetupFailedTest" classname="Tests.SetupFailedTest" runstate="Runnable" testcasecount="1" result="Failed" site="Child" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.004318" total="1" passed="0" failed="1" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-case id="1009" name="PassedTest" fullname="Tests.SetupFailedTest.PassedTest" methodname="PassedTest" classname="Tests.SetupFailedTest" runstate="Runnable" seed="1423699315" result="Failed" label="Error" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.003206" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[SetUp : System.NullReferenceException : Object reference not set to an instance of an object]]></message>
|
||||||
|
<stack-trace><![CDATA[--SetUp
|
||||||
|
at Tests.SetupFailedTest.SetUp () [0x00000] in /github/workspace/unity-project/Assets/Tests/SetupFailedTest.cs:10
|
||||||
|
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
|
||||||
|
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <9577ac7a62ef43179789031239ba8798>:0 ]]></stack-trace>
|
||||||
|
</failure>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1010" name="TearDownFailedTest" fullname="Tests.TearDownFailedTest" classname="Tests.TearDownFailedTest" runstate="Runnable" testcasecount="1" result="Failed" site="Child" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.001857" total="1" passed="0" failed="1" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[One or more child tests had errors]]></message>
|
||||||
|
</failure>
|
||||||
|
<test-case id="1011" name="PassedTest" fullname="Tests.TearDownFailedTest.PassedTest" methodname="PassedTest" classname="Tests.TearDownFailedTest" runstate="Runnable" seed="1384928637" result="Failed" label="Error" start-time="2021-01-19 20:51:44Z" end-time="2021-01-19 20:51:44Z" duration="0.000755" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<failure>
|
||||||
|
<message><![CDATA[TearDown : System.NullReferenceException : Object reference not set to an instance of an object]]></message>
|
||||||
|
<stack-trace><![CDATA[--TearDown
|
||||||
|
at Tests.TearDownFailedTest.TearDown () [0x00000] in /github/workspace/unity-project/Assets/Tests/TearDownFailedTest.cs:10
|
||||||
|
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
|
||||||
|
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <9577ac7a62ef43179789031239ba8798>:0 ]]></stack-trace>
|
||||||
|
</failure>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-run>
|
||||||
17
babel.config.js
Normal file
17
babel.config.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const esModules = ['lodash-es'].join('|');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ignore: [`/node_modules/(?!${esModules})`],
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
targets: {
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@babel/typescript',
|
||||||
|
],
|
||||||
|
plugins: ['@babel/plugin-proposal-class-properties', '@babel/proposal-object-rest-spread'],
|
||||||
|
};
|
||||||
8
jest.config.js
Normal file
8
jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
const esModules = ['lodash-es'].join('|');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
testEnvironment: 'node',
|
||||||
|
moduleFileExtensions: ['ts', 'js', 'jsx', 'json', 'vue'],
|
||||||
|
transform: { '^.+\\.(ts|js|jsx)?$': 'babel-jest' },
|
||||||
|
transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
|
||||||
|
};
|
||||||
1
media/Discord-Logo.svg
Normal file
1
media/Discord-Logo.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 272.1"><style>.st0{fill:#7289DA;}</style><path class="st0" d="M142.8 120.1c-5.7 0-10.2 4.9-10.2 11s4.6 11 10.2 11c5.7 0 10.2-4.9 10.2-11s-4.6-11-10.2-11zM106.3 120.1c-5.7 0-10.2 4.9-10.2 11s4.6 11 10.2 11c5.7 0 10.2-4.9 10.2-11 .1-6.1-4.5-11-10.2-11z"/><path class="st0" d="M191.4 36.9h-134c-11.3 0-20.5 9.2-20.5 20.5v134c0 11.3 9.2 20.5 20.5 20.5h113.4l-5.3-18.3 12.8 11.8 12.1 11.1 21.6 18.7V57.4c-.1-11.3-9.3-20.5-20.6-20.5zm-38.6 129.5s-3.6-4.3-6.6-8c13.1-3.7 18.1-11.8 18.1-11.8-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.4-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.6-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.2-1.8-1-2.8-1.7-2.8-1.7s4.8 7.9 17.5 11.7c-3 3.8-6.7 8.2-6.7 8.2-22.1-.7-30.5-15.1-30.5-15.1 0-31.9 14.4-57.8 14.4-57.8 14.4-10.7 28-10.4 28-10.4l1 1.2c-18 5.1-26.2 13-26.2 13s2.2-1.2 5.9-2.8c10.7-4.7 19.2-5.9 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.5 0 0-7.9-7.5-24.9-12.6l1.4-1.6s13.7-.3 28 10.4c0 0 14.4 25.9 14.4 57.8 0-.1-8.4 14.3-30.5 15zM303.8 79.7h-33.2V117l22.1 19.9v-36.2h11.8c7.5 0 11.2 3.6 11.2 9.4v27.7c0 5.8-3.5 9.7-11.2 9.7h-34v21.1h33.2c17.8.1 34.5-8.8 34.5-29.2v-29.8c.1-20.8-16.6-29.9-34.4-29.9zm174 59.7v-30.6c0-11 19.8-13.5 25.8-2.5l18.3-7.4c-7.2-15.8-20.3-20.4-31.2-20.4-17.8 0-35.4 10.3-35.4 30.3v30.6c0 20.2 17.6 30.3 35 30.3 11.2 0 24.6-5.5 32-19.9l-19.6-9c-4.8 12.3-24.9 9.3-24.9-1.4zM417.3 113c-6.9-1.5-11.5-4-11.8-8.3.4-10.3 16.3-10.7 25.6-.8l14.7-11.3c-9.2-11.2-19.6-14.2-30.3-14.2-16.3 0-32.1 9.2-32.1 26.6 0 16.9 13 26 27.3 28.2 7.3 1 15.4 3.9 15.2 8.9-.6 9.5-20.2 9-29.1-1.8l-14.2 13.3c8.3 10.7 19.6 16.1 30.2 16.1 16.3 0 34.4-9.4 35.1-26.6 1-21.7-14.8-27.2-30.6-30.1zm-67 55.5h22.4V79.7h-22.4v88.8zM728 79.7h-33.2V117l22.1 19.9v-36.2h11.8c7.5 0 11.2 3.6 11.2 9.4v27.7c0 5.8-3.5 9.7-11.2 9.7h-34v21.1H728c17.8.1 34.5-8.8 34.5-29.2v-29.8c0-20.8-16.7-29.9-34.5-29.9zm-162.9-1.2c-18.4 0-36.7 10-36.7 30.5v30.3c0 20.3 18.4 30.5 36.9 30.5 18.4 0 36.7-10.2 36.7-30.5V109c0-20.4-18.5-30.5-36.9-30.5zm14.4 60.8c0 6.4-7.2 9.7-14.3 9.7-7.2 0-14.4-3.1-14.4-9.7V109c0-6.5 7-10 14-10 7.3 0 14.7 3.1 14.7 10v30.3zM682.4 109c-.5-20.8-14.7-29.2-33-29.2h-35.5v88.8h22.7v-28.2h4l20.6 28.2h28L665 138.1c10.7-3.4 17.4-12.7 17.4-29.1zm-32.6 12h-13.2v-20.3h13.2c14.1 0 14.1 20.3 0 20.3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
67
package.json
Normal file
67
package.json
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"name": "unity-test-runner",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Run tests for any Unity project.",
|
||||||
|
"main": "action/index.js",
|
||||||
|
"repository": "git@github.com:webbertakken/unity-test-runner.git",
|
||||||
|
"author": "Webber <webber@takken.io>",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"prebuild": "yarn",
|
||||||
|
"build": "ncc build src --out action --minify",
|
||||||
|
"lint": "prettier --check \"src/**/*.js\" && eslint src",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.2.6",
|
||||||
|
"@actions/exec": "1.0.4",
|
||||||
|
"@actions/github": "^4.0.0",
|
||||||
|
"@octokit/types": "6.10.1",
|
||||||
|
"handlebars": "4.7.7",
|
||||||
|
"xml-js": "1.6.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.12.10",
|
||||||
|
"@babel/core": "7.12.10",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.12.13",
|
||||||
|
"@babel/plugin-proposal-object-rest-spread": "^7.12.13",
|
||||||
|
"@babel/preset-env": "7.12.11",
|
||||||
|
"@babel/preset-typescript": "^7.12.17",
|
||||||
|
"@zeit/ncc": "0.22.3",
|
||||||
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
|
"babel-eslint": "10.1.0",
|
||||||
|
"babel-jest": "^26.6.3",
|
||||||
|
"eslint": "7.17.0",
|
||||||
|
"eslint-config-airbnb": "18.2.1",
|
||||||
|
"eslint-config-prettier": "7.1.0",
|
||||||
|
"eslint-plugin-flowtype": "5.2.0",
|
||||||
|
"eslint-plugin-import": "2.22.1",
|
||||||
|
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||||
|
"eslint-plugin-prettier": "3.3.1",
|
||||||
|
"eslint-plugin-react": "7.22.0",
|
||||||
|
"eslint-plugin-unicorn": "25.0.1",
|
||||||
|
"husky": "4.3.7",
|
||||||
|
"jest": "26.6.3",
|
||||||
|
"lint-staged": "10.5.3",
|
||||||
|
"lodash-es": "4.17.20",
|
||||||
|
"prettier": "2.2.1",
|
||||||
|
"typescript": "^4.1.5"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged && yarn build && git add action/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,jsx}": [
|
||||||
|
"prettier --write",
|
||||||
|
"eslint",
|
||||||
|
"git add",
|
||||||
|
"jest --findRelatedTests"
|
||||||
|
],
|
||||||
|
"*.{json,md,yaml,yml}": [
|
||||||
|
"prettier --write",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/index.js
Normal file
51
src/index.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import * as core from '@actions/core';
|
||||||
|
import { Action, Docker, Input, ImageTag, Output, ResultsCheck } from './model';
|
||||||
|
|
||||||
|
async function action() {
|
||||||
|
Action.checkCompatibility();
|
||||||
|
|
||||||
|
const { dockerfile, workspace, actionFolder } = Action;
|
||||||
|
const {
|
||||||
|
unityVersion,
|
||||||
|
customImage,
|
||||||
|
projectPath,
|
||||||
|
testMode,
|
||||||
|
artifactsPath,
|
||||||
|
useHostNetwork,
|
||||||
|
customParameters,
|
||||||
|
githubToken,
|
||||||
|
checkName,
|
||||||
|
} = Input.getFromUser();
|
||||||
|
const baseImage = ImageTag.createForBase({ version: unityVersion, customImage });
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Build docker image
|
||||||
|
const actionImage = await Docker.build({ path: actionFolder, dockerfile, baseImage });
|
||||||
|
|
||||||
|
// Run docker image
|
||||||
|
await Docker.run(actionImage, {
|
||||||
|
workspace,
|
||||||
|
unityVersion,
|
||||||
|
projectPath,
|
||||||
|
testMode,
|
||||||
|
artifactsPath,
|
||||||
|
useHostNetwork,
|
||||||
|
customParameters,
|
||||||
|
githubToken,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// Set output
|
||||||
|
await Output.setArtifactsPath(artifactsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (githubToken) {
|
||||||
|
const failedTestCount = await ResultsCheck.createCheck(artifactsPath, githubToken, checkName);
|
||||||
|
if (failedTestCount >= 1) {
|
||||||
|
core.setFailed(`Test(s) Failed! Check '${checkName}' for details.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action().catch(error => {
|
||||||
|
core.setFailed(error.message);
|
||||||
|
});
|
||||||
48
src/model/action.js
Normal file
48
src/model/action.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
class Action {
|
||||||
|
static get supportedPlatforms() {
|
||||||
|
return ['linux'];
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isRunningLocally() {
|
||||||
|
return process.env.RUNNER_WORKSPACE === undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isRunningFromSource() {
|
||||||
|
return path.basename(__dirname) === 'model';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get name() {
|
||||||
|
return 'unity-test-runner';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get rootFolder() {
|
||||||
|
if (Action.isRunningFromSource) {
|
||||||
|
return path.dirname(path.dirname(path.dirname(__filename)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.dirname(path.dirname(__filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
static get actionFolder() {
|
||||||
|
return `${Action.rootFolder}/action`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get dockerfile() {
|
||||||
|
return `${Action.actionFolder}/Dockerfile`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get workspace() {
|
||||||
|
return process.env.GITHUB_WORKSPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static checkCompatibility() {
|
||||||
|
const currentPlatform = process.platform;
|
||||||
|
if (!Action.supportedPlatforms.includes(currentPlatform)) {
|
||||||
|
throw new Error(`Currently ${currentPlatform}-platform is not supported`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Action;
|
||||||
36
src/model/action.test.js
Normal file
36
src/model/action.test.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import Action from './action';
|
||||||
|
|
||||||
|
describe('Action', () => {
|
||||||
|
describe('compatibility check', () => {
|
||||||
|
it('throws for anything other than linux', () => {
|
||||||
|
if (process.platform !== 'linux') {
|
||||||
|
expect(() => Action.checkCompatibility()).toThrow();
|
||||||
|
} else {
|
||||||
|
expect(() => Action.checkCompatibility()).not.toThrow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the root folder of the action', () => {
|
||||||
|
const { rootFolder, name } = Action;
|
||||||
|
|
||||||
|
expect(path.basename(rootFolder)).toStrictEqual(name);
|
||||||
|
expect(fs.existsSync(rootFolder)).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the action folder', () => {
|
||||||
|
const { actionFolder } = Action;
|
||||||
|
|
||||||
|
expect(path.basename(actionFolder)).toStrictEqual('action');
|
||||||
|
expect(fs.existsSync(actionFolder)).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the docker file', () => {
|
||||||
|
const { dockerfile } = Action;
|
||||||
|
|
||||||
|
expect(path.basename(dockerfile)).toStrictEqual('Dockerfile');
|
||||||
|
expect(fs.existsSync(dockerfile)).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
73
src/model/docker.js
Normal file
73
src/model/docker.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { exec } from '@actions/exec';
|
||||||
|
import ImageTag from './image-tag';
|
||||||
|
|
||||||
|
class Docker {
|
||||||
|
static async build(buildParameters, silent = false) {
|
||||||
|
const { path, dockerfile, baseImage } = buildParameters;
|
||||||
|
const { version } = baseImage;
|
||||||
|
|
||||||
|
const tag = ImageTag.createForAction(version);
|
||||||
|
const command = `docker build ${path} \
|
||||||
|
--file ${dockerfile} \
|
||||||
|
--build-arg IMAGE=${baseImage} \
|
||||||
|
--tag ${tag}`;
|
||||||
|
|
||||||
|
await exec(command, undefined, { silent });
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async run(image, parameters, silent = false) {
|
||||||
|
const {
|
||||||
|
unityVersion,
|
||||||
|
workspace,
|
||||||
|
projectPath,
|
||||||
|
testMode,
|
||||||
|
artifactsPath,
|
||||||
|
useHostNetwork,
|
||||||
|
customParameters,
|
||||||
|
githubToken,
|
||||||
|
} = parameters;
|
||||||
|
|
||||||
|
const command = `docker run \
|
||||||
|
--workdir /github/workspace \
|
||||||
|
--rm \
|
||||||
|
--env UNITY_LICENSE \
|
||||||
|
--env UNITY_LICENSE_FILE \
|
||||||
|
--env UNITY_EMAIL \
|
||||||
|
--env UNITY_PASSWORD \
|
||||||
|
--env UNITY_SERIAL \
|
||||||
|
--env UNITY_VERSION="${unityVersion}" \
|
||||||
|
--env PROJECT_PATH="${projectPath}" \
|
||||||
|
--env TEST_MODE="${testMode}" \
|
||||||
|
--env ARTIFACTS_PATH="${artifactsPath}" \
|
||||||
|
--env CUSTOM_PARAMETERS="${customParameters}" \
|
||||||
|
--env HOME=/github/home \
|
||||||
|
--env GITHUB_REF \
|
||||||
|
--env GITHUB_SHA \
|
||||||
|
--env GITHUB_REPOSITORY \
|
||||||
|
--env GITHUB_ACTOR \
|
||||||
|
--env GITHUB_WORKFLOW \
|
||||||
|
--env GITHUB_HEAD_REF \
|
||||||
|
--env GITHUB_BASE_REF \
|
||||||
|
--env GITHUB_EVENT_NAME \
|
||||||
|
--env GITHUB_WORKSPACE=/github/workspace \
|
||||||
|
--env GITHUB_ACTION \
|
||||||
|
--env GITHUB_EVENT_PATH \
|
||||||
|
--env RUNNER_OS \
|
||||||
|
--env RUNNER_TOOL_CACHE \
|
||||||
|
--env RUNNER_TEMP \
|
||||||
|
--env RUNNER_WORKSPACE \
|
||||||
|
--volume "/var/run/docker.sock":"/var/run/docker.sock" \
|
||||||
|
--volume "/home/runner/work/_temp/_github_home":"/github/home" \
|
||||||
|
--volume "/home/runner/work/_temp/_github_workflow":"/github/workflow" \
|
||||||
|
--volume "${workspace}":"/github/workspace" \
|
||||||
|
${useHostNetwork ? '--net=host' : ''} \
|
||||||
|
${githubToken ? '--env USE_EXIT_CODE=false' : '--env USE_EXIT_CODE=true'} \
|
||||||
|
${image}`;
|
||||||
|
|
||||||
|
await exec(command, undefined, { silent });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Docker;
|
||||||
25
src/model/docker.test.js
Normal file
25
src/model/docker.test.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import Action from './action';
|
||||||
|
import Docker from './docker';
|
||||||
|
import ImageTag from './image-tag';
|
||||||
|
|
||||||
|
describe('Docker', () => {
|
||||||
|
it('builds', async () => {
|
||||||
|
const path = Action.actionFolder;
|
||||||
|
const dockerfile = `${path}/Dockerfile`;
|
||||||
|
const image = new ImageTag({
|
||||||
|
repository: '',
|
||||||
|
name: 'alpine',
|
||||||
|
version: '3',
|
||||||
|
});
|
||||||
|
|
||||||
|
const baseImage = {
|
||||||
|
toString: () => image.toString().slice(0, image.toString().lastIndexOf('-base-0')),
|
||||||
|
version: image.version,
|
||||||
|
};
|
||||||
|
|
||||||
|
const tag = await Docker.build({ path, dockerfile, baseImage }, true);
|
||||||
|
|
||||||
|
expect(tag).toBeInstanceOf(ImageTag);
|
||||||
|
expect(tag.toString()).toStrictEqual('unity-action:3-base-0');
|
||||||
|
}, 240000);
|
||||||
|
});
|
||||||
45
src/model/image-tag.js
Normal file
45
src/model/image-tag.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { trimStart } from 'lodash-es';
|
||||||
|
|
||||||
|
class ImageTag {
|
||||||
|
static createForBase({ version, customImage }) {
|
||||||
|
const repository = 'unityci';
|
||||||
|
const name = 'editor';
|
||||||
|
return new this({ repository, name, version, customImage });
|
||||||
|
}
|
||||||
|
|
||||||
|
static createForAction(version) {
|
||||||
|
const repository = '';
|
||||||
|
const name = 'unity-action';
|
||||||
|
return new this({ repository, name, version });
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor({ repository = '', name, version, customImage }) {
|
||||||
|
if (!ImageTag.versionPattern.test(version)) {
|
||||||
|
throw new Error(`Invalid version "${version}".`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, { repository, name, version, customImage });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get versionPattern() {
|
||||||
|
return /^20\d{2}\.\d\.\w{3,4}|3$/;
|
||||||
|
}
|
||||||
|
|
||||||
|
get tag() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
get image() {
|
||||||
|
return trimStart(`${this.repository}/${this.name}`, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
if (this.customImage && this.customImage !== '') {
|
||||||
|
return this.customImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${this.image}:${this.tag}-base-0`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageTag;
|
||||||
47
src/model/image-tag.test.js
Normal file
47
src/model/image-tag.test.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import ImageTag from './image-tag';
|
||||||
|
|
||||||
|
describe('ImageTag', () => {
|
||||||
|
describe('constructor', () => {
|
||||||
|
const some = {
|
||||||
|
name: 'someName',
|
||||||
|
version: '2020.0.00f0',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('can be called', () => {
|
||||||
|
expect(() => new ImageTag(some)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts parameters and sets the right properties', () => {
|
||||||
|
const image = new ImageTag(some);
|
||||||
|
|
||||||
|
expect(image.repository).toStrictEqual('');
|
||||||
|
expect(image.name).toStrictEqual(some.name);
|
||||||
|
expect(image.version).toStrictEqual(some.version);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(['2000.0.0f0', '2011.1.11f1'])('accepts %p version format', version => {
|
||||||
|
expect(() => new ImageTag({ version })).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(['some version', '', 1, undefined])('throws for incorrect versions %p', version => {
|
||||||
|
expect(() => new ImageTag({ version })).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toString', () => {
|
||||||
|
it('returns the correct version', () => {
|
||||||
|
const image = ImageTag.createForBase({ version: '2099.1.1111' });
|
||||||
|
|
||||||
|
expect(image.toString()).toStrictEqual(`unityci/editor:2099.1.1111-base-0`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns customImage if given', () => {
|
||||||
|
const image = ImageTag.createForBase({
|
||||||
|
version: '2099.1.1111',
|
||||||
|
customImage: 'unityci/editor:2099.1.1111-base-0',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(image.toString()).toStrictEqual(image.customImage);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
8
src/model/index.js
Normal file
8
src/model/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import Action from './action';
|
||||||
|
import Docker from './docker';
|
||||||
|
import Input from './input';
|
||||||
|
import ImageTag from './image-tag';
|
||||||
|
import Output from './output';
|
||||||
|
import ResultsCheck from './results-check';
|
||||||
|
|
||||||
|
export { Action, Docker, Input, ImageTag, Output, ResultsCheck };
|
||||||
10
src/model/index.test.js
Normal file
10
src/model/index.test.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import * as Index from '.';
|
||||||
|
|
||||||
|
describe('Index', () => {
|
||||||
|
test.each(['Action', 'Docker', 'ImageTag', 'Input', 'Output', 'ResultsCheck'])(
|
||||||
|
'exports %s',
|
||||||
|
exportedModule => {
|
||||||
|
expect(typeof Index[exportedModule]).toStrictEqual('function');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
67
src/model/input.js
Normal file
67
src/model/input.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { getInput } from '@actions/core';
|
||||||
|
import { includes } from 'lodash-es';
|
||||||
|
import UnityVersionParser from './unity-version-parser';
|
||||||
|
|
||||||
|
class Input {
|
||||||
|
static get testModes() {
|
||||||
|
return ['all', 'playmode', 'editmode'];
|
||||||
|
}
|
||||||
|
|
||||||
|
static isValidFolderName(folderName) {
|
||||||
|
const validFolderName = new RegExp(/^(\.|\.\/)?(\.?\w+([_-]?\w+)*\/?)*$/);
|
||||||
|
|
||||||
|
return validFolderName.test(folderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFromUser() {
|
||||||
|
// Input variables specified in workflow using "with" prop.
|
||||||
|
const rawUnityVersion = getInput('unityVersion') || 'auto';
|
||||||
|
const customImage = getInput('customImage') || '';
|
||||||
|
const testMode = getInput('testMode') || 'all';
|
||||||
|
const rawProjectPath = getInput('projectPath') || '.';
|
||||||
|
const rawArtifactsPath = getInput('artifactsPath') || 'artifacts';
|
||||||
|
const rawUseHostNetwork = getInput('useHostNetwork') || 'false';
|
||||||
|
const customParameters = getInput('customParameters') || '';
|
||||||
|
const githubToken = getInput('githubToken') || '';
|
||||||
|
const checkName = getInput('checkName') || 'Test Results';
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
if (!includes(this.testModes, testMode)) {
|
||||||
|
throw new Error(`Invalid testMode ${testMode}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isValidFolderName(rawArtifactsPath)) {
|
||||||
|
throw new Error(`Invalid artifactsPath "${rawArtifactsPath}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isValidFolderName(rawProjectPath)) {
|
||||||
|
throw new Error(`Invalid projectPath "${rawProjectPath}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawUseHostNetwork !== 'true' && rawUseHostNetwork !== 'false') {
|
||||||
|
throw new Error(`Invalid useHostNetwork "${rawUseHostNetwork}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitise input
|
||||||
|
const projectPath = rawProjectPath.replace(/\/$/, '');
|
||||||
|
const artifactsPath = rawArtifactsPath.replace(/\/$/, '');
|
||||||
|
const useHostNetwork = rawUseHostNetwork === 'true';
|
||||||
|
const unityVersion =
|
||||||
|
rawUnityVersion === 'auto' ? UnityVersionParser.read(projectPath) : rawUnityVersion;
|
||||||
|
|
||||||
|
// Return sanitised input
|
||||||
|
return {
|
||||||
|
unityVersion,
|
||||||
|
customImage,
|
||||||
|
projectPath,
|
||||||
|
testMode,
|
||||||
|
artifactsPath,
|
||||||
|
useHostNetwork,
|
||||||
|
customParameters,
|
||||||
|
githubToken,
|
||||||
|
checkName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Input;
|
||||||
34
src/model/input.test.js
Normal file
34
src/model/input.test.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import Input from './input';
|
||||||
|
|
||||||
|
describe('Input', () => {
|
||||||
|
describe('getFromUser', () => {
|
||||||
|
it('does not throw', () => {
|
||||||
|
expect(() => Input.getFromUser()).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an object', () => {
|
||||||
|
expect(typeof Input.getFromUser()).toStrictEqual('object');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidFolderName', () => {
|
||||||
|
test.each([
|
||||||
|
'.',
|
||||||
|
'./',
|
||||||
|
'folder',
|
||||||
|
'trailing/',
|
||||||
|
'.hidden',
|
||||||
|
'.hidden/sub',
|
||||||
|
'.hidden/trailing/',
|
||||||
|
'./.hidden-sub',
|
||||||
|
'hyphen-folder',
|
||||||
|
'under_score',
|
||||||
|
])('returns true for %s', folderName => {
|
||||||
|
expect(Input.isValidFolderName(folderName)).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(['..', '../'])('returns false for %s', folderName => {
|
||||||
|
expect(Input.isValidFolderName(folderName)).toStrictEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
9
src/model/output.js
Normal file
9
src/model/output.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const core = require('@actions/core');
|
||||||
|
|
||||||
|
class Output {
|
||||||
|
static async setArtifactsPath(artifactsPath) {
|
||||||
|
await core.setOutput('artifactsPath', artifactsPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Output;
|
||||||
9
src/model/output.test.js
Normal file
9
src/model/output.test.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Output from './output';
|
||||||
|
|
||||||
|
describe('Output', () => {
|
||||||
|
describe('setArtifactsPath', () => {
|
||||||
|
it('does not throw', async () => {
|
||||||
|
await expect(Output.setArtifactsPath()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
120
src/model/results-check.js
Normal file
120
src/model/results-check.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as github from '@actions/github';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import Handlebars from 'handlebars';
|
||||||
|
import ResultsParser from './results-parser';
|
||||||
|
import { RunMeta } from './ts/results-meta.ts';
|
||||||
|
|
||||||
|
class ResultsCheck {
|
||||||
|
static async createCheck(artifactsPath, githubToken, checkName) {
|
||||||
|
// Validate input
|
||||||
|
if (!fs.existsSync(artifactsPath) || !githubToken || !checkName) {
|
||||||
|
throw new Error(
|
||||||
|
`Missing input! {"artifactsPath": "${artifactsPath}", "githubToken": "${githubToken}, "checkName": "${checkName}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse all results files
|
||||||
|
const runs = [];
|
||||||
|
const files = fs.readdirSync(artifactsPath);
|
||||||
|
await Promise.all(
|
||||||
|
files.map(async filepath => {
|
||||||
|
if (!filepath.endsWith('.xml')) return;
|
||||||
|
core.info(`Processing file ${filepath}...`);
|
||||||
|
const fileData = await ResultsParser.parseResults(path.join(artifactsPath, filepath));
|
||||||
|
core.info(fileData.summary);
|
||||||
|
runs.push(fileData);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Combine all results into a single run summary
|
||||||
|
const runSummary = new RunMeta(checkName);
|
||||||
|
runs.forEach(run => {
|
||||||
|
runSummary.total += run.total;
|
||||||
|
runSummary.passed += run.passed;
|
||||||
|
runSummary.skipped += run.skipped;
|
||||||
|
runSummary.failed += run.failed;
|
||||||
|
runSummary.duration += run.duration;
|
||||||
|
run.suites.forEach(suite => {
|
||||||
|
runSummary.addTests(suite.tests);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log
|
||||||
|
core.info('=================');
|
||||||
|
core.info('Analyze result:');
|
||||||
|
core.info(runSummary.summary);
|
||||||
|
|
||||||
|
// Format output
|
||||||
|
const title = runSummary.summary;
|
||||||
|
const summary = await ResultsCheck.renderSummary(runs);
|
||||||
|
core.debug(`Summary view: ${summary}`);
|
||||||
|
const details = await ResultsCheck.renderDetails(runs);
|
||||||
|
core.debug(`Details view: ${details}`);
|
||||||
|
const rawAnnotations = runSummary.extractAnnotations();
|
||||||
|
core.debug(`Raw annotations: ${rawAnnotations}`);
|
||||||
|
const annotations = rawAnnotations.map(rawAnnotation => {
|
||||||
|
const annotation = rawAnnotation;
|
||||||
|
annotation.path = rawAnnotation.path.replace('/github/workspace/', '');
|
||||||
|
return annotation;
|
||||||
|
});
|
||||||
|
core.debug(`Annotations: ${annotations}`);
|
||||||
|
const output = {
|
||||||
|
title,
|
||||||
|
summary,
|
||||||
|
text: details,
|
||||||
|
annotations: annotations.slice(0, 50),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call GitHub API
|
||||||
|
await ResultsCheck.requestGitHubCheck(githubToken, checkName, output);
|
||||||
|
return runSummary.failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async requestGitHubCheck(githubToken, checkName, output) {
|
||||||
|
const pullRequest = github.context.payload.pull_request;
|
||||||
|
const headSha = (pullRequest && pullRequest.head.sha) || github.context.sha;
|
||||||
|
|
||||||
|
core.info(`Posting results for ${headSha}`);
|
||||||
|
const createCheckRequest = {
|
||||||
|
...github.context.repo,
|
||||||
|
name: checkName,
|
||||||
|
head_sha: headSha,
|
||||||
|
status: 'completed',
|
||||||
|
conclusion: 'neutral',
|
||||||
|
output,
|
||||||
|
};
|
||||||
|
|
||||||
|
const octokit = github.getOctokit(githubToken);
|
||||||
|
await octokit.checks.create(createCheckRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async renderSummary(runMetas) {
|
||||||
|
return ResultsCheck.render(`${__dirname}/../views/results-check-summary.hbs`, runMetas);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async renderDetails(runMetas) {
|
||||||
|
return ResultsCheck.render(`${__dirname}/../views/results-check-details.hbs`, runMetas);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async render(viewPath, runMetas) {
|
||||||
|
Handlebars.registerHelper('indent', toIndent =>
|
||||||
|
toIndent
|
||||||
|
.split('\n')
|
||||||
|
.map(s => ` ${s.replace('/github/workspace/', '')}`)
|
||||||
|
.join('\n'),
|
||||||
|
);
|
||||||
|
const source = await fs.promises.readFile(viewPath, 'utf8');
|
||||||
|
const template = Handlebars.compile(source);
|
||||||
|
return template(
|
||||||
|
{ runs: runMetas },
|
||||||
|
{
|
||||||
|
allowProtoMethodsByDefault: true,
|
||||||
|
allowProtoPropertiesByDefault: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ResultsCheck;
|
||||||
9
src/model/results-check.test.js
Normal file
9
src/model/results-check.test.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import ResultsCheck from './results-check';
|
||||||
|
|
||||||
|
describe('ResultsCheck', () => {
|
||||||
|
describe('createCheck', () => {
|
||||||
|
it('throws for missing input', () => {
|
||||||
|
expect(() => ResultsCheck.createCheck('', '', '')).rejects.toEqual(Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
123
src/model/results-parser.js
Normal file
123
src/model/results-parser.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as xmljs from 'xml-js';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { RunMeta, TestMeta } from './ts/results-meta.ts';
|
||||||
|
|
||||||
|
class ResultsParser {
|
||||||
|
static async parseResults(filepath) {
|
||||||
|
if (!fs.existsSync(filepath)) {
|
||||||
|
throw new Error(`Missing file! {"filepath": "${filepath}"}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`Trying to open ${filepath}`);
|
||||||
|
const file = await fs.promises.readFile(filepath, 'utf8');
|
||||||
|
const results = xmljs.xml2js(file, { compact: true });
|
||||||
|
core.info(`File ${filepath} parsed...`);
|
||||||
|
|
||||||
|
return ResultsParser.convertResults(path.basename(filepath), results);
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertResults(filename, filedata) {
|
||||||
|
core.info(`Start analyzing results: ${filename}`);
|
||||||
|
|
||||||
|
const run = filedata['test-run'];
|
||||||
|
const runMeta = new RunMeta(filename);
|
||||||
|
const tests = ResultsParser.convertSuite(run['test-suite']);
|
||||||
|
core.debug(tests);
|
||||||
|
|
||||||
|
runMeta.total = Number(run._attributes.total);
|
||||||
|
runMeta.failed = Number(run._attributes.failed);
|
||||||
|
runMeta.skipped = Number(run._attributes.skipped);
|
||||||
|
runMeta.passed = Number(run._attributes.passed);
|
||||||
|
runMeta.duration = Number(run._attributes.duration);
|
||||||
|
runMeta.addTests(tests);
|
||||||
|
|
||||||
|
return runMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertSuite(suites) {
|
||||||
|
if (Array.isArray(suites)) {
|
||||||
|
const innerResult = [];
|
||||||
|
suites.forEach(suite => {
|
||||||
|
innerResult.push(...ResultsParser.convertSuite(suite));
|
||||||
|
});
|
||||||
|
return innerResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
const innerSuite = suites['test-suite'];
|
||||||
|
if (innerSuite) {
|
||||||
|
result.push(...ResultsParser.convertSuite(innerSuite));
|
||||||
|
}
|
||||||
|
|
||||||
|
const tests = suites['test-case'];
|
||||||
|
if (tests) {
|
||||||
|
result.push(...ResultsParser.convertTests(suites._attributes.fullname, tests));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertTests(suite, tests) {
|
||||||
|
if (Array.isArray(tests)) {
|
||||||
|
const result = [];
|
||||||
|
tests.forEach(test => {
|
||||||
|
result.push(ResultsParser.convertTestCase(suite, test));
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ResultsParser.convertTestCase(suite, tests)];
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertTestCase(suite, testCase) {
|
||||||
|
const { _attributes, failure } = testCase;
|
||||||
|
const { name, fullname, result, duration } = _attributes;
|
||||||
|
const testMeta = new TestMeta(suite, name);
|
||||||
|
testMeta.result = result;
|
||||||
|
testMeta.duration = Number(duration);
|
||||||
|
|
||||||
|
if (!failure) {
|
||||||
|
core.debug(`Skip test ${fullname} without failure data`);
|
||||||
|
return testMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.debug(`Convert data for test ${fullname}`);
|
||||||
|
if (failure['stack-trace'] === undefined) {
|
||||||
|
core.warning(`No stack trace for test case: ${fullname}`);
|
||||||
|
return testMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trace = failure['stack-trace']._cdata;
|
||||||
|
const point = ResultsParser.findAnnotationPoint(trace);
|
||||||
|
if (!point.path || !point.line) {
|
||||||
|
core.warning(`Not able to find annotation point for failed test! Test trace: ${trace}`);
|
||||||
|
return testMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
testMeta.annotation = {
|
||||||
|
path: point.path,
|
||||||
|
start_line: point.line,
|
||||||
|
end_line: point.line,
|
||||||
|
annotation_level: 'failure',
|
||||||
|
title: fullname,
|
||||||
|
message: failure.message._cdata,
|
||||||
|
raw_details: trace,
|
||||||
|
};
|
||||||
|
core.info(
|
||||||
|
`- ${testMeta.annotation.path}:${testMeta.annotation.start_line} - ${testMeta.annotation.title}`,
|
||||||
|
);
|
||||||
|
return testMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
static findAnnotationPoint(trace) {
|
||||||
|
const match = trace.match(/at .* in ((?<path>[^:]+):(?<line>\d+))/);
|
||||||
|
return {
|
||||||
|
path: match ? match.groups.path : '',
|
||||||
|
line: match ? Number(match.groups.line) : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ResultsParser;
|
||||||
205
src/model/results-parser.test.js
Normal file
205
src/model/results-parser.test.js
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import * as xmljs from 'xml-js';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import ResultsParser from './results-parser';
|
||||||
|
|
||||||
|
describe('ResultsParser', () => {
|
||||||
|
describe('parseResults', () => {
|
||||||
|
it('throws for missing file', () => {
|
||||||
|
expect(() => ResultsParser.parseResults('')).rejects.toEqual(Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses editmode-results.xml', () => {
|
||||||
|
expect(() => ResultsParser.parseResults('./artifacts/editmode-results.xml')).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses playmode-results.xml', () => {
|
||||||
|
expect(() => ResultsParser.parseResults('./artifacts/playmode-results.xml')).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('convertResults', () => {
|
||||||
|
it('converts editmode-results.xml', () => {
|
||||||
|
const file = fs.readFileSync('./artifacts/editmode-results.xml');
|
||||||
|
const filedata = xmljs.xml2js(file, { compact: true });
|
||||||
|
const result = ResultsParser.convertResults('editmode-results.xml', filedata);
|
||||||
|
expect(result.suites.length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts playmode-results.xml', () => {
|
||||||
|
const file = fs.readFileSync('./artifacts/playmode-results.xml');
|
||||||
|
const filedata = xmljs.xml2js(file, { compact: true });
|
||||||
|
const result = ResultsParser.convertResults('playmode-results.xml', filedata);
|
||||||
|
expect(result.suites.length).toEqual(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('convertSuite', () => {
|
||||||
|
test('convert single', () => {
|
||||||
|
const targetSuite = {
|
||||||
|
_attributes: {
|
||||||
|
fullname: 'Suite Full Name',
|
||||||
|
},
|
||||||
|
'test-case': [{ _attributes: { name: 'testA' } }, { _attributes: { name: 'testB' } }],
|
||||||
|
'test-suite': [
|
||||||
|
{
|
||||||
|
_attributes: {
|
||||||
|
fullname: 'Inner Suite Full Name',
|
||||||
|
},
|
||||||
|
'test-case': { _attributes: { name: 'testC' } },
|
||||||
|
'test-suite': [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const result = ResultsParser.convertSuite(targetSuite);
|
||||||
|
|
||||||
|
expect(result).toMatchObject([
|
||||||
|
{
|
||||||
|
annotation: undefined,
|
||||||
|
duration: Number.NaN,
|
||||||
|
result: undefined,
|
||||||
|
suite: 'Inner Suite Full Name',
|
||||||
|
title: 'testC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
annotation: undefined,
|
||||||
|
duration: Number.NaN,
|
||||||
|
result: undefined,
|
||||||
|
suite: 'Suite Full Name',
|
||||||
|
title: 'testA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
annotation: undefined,
|
||||||
|
duration: Number.NaN,
|
||||||
|
result: undefined,
|
||||||
|
suite: 'Suite Full Name',
|
||||||
|
title: 'testB',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('convertTests', () => {
|
||||||
|
test('convert array', () => {
|
||||||
|
const testA = { _attributes: { name: 'testA' } };
|
||||||
|
const testB = { _attributes: { name: 'testB' } };
|
||||||
|
const testResult = [testA, testB];
|
||||||
|
const result = ResultsParser.convertTests('Test Suite', testResult);
|
||||||
|
|
||||||
|
expect(result).toMatchObject([
|
||||||
|
{ suite: 'Test Suite', title: 'testA' },
|
||||||
|
{ suite: 'Test Suite', title: 'testB' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('convert single', () => {
|
||||||
|
const testA = { _attributes: { name: 'testA' } };
|
||||||
|
const result = ResultsParser.convertTests('Test Suite', testA);
|
||||||
|
|
||||||
|
expect(result).toMatchObject([{ suite: 'Test Suite', title: 'testA' }]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('convertTestCase', () => {
|
||||||
|
test('not failed', () => {
|
||||||
|
const result = ResultsParser.convertTestCase('Test Suite', {
|
||||||
|
_attributes: {
|
||||||
|
name: 'Test Name',
|
||||||
|
duration: '3.14',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.suite).toBe('Test Suite');
|
||||||
|
expect(result.title).toBe('Test Name');
|
||||||
|
expect(result.duration).toBe(3.14);
|
||||||
|
expect(result.annotation).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('no stack trace', () => {
|
||||||
|
const result = ResultsParser.convertTestCase('Test Suite', {
|
||||||
|
_attributes: {
|
||||||
|
name: 'Test Name',
|
||||||
|
duration: '3.14',
|
||||||
|
},
|
||||||
|
failure: {
|
||||||
|
message: { _cdata: 'Message CDATA' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.suite).toBe('Test Suite');
|
||||||
|
expect(result.title).toBe('Test Name');
|
||||||
|
expect(result.duration).toBe(3.14);
|
||||||
|
expect(result.annotation).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('no annotation path', () => {
|
||||||
|
const result = ResultsParser.convertTestCase('Test Suite', {
|
||||||
|
_attributes: {
|
||||||
|
name: 'Test Name',
|
||||||
|
duration: '3.14',
|
||||||
|
},
|
||||||
|
failure: {
|
||||||
|
message: { _cdata: 'Message CDATA' },
|
||||||
|
'stack-trace': { _cdata: 'Test CDATA' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.suite).toBe('Test Suite');
|
||||||
|
expect(result.title).toBe('Test Name');
|
||||||
|
expect(result.duration).toBe(3.14);
|
||||||
|
expect(result.annotation).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('prepare annotation', () => {
|
||||||
|
const result = ResultsParser.convertTestCase('Test Suite', {
|
||||||
|
_attributes: {
|
||||||
|
name: 'Test Name',
|
||||||
|
fullname: 'Test Full Name',
|
||||||
|
duration: '3.14',
|
||||||
|
},
|
||||||
|
failure: {
|
||||||
|
message: { _cdata: 'Message CDATA' },
|
||||||
|
'stack-trace': {
|
||||||
|
_cdata:
|
||||||
|
'at Tests.SetupFailedTest.SetUp () [0x00000] in /github/workspace/unity-project/Assets/Tests/SetupFailedTest.cs:10',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.suite).toBe('Test Suite');
|
||||||
|
expect(result.title).toBe('Test Name');
|
||||||
|
expect(result.duration).toBe(3.14);
|
||||||
|
expect(result.annotation).toMatchObject({
|
||||||
|
annotation_level: 'failure',
|
||||||
|
end_line: 10,
|
||||||
|
message: 'Message CDATA',
|
||||||
|
path: '/github/workspace/unity-project/Assets/Tests/SetupFailedTest.cs',
|
||||||
|
raw_details:
|
||||||
|
'at Tests.SetupFailedTest.SetUp () [0x00000] in /github/workspace/unity-project/Assets/Tests/SetupFailedTest.cs:10',
|
||||||
|
start_line: 10,
|
||||||
|
title: 'Test Full Name',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findAnnotationPoint', () => {
|
||||||
|
test('keep working if not matching', () => {
|
||||||
|
const result = ResultsParser.findAnnotationPoint('');
|
||||||
|
expect(result.path).toBe('');
|
||||||
|
expect(result.line).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('simple annotation point', () => {
|
||||||
|
const result = ResultsParser.findAnnotationPoint(`at Tests.PlayModeTest+<FailedUnityTest>d__5.MoveNext () [0x0002e] in /github/workspace/unity-project/Assets/Tests/PlayModeTest.cs:39
|
||||||
|
at UnityEngine.TestTools.TestEnumerator+<Execute>d__6.MoveNext () [0x00038] in /github/workspace/unity-project/Library/PackageCache/com.unity.test-framework@1.1.19/UnityEngine.TestRunner/NUnitExtensions/Attributes/TestEnumerator.cs:36`);
|
||||||
|
expect(result.path).toBe('/github/workspace/unity-project/Assets/Tests/PlayModeTest.cs');
|
||||||
|
expect(result.line).toBe(39);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setup annotation point', () => {
|
||||||
|
const result = ResultsParser.findAnnotationPoint(`SetUp
|
||||||
|
at Tests.SetupFailedTest.SetUp () [0x00000] in /github/workspace/unity-project/Assets/Tests/SetupFailedTest.cs:10`);
|
||||||
|
expect(result.path).toBe('/github/workspace/unity-project/Assets/Tests/SetupFailedTest.cs');
|
||||||
|
expect(result.line).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
118
src/model/ts/results-meta.ts
Normal file
118
src/model/ts/results-meta.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { components } from '@octokit/openapi-types/dist-types/generated/types';
|
||||||
|
|
||||||
|
export function timeHelper(seconds: number): string {
|
||||||
|
return `${seconds.toFixed(3)}s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Meta {
|
||||||
|
title: string;
|
||||||
|
duration = 0;
|
||||||
|
|
||||||
|
constructor(title: string) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract get summary(): string;
|
||||||
|
|
||||||
|
abstract get mark(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Annotation = components['schemas']['check-annotation'];
|
||||||
|
|
||||||
|
export class RunMeta extends Meta {
|
||||||
|
total = 0;
|
||||||
|
passed = 0;
|
||||||
|
skipped = 0;
|
||||||
|
failed = 0;
|
||||||
|
|
||||||
|
tests: TestMeta[] = [];
|
||||||
|
suites: RunMeta[] = [];
|
||||||
|
|
||||||
|
extractAnnotations(): Annotation[] {
|
||||||
|
const result = [] as Annotation[];
|
||||||
|
for (const suite of this.suites) {
|
||||||
|
result.push(...suite.extractAnnotations());
|
||||||
|
}
|
||||||
|
for (const test of this.tests) {
|
||||||
|
if (test.annotation !== undefined) {
|
||||||
|
result.push(test.annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTests(testSuite: TestMeta[]): void {
|
||||||
|
testSuite.forEach(test => {
|
||||||
|
this.addTest(test);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addTest(test: TestMeta): void {
|
||||||
|
if (test.suite === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (test.suite === this.title) {
|
||||||
|
this.total++;
|
||||||
|
this.duration += test.duration;
|
||||||
|
this.tests.push(test);
|
||||||
|
if (test.result === 'Passed') this.passed++;
|
||||||
|
else if (test.result === 'Failed') this.failed++;
|
||||||
|
else this.skipped++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = this.suites.find(s => s.title === test.suite);
|
||||||
|
if (target === undefined) {
|
||||||
|
target = new RunMeta(test.suite);
|
||||||
|
this.suites.push(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.addTest(test);
|
||||||
|
}
|
||||||
|
|
||||||
|
get summary(): string {
|
||||||
|
const result = this.failed > 0 ? 'Failed' : 'Passed';
|
||||||
|
const sPart = this.skipped > 0 ? `, skipped: ${this.skipped}` : '';
|
||||||
|
const fPart = this.failed > 0 ? `, failed: ${this.failed}` : '';
|
||||||
|
const dPart = ` in ${timeHelper(this.duration)}`;
|
||||||
|
return `${this.mark} ${this.title} - ${this.passed}/${this.total}${sPart}${fPart} - ${result}${dPart}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get mark(): string {
|
||||||
|
if (this.failed > 0) return '❌️';
|
||||||
|
else if (this.skipped === 0) return '✅';
|
||||||
|
return '⚠️';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TestMeta extends Meta {
|
||||||
|
suite: string;
|
||||||
|
result: string | undefined;
|
||||||
|
annotation: Annotation | undefined;
|
||||||
|
|
||||||
|
constructor(suite: string, title: string) {
|
||||||
|
super(title);
|
||||||
|
this.suite = suite;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSkipped(): boolean {
|
||||||
|
return this.result === 'Skipped';
|
||||||
|
}
|
||||||
|
|
||||||
|
isFailed(): boolean {
|
||||||
|
return this.result === 'Failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
get summary(): string {
|
||||||
|
const dPart = this.isSkipped()
|
||||||
|
? ''
|
||||||
|
: ` in ${timeHelper(this.duration)}`;
|
||||||
|
return `${this.mark} **${this.title}** - ${this.result}${dPart}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get mark(): string {
|
||||||
|
if (this.isFailed()) return '❌️';
|
||||||
|
else if (this.isSkipped()) return '⚠️';
|
||||||
|
return '✅';
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/model/ts/results-report.ts
Normal file
58
src/model/ts/results-report.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
interface CommonAttributes {
|
||||||
|
id: string;
|
||||||
|
result: string;
|
||||||
|
asserts: string;
|
||||||
|
|
||||||
|
'start-time': string;
|
||||||
|
'end-time': string;
|
||||||
|
duration: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommonSuiteAttributes extends CommonAttributes {
|
||||||
|
total: string;
|
||||||
|
passed: string;
|
||||||
|
failed: string;
|
||||||
|
skipped: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestRun {
|
||||||
|
_attributes: TestRunAttributes;
|
||||||
|
'test-suite': TestSuite | TestSuite[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestRunAttributes extends CommonSuiteAttributes {
|
||||||
|
testcasecount: string;
|
||||||
|
'engine-version': string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestSuite {
|
||||||
|
_attributes: TestSuiteAttributes;
|
||||||
|
'test-suite': TestSuite | TestSuite[];
|
||||||
|
'test-case': TestCase | TestCase[];
|
||||||
|
failure?: FailureMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestSuiteAttributes extends CommonSuiteAttributes {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
fullname: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestCase {
|
||||||
|
_attributes: TestCaseAttributes;
|
||||||
|
failure?: FailureMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestCaseAttributes extends CommonAttributes {
|
||||||
|
name: string;
|
||||||
|
fullname: string;
|
||||||
|
methodname: string;
|
||||||
|
classname: string;
|
||||||
|
runstate: string;
|
||||||
|
seed: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FailureMessage {
|
||||||
|
message: { _cdata: string };
|
||||||
|
'stack-trace'?: { _cdata: string };
|
||||||
|
}
|
||||||
26
src/model/unity-version-parser.js
Normal file
26
src/model/unity-version-parser.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
class UnityVersionParser {
|
||||||
|
static get versionPattern() {
|
||||||
|
return /20\d{2}\.\d\.\w{3,4}|3/;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parse(projectVersionTxt) {
|
||||||
|
const matches = projectVersionTxt.match(UnityVersionParser.versionPattern);
|
||||||
|
if (!matches || matches.length === 0) {
|
||||||
|
throw new Error(`Failed to parse version from "${projectVersionTxt}".`);
|
||||||
|
}
|
||||||
|
return matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static read(projectPath) {
|
||||||
|
const filePath = path.join(projectPath, 'ProjectSettings', 'ProjectVersion.txt');
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
return 'auto';
|
||||||
|
}
|
||||||
|
return UnityVersionParser.parse(fs.readFileSync(filePath, 'utf8'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UnityVersionParser;
|
||||||
25
src/model/unity-version-parser.test.js
Normal file
25
src/model/unity-version-parser.test.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import UnityVersionParser from './unity-version-parser';
|
||||||
|
|
||||||
|
describe('UnityVersionParser', () => {
|
||||||
|
describe('parse', () => {
|
||||||
|
it('throws for empty string', () => {
|
||||||
|
expect(() => UnityVersionParser.parse('')).toThrow(Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses from ProjectVersion.txt', () => {
|
||||||
|
const projectVersionContents = `m_EditorVersion: 2019.2.11f1
|
||||||
|
m_EditorVersionWithRevision: 2019.2.11f1 (5f859a4cfee5)`;
|
||||||
|
expect(UnityVersionParser.parse(projectVersionContents)).toBe('2019.2.11f1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('read', () => {
|
||||||
|
it('does not throw', () => {
|
||||||
|
expect(() => UnityVersionParser.read('')).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reads from unity-project-with-correct-tests', () => {
|
||||||
|
expect(UnityVersionParser.read('./unity-project-with-correct-tests')).toBe('2019.2.11f1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
18
src/views/results-check-details.hbs
Normal file
18
src/views/results-check-details.hbs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{{#runs}}
|
||||||
|
|
||||||
|
<details><summary>{{summary}}</summary>
|
||||||
|
|
||||||
|
{{#suites}}
|
||||||
|
* {{summary}}
|
||||||
|
{{#tests}}
|
||||||
|
* {{summary}}
|
||||||
|
{{#if annotation}}
|
||||||
|
{{indent annotation.message}}
|
||||||
|
{{indent annotation.raw_details}}
|
||||||
|
{{/if}}
|
||||||
|
{{/tests}}
|
||||||
|
{{/suites}}
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
{{/runs}}
|
||||||
3
src/views/results-check-summary.hbs
Normal file
3
src/views/results-check-summary.hbs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{{#runs}}
|
||||||
|
### {{summary}}
|
||||||
|
{{/runs}}
|
||||||
71
tsconfig.json
Normal file
71
tsconfig.json
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
/* Basic Options */
|
||||||
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
|
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
|
||||||
|
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
|
||||||
|
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||||
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
|
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||||
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
|
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||||
|
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
// "composite": true, /* Enable project compilation */
|
||||||
|
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||||
|
// "removeComments": true, /* Do not emit comments to output. */
|
||||||
|
// "noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
|
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
|
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
|
||||||
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
|
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||||
|
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||||
|
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||||
|
|
||||||
|
/* Module Resolution Options */
|
||||||
|
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
|
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||||
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
|
||||||
|
/* Source Map Options */
|
||||||
|
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||||
|
|
||||||
|
/* Experimental Options */
|
||||||
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
|
||||||
|
/* Advanced Options */
|
||||||
|
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
||||||
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||||
|
},
|
||||||
|
"include": ["src/model/ts"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class SampleComponent : MonoBehaviour
|
||||||
|
{
|
||||||
|
public BasicCounter Counter;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
Counter = new BasicCounter(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called once per frame
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
Counter.Increment();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7a5f63b9ea4b465194653c4d681faf42
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class TimerComponent : MonoBehaviour
|
||||||
|
{
|
||||||
|
public BasicCounter Counter = new BasicCounter();
|
||||||
|
public float Timer = 1f;
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
Timer -= Time.deltaTime;
|
||||||
|
|
||||||
|
if (Timer > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Counter.Increment();
|
||||||
|
Timer = 1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b668d68a45bb48108ccda73269e3da7b
|
||||||
|
timeCreated: 1610056748
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
"name": "PlayModeTests",
|
"name": "PlayModeTests",
|
||||||
"references": [
|
"references": [
|
||||||
"UnityEngine.TestRunner",
|
"UnityEngine.TestRunner",
|
||||||
"UnityEditor.TestRunner"
|
"UnityEditor.TestRunner",
|
||||||
|
"MyScripts"
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
public class SampleComponentTest
|
||||||
|
{
|
||||||
|
private GameObject target;
|
||||||
|
private SampleComponent component;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
target = GameObject.Instantiate(new GameObject());
|
||||||
|
component = target.AddComponent<SampleComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TestIncrementOnUpdateAfterNextFrame()
|
||||||
|
{
|
||||||
|
// Save the current value, since it was updated after component Start() method called
|
||||||
|
var count = component.Counter.Count;
|
||||||
|
|
||||||
|
// Skip frame and assert the new value
|
||||||
|
yield return null;
|
||||||
|
Assert.AreEqual(count + 1, component.Counter.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c688799d17b14d35ad515bff9de8d12c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
namespace Tests
|
namespace Tests
|
||||||
@@ -12,8 +10,14 @@ namespace Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void NewTestScriptSimplePasses()
|
public void NewTestScriptSimplePasses()
|
||||||
{
|
{
|
||||||
// Use the Assert class to test conditions
|
// Given
|
||||||
Assert.True(true);
|
var counter = new BasicCounter(0);
|
||||||
|
|
||||||
|
// When
|
||||||
|
counter.Increment();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
Assert.AreEqual(1, counter.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
|
// A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
|
||||||
@@ -21,9 +25,18 @@ namespace Tests
|
|||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator NewTestScriptWithEnumeratorPasses()
|
public IEnumerator NewTestScriptWithEnumeratorPasses()
|
||||||
{
|
{
|
||||||
|
// Given
|
||||||
|
var counter = new BasicCounter(3);
|
||||||
|
|
||||||
// Use the Assert class to test conditions.
|
// Use the Assert class to test conditions.
|
||||||
// Use yield to skip a frame.
|
// Use yield to skip a frame.
|
||||||
yield return null;
|
yield return null;
|
||||||
|
|
||||||
|
// When
|
||||||
|
counter.Increment();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
Assert.AreEqual(4, counter.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
public class TimerComponentTest
|
||||||
|
{
|
||||||
|
private GameObject target;
|
||||||
|
private TimerComponent component;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
target = GameObject.Instantiate(new GameObject());
|
||||||
|
component = target.AddComponent<TimerComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TestIncrementAfterSomeTime()
|
||||||
|
{
|
||||||
|
// Save the current value, since it was updated after component Start() method called
|
||||||
|
var count = component.Counter.Count;
|
||||||
|
|
||||||
|
// Skip frame and assert the new value
|
||||||
|
yield return null;
|
||||||
|
Assert.AreEqual(count, component.Counter.Count);
|
||||||
|
|
||||||
|
yield return new WaitForSeconds(1.1f);
|
||||||
|
Assert.AreEqual(count + 1, component.Counter.Count);
|
||||||
|
|
||||||
|
yield return new WaitForSeconds(1.1f);
|
||||||
|
Assert.AreEqual(count + 2, component.Counter.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TestTimeScaleIsAffectingIncrement()
|
||||||
|
{
|
||||||
|
// Save the current value, since it was updated after component Start() method called
|
||||||
|
var count = component.Counter.Count;
|
||||||
|
Time.timeScale = .5f;
|
||||||
|
|
||||||
|
// Skip frame and assert the new value
|
||||||
|
yield return null;
|
||||||
|
Assert.AreEqual(count, component.Counter.Count);
|
||||||
|
|
||||||
|
yield return WaitForRealSeconds(1.1f);
|
||||||
|
Assert.AreEqual(count, component.Counter.Count);
|
||||||
|
|
||||||
|
yield return WaitForRealSeconds(1.1f);
|
||||||
|
Assert.AreEqual(count + 1, component.Counter.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping time ignoring Time.scale
|
||||||
|
// https://answers.unity.com/questions/301868/yield-waitforseconds-outside-of-timescale.html
|
||||||
|
public static IEnumerator WaitForRealSeconds(float time)
|
||||||
|
{
|
||||||
|
float start = Time.realtimeSinceStartup;
|
||||||
|
while (Time.realtimeSinceStartup < start + time)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 818b22370d404398b87b47a922a435c9
|
||||||
|
timeCreated: 1610056889
|
||||||
Reference in New Issue
Block a user