From f28363ae56ec3d182c43e7b74f82b1b3f782b01e Mon Sep 17 00:00:00 2001 From: Letter N <24603524+LetterN@users.noreply.github.com> Date: Tue, 29 Dec 2020 11:12:56 +0800 Subject: [PATCH] code quality --- .dockerignore | 2 - .gitattributes | 3 + .github/workflows/ci_suite.yml | 110 ++++++++++++++++++ .github/workflows/compile_changelogs.yml | 2 +- .github/workflows/docker_publish.yml | 22 ++++ .github/workflows/generate_documentation.yml | 30 +++++ .github/workflows/update_tgs_dmapi.yml | 4 +- .travis.yml | 102 ---------------- Dockerfile | 50 ++------ SpacemanDMM.toml | 7 ++ TGS3.json | 17 +-- _maps/_basemap.dm | 2 +- appveyor.yml | 15 --- code/__HELPERS/_logging.dm | 2 +- code/_compile_options.dm | 8 +- code/game/world.dm | 12 +- code/modules/unit_tests/README.md | 70 +++++++++++ code/modules/unit_tests/_unit_tests.dm | 62 ++++++++-- code/modules/unit_tests/card_mismatch.dm | 7 ++ code/modules/unit_tests/combat.dm | 98 ++++++++++++++++ code/modules/unit_tests/confusion.dm | 16 +++ code/modules/unit_tests/emoting.dm | 25 ++++ code/modules/unit_tests/heretic_knowledge.dm | 21 ++++ code/modules/unit_tests/holidays.dm | 33 ++++++ code/modules/unit_tests/initialize_sanity.dm | 11 ++ code/modules/unit_tests/keybinding_init.dm | 6 + .../modules/unit_tests/machine_disassembly.dm | 3 +- code/modules/unit_tests/merge_type.dm | 15 +++ code/modules/unit_tests/metabolizing.dm | 19 +++ code/modules/unit_tests/outfit_sanity.dm | 4 +- code/modules/unit_tests/pills.dm | 10 ++ code/modules/unit_tests/projectiles.dm | 5 + code/modules/unit_tests/reagent_mod_expose.dm | 59 ++++++++++ code/modules/unit_tests/reagent_mod_procs.dm | 12 ++ code/modules/unit_tests/say.dm | 1 - code/modules/unit_tests/serving_tray.dm | 47 ++++++++ code/modules/unit_tests/siunit.dm | 15 +++ code/modules/unit_tests/spawn_humans.dm | 8 +- code/modules/unit_tests/species_whitelists.dm | 5 + code/modules/unit_tests/stomach.dm | 40 +++++++ code/modules/unit_tests/surgeries.dm | 29 ++++- code/modules/unit_tests/teleporters.dm | 10 ++ code/modules/unit_tests/unit_test.dm | 34 +++++- dependencies.sh | 13 +-- tools/{appveyor => ci}/build.ps1 | 6 +- tools/ci/build_spaceman_dmm.sh | 19 +++ tools/{travis => ci}/build_tgui.sh | 0 tools/{travis => ci}/check_changelogs.sh | 2 +- tools/{travis => ci}/check_filedirs.sh | 2 +- tools/{travis => ci}/check_grep.sh | 42 ++++++- .../travis_config.txt => ci/ci_config.txt} | 2 +- tools/{travis => ci}/dm.sh | 0 tools/{appveyor => ci}/download_byond.sh | 0 tools/{travis => ci}/install_build_tools.sh | 2 - tools/{travis => ci}/install_byond.sh | 0 tools/ci/install_rust_g.sh | 9 ++ tools/ci/install_spaceman_dmm.sh | 19 +++ tools/ci/run_server.sh | 14 +++ tools/{travis => ci}/template_dm_generator.py | 0 tools/travis/build_bsql.sh | 21 ---- tools/travis/check_line_endings.py | 47 -------- tools/travis/install_libmariadb.sh | 15 --- tools/travis/install_rust_g.sh | 8 -- tools/travis/install_spaceman_dmm.sh | 8 -- tools/travis/run_server.sh | 15 --- 65 files changed, 950 insertions(+), 347 deletions(-) create mode 100644 .github/workflows/ci_suite.yml create mode 100644 .github/workflows/docker_publish.yml create mode 100644 .github/workflows/generate_documentation.yml delete mode 100644 .travis.yml delete mode 100644 appveyor.yml create mode 100644 code/modules/unit_tests/README.md create mode 100644 code/modules/unit_tests/card_mismatch.dm create mode 100644 code/modules/unit_tests/combat.dm create mode 100644 code/modules/unit_tests/confusion.dm create mode 100644 code/modules/unit_tests/emoting.dm create mode 100644 code/modules/unit_tests/heretic_knowledge.dm create mode 100644 code/modules/unit_tests/holidays.dm create mode 100644 code/modules/unit_tests/initialize_sanity.dm create mode 100644 code/modules/unit_tests/keybinding_init.dm create mode 100644 code/modules/unit_tests/merge_type.dm create mode 100644 code/modules/unit_tests/pills.dm create mode 100644 code/modules/unit_tests/projectiles.dm create mode 100644 code/modules/unit_tests/reagent_mod_expose.dm create mode 100644 code/modules/unit_tests/reagent_mod_procs.dm create mode 100644 code/modules/unit_tests/serving_tray.dm create mode 100644 code/modules/unit_tests/siunit.dm create mode 100644 code/modules/unit_tests/species_whitelists.dm create mode 100644 code/modules/unit_tests/stomach.dm create mode 100644 code/modules/unit_tests/teleporters.dm rename tools/{appveyor => ci}/build.ps1 (66%) mode change 100644 => 100755 create mode 100755 tools/ci/build_spaceman_dmm.sh rename tools/{travis => ci}/build_tgui.sh (100%) rename tools/{travis => ci}/check_changelogs.sh (64%) rename tools/{travis => ci}/check_filedirs.sh (100%) rename tools/{travis => ci}/check_grep.sh (59%) rename tools/{travis/travis_config.txt => ci/ci_config.txt} (82%) rename tools/{travis => ci}/dm.sh (100%) rename tools/{appveyor => ci}/download_byond.sh (100%) mode change 100644 => 100755 rename tools/{travis => ci}/install_build_tools.sh (88%) rename tools/{travis => ci}/install_byond.sh (100%) create mode 100755 tools/ci/install_rust_g.sh create mode 100755 tools/ci/install_spaceman_dmm.sh create mode 100755 tools/ci/run_server.sh rename tools/{travis => ci}/template_dm_generator.py (100%) delete mode 100755 tools/travis/build_bsql.sh delete mode 100644 tools/travis/check_line_endings.py delete mode 100755 tools/travis/install_libmariadb.sh delete mode 100755 tools/travis/install_rust_g.sh delete mode 100755 tools/travis/install_spaceman_dmm.sh delete mode 100755 tools/travis/run_server.sh diff --git a/.dockerignore b/.dockerignore index 2e6259d23d..9c6ac6b4aa 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,5 @@ .dockerignore .editorconfig -.travis.yml GPLv3.txt LICENSE README.md @@ -26,5 +25,4 @@ tgstation.dyn.rsc libmariadb.dll rust_g.dll BSQL.dll -appveyor.yml Dockerfile diff --git a/.gitattributes b/.gitattributes index c447869d3e..b23dfe6932 100644 --- a/.gitattributes +++ b/.gitattributes @@ -38,5 +38,8 @@ *.dmm text eol=lf merge=dmm *.dmi binary merge=dmi +##Force tab indents on dm files +*.dm whitespace=indent-with-non-tab + ## Force changelog merging to use union html/changelog.html text eol=lf merge=union diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml new file mode 100644 index 0000000000..aaa87fd6d3 --- /dev/null +++ b/.github/workflows/ci_suite.yml @@ -0,0 +1,110 @@ +name: CI Suite +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + run_linters: + name: Run Linters + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup cache + uses: actions/cache@v2 + with: + path: $HOME/SpacemanDMM + key: ${{ runner.os }}-spacemandmm + - name: Install Tools + run: | + pip3 install setuptools + bash tools/ci/install_build_tools.sh + bash tools/ci/install_spaceman_dmm.sh dreamchecker + pip3 install -r tools/mapmerge2/requirements.txt + - name: Run Linters + run: | + bash tools/ci/check_filedirs.sh tgstation.dme + bash tools/ci/check_changelogs.sh + find . -name "*.php" -print0 | xargs -0 -n1 php -l + find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py + bash tools/ci/build_tgui.sh + bash tools/ci/check_grep.sh + python3 tools/mapmerge2/dmi.py --test + ~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1 + - name: Annotate Lints + uses: yogstation13/DreamAnnotate@v1 + if: always() + with: + outputFile: output-annotations.txt + + compile_all_maps: + name: Compile Maps + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup cache + uses: actions/cache@v2 + with: + path: $HOME/BYOND + key: ${{ runner.os }}-byond + - name: Compile All Maps + run: | + bash tools/ci/install_byond.sh + source $HOME/BYOND/byond/bin/byondsetup + python3 tools/ci/template_dm_generator.py + bash tools/ci/dm.sh -DCIBUILDING -DCITESTING -DALL_MAPS tgstation.dme + run_all_tests: + name: Integration Tests + runs-on: ubuntu-latest + services: + mysql: + image: mysql:latest + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - uses: actions/checkout@v2 + - name: Setup cache + uses: actions/cache@v2 + with: + path: $HOME/BYOND + key: ${{ runner.os }}-byond + - name: Setup database + run: | + sudo systemctl start mysql + mysql -u root -proot -e 'CREATE DATABASE tg_ci;' + mysql -u root -proot tg_ci < SQL/tgstation_schema.sql + mysql -u root -proot -e 'CREATE DATABASE tg_ci_prefixed;' + mysql -u root -proot tg_ci_prefixed < SQL/tgstation_schema_prefixed.sql + - name: Install rust-g + run: | + sudo dpkg --add-architecture i386 + sudo apt update || true + sudo apt install libssl1.1:i386 + bash tools/ci/install_rust_g.sh + - name: Compile and run tests + run: | + bash tools/ci/install_byond.sh + source $HOME/BYOND/byond/bin/byondsetup + bash tools/ci/dm.sh -DCIBUILDING tgstation.dme + bash tools/ci/run_server.sh + test_windows: + name: Windows Build + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Compile + run: pwsh tools/ci/build.ps1 + - name: Create artifact + run: | + md deploy + bash tools/deploy.sh ./deploy + - name: Deploy artifact + uses: actions/upload-artifact@v2 + with: + name: deploy + path: deploy diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml index 4fd396f133..c8756f0d28 100644 --- a/.github/workflows/compile_changelogs.yml +++ b/.github/workflows/compile_changelogs.yml @@ -15,7 +15,7 @@ jobs: CHANGELOG_ENABLER: ${{ secrets.CHANGELOG_ENABLER }} run: | unset SECRET_EXISTS - if [-n $CHANGELOG_ENABLER]; then SECRET_EXISTS='true' ; fi + if [ -n $CHANGELOG_ENABLER ]; then SECRET_EXISTS='true' ; fi echo ::set-output name=CL_ENABLED::${SECRET_EXISTS} - name: "Setup python" if: steps.value_holder.outputs.CL_ENABLED diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml new file mode 100644 index 0000000000..7417a382c4 --- /dev/null +++ b/.github/workflows/docker_publish.yml @@ -0,0 +1,22 @@ +name: Docker Build + +on: + push: + branches: + - master + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Build and Publish Docker Image to Registry + uses: elgohr/Publish-Docker-Github-Action@master + with: + name: tgstation/tgstation + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + dockerfile: Dockerfile + tags: "latest" + cache: true diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml new file mode 100644 index 0000000000..9407693d4f --- /dev/null +++ b/.github/workflows/generate_documentation.yml @@ -0,0 +1,30 @@ +name: Generate documentation +on: + push: + branches: + - master +jobs: + generate_documentation: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup cache + uses: actions/cache@v2 + with: + path: $HOME/SpacemanDMM + key: ${{ runner.os }}-spacemandmm + - name: Install SpacemanDMM + run: bash tools/ci/install_spaceman_dmm.sh dmdoc + - name: Generate documentation + run: | + ~/dmdoc + touch dmdoc/.nojekyll + echo codedocs.tgstation13.org > dmdoc/CNAME + - name: Deploy + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + BRANCH: gh-pages + CLEAN: true + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SINGLE_COMMIT: true + FOLDER: dmdoc diff --git a/.github/workflows/update_tgs_dmapi.yml b/.github/workflows/update_tgs_dmapi.yml index 6fe53f700c..c7bb5c970c 100644 --- a/.github/workflows/update_tgs_dmapi.yml +++ b/.github/workflows/update_tgs_dmapi.yml @@ -26,6 +26,7 @@ jobs: library-path: 'code/modules/tgs' - name: Commit and Push + continue-on-error: true run: | git config user.name tgstation-server git config user.email tgstation-server@users.noreply.github.com @@ -35,6 +36,7 @@ jobs: - name: Create Pull Request uses: repo-sync/pull-request@v2 + if: ${{ success() }} with: source_branch: "tgs-dmapi-update" destination_branch: "master" @@ -42,4 +44,4 @@ jobs: pr_body: "This pull request updates the TGS DMAPI to the latest version. Please note any breaking or unimplemented changes before merging." pr_label: "Tools" pr_allow_empty: false - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.TGS_UPDATER }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 83ef6fa8ff..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,102 +0,0 @@ -language: generic -os: linux -dist: xenial - -branches: - except: - - ___TGS3TempBranch - - ___TGSTempBranch - -jobs: - include: - - name: "Run Linters" - addons: - apt: - packages: - - python3 - - python3-pip - - python3-setuptools - - pcregrep - - rustc - - cargo - cache: - directories: - - $HOME/SpacemanDMM - install: - - tools/travis/install_build_tools.sh - - tools/travis/install_spaceman_dmm.sh dreamchecker - script: - - tools/travis/check_filedirs.sh tgstation.dme - - tools/travis/check_changelogs.sh - - find . -name "*.php" -print0 | xargs -0 -n1 php -l - - find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py - - tools/travis/build_tgui.sh - - tools/travis/check_grep.sh - - python3 tools/travis/check_line_endings.py - - ~/dreamchecker - - - name: "Compile All Maps" - addons: - apt: - packages: - - libstdc++6:i386 - cache: - directories: - - $HOME/BYOND - install: - - tools/travis/install_byond.sh - - source $HOME/BYOND/byond/bin/byondsetup - before_script: - - tools/travis/template_dm_generator.py - script: - - tools/travis/dm.sh -DTRAVISBUILDING -DTRAVISTESTING -DALL_MAPS tgstation.dme - - - name: "Compile and Run Tests" - addons: - mariadb: '10.2' - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - libstdc++6:i386 - - gcc-multilib - - g++-7 - - g++-7-multilib - - libmariadb-client-lgpl-dev:i386 - - libmariadbd-dev - cache: - directories: - - $HOME/BYOND - - $HOME/libmariadb - install: - - tools/travis/install_byond.sh - - source $HOME/BYOND/byond/bin/byondsetup - - tools/travis/install_libmariadb.sh - - tools/travis/install_rust_g.sh - before_script: - - mysql -u root -e 'CREATE DATABASE tg_travis;' - - mysql -u root tg_travis < SQL/tgstation_schema.sql - - mysql -u root -e 'CREATE DATABASE tg_travis_prefixed;' - - mysql -u root tg_travis_prefixed < SQL/tgstation_schema_prefixed.sql - - tools/travis/build_bsql.sh - script: - - tools/travis/dm.sh -DTRAVISBUILDING tgstation.dme || travis_terminate 1 - - tools/travis/run_server.sh - - # - name: "Generate Documentation" - # # Only run for non-PR commits to the real master branch. - # if: branch = master AND head_branch IS blank - # install: - # - tools/travis/install_spaceman_dmm.sh dmdoc - # before_script: - # # Travis checks out a hash, try to get back on a branch. - # - git checkout $TRAVIS_BRANCH || true - # script: - # - ~/dmdoc - # - touch dmdoc/.nojekyll - # deploy: - # provider: pages - # skip_cleanup: true - # local_dir: dmdoc - # token: $DMDOC_GITHUB_TOKEN - # fqdn: codedocs.tgstation13.org diff --git a/Dockerfile b/Dockerfile index e8a5f44908..cca7e43a54 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,12 @@ -FROM tgstation/byond:513.1508 as base +FROM tgstation/byond:513.1533 as base -FROM base as build_base +FROM base as rust_g RUN apt-get update \ && apt-get install -y --no-install-recommends \ git \ ca-certificates -FROM build_base as rust_g - WORKDIR /rust_g RUN apt-get install -y --no-install-recommends \ @@ -27,36 +25,6 @@ RUN /bin/bash -c "source dependencies.sh \ && git checkout FETCH_HEAD \ && ~/.cargo/bin/cargo build --release -FROM build_base as bsql - -WORKDIR /bsql - -RUN apt-get install -y --no-install-recommends software-properties-common \ - && add-apt-repository ppa:ubuntu-toolchain-r/test \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - cmake \ - make \ - g++-7 \ - libmariadb-client-lgpl-dev \ - && git init \ - && git remote add origin https://github.com/tgstation/BSQL - -COPY dependencies.sh . - -RUN /bin/bash -c "source dependencies.sh \ - && git fetch --depth 1 origin \$BSQL_VERSION" \ - && git checkout FETCH_HEAD - -WORKDIR /bsql/artifacts - -ENV CC=gcc-7 CXX=g++-7 - -RUN ln -s /usr/include/mariadb /usr/include/mysql \ - && ln -s /usr/lib/i386-linux-gnu /root/MariaDB \ - && cmake .. \ - && make - FROM base as dm_base WORKDIR /tgstation @@ -65,26 +33,30 @@ FROM dm_base as build COPY . . -RUN DreamMaker -max_errors 0 tgstation.dme && tools/deploy.sh /deploy +RUN DreamMaker -max_errors 0 tgstation.dme \ + && tools/deploy.sh /deploy \ + && rm /deploy/*.dll FROM dm_base EXPOSE 1337 RUN apt-get update \ + && apt-get install -y --no-install-recommends software-properties-common \ + && add-apt-repository ppa:ubuntu-toolchain-r/test \ + && apt-get update \ + && apt-get upgrade -y \ + && apt-get dist-upgrade -y \ && apt-get install -y --no-install-recommends \ + libmariadb2 \ mariadb-client \ libssl1.0.0 \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /root/.byond/bin COPY --from=rust_g /rust_g/target/release/librust_g.so /root/.byond/bin/rust_g -COPY --from=bsql /bsql/artifacts/src/BSQL/libBSQL.so ./ COPY --from=build /deploy ./ -#bsql fexists memes -RUN ln -s /tgstation/libBSQL.so /root/.byond/bin/libBSQL.so - VOLUME [ "/tgstation/config", "/tgstation/data" ] ENTRYPOINT [ "DreamDaemon", "tgstation.dmb", "-port", "1337", "-trusted", "-close", "-verbose" ] diff --git a/SpacemanDMM.toml b/SpacemanDMM.toml index b827472254..81aff0d557 100644 --- a/SpacemanDMM.toml +++ b/SpacemanDMM.toml @@ -1,2 +1,9 @@ [langserver] dreamchecker = true + +[code_standards] +disallow_relative_type_definitions = true +disallow_relative_proc_definitions = true + +[dmdoc] +use_typepath_names = true diff --git a/TGS3.json b/TGS3.json index 39b75bd913..228854a166 100644 --- a/TGS3.json +++ b/TGS3.json @@ -1,22 +1,9 @@ { "documentation": "/tg/station server 3 configuration file", - "changelog": { - "script": "tools/ss13_genchangelog.py", - "arguments": "html/changelog.html html/changelogs", - "pip_dependancies": [ - "PyYaml", - "beautifulsoup4" - ] - }, - "synchronize_paths": [ - "html/changelog.html", - "html/changelogs/*" - ], + "synchronize_paths": [], "static_directories": [ "config", "data" ], - "dlls": [ - "libmariadb.dll" - ] + "dlls": [] } diff --git a/_maps/_basemap.dm b/_maps/_basemap.dm index 213211fc42..bf5b4f7d49 100644 --- a/_maps/_basemap.dm +++ b/_maps/_basemap.dm @@ -13,7 +13,7 @@ #include "map_files\BoxStation\BoxStation.dmm" #include "map_files\LambdaStation\lambda.dmm" - #ifdef TRAVISBUILDING + #ifdef CIBUILDING #include "templates.dm" #endif #endif diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index c9decee834..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '{build}' -skip_branch_with_pr: true -shallow_clone: true -branches: - except: - - ___TGS3TempBranch - - ___TGSTempBranch -cache: - - C:\byond\ -> dependencies.sh -build_script: - - ps: tools/appveyor/build.ps1 - - ps: "$deployPath = $env:APPVEYOR_BUILD_FOLDER + '/deploy'; bash tools/deploy.sh $deployPath" - - ps: "[System.IO.Compression.ZipFile]::CreateFromDirectory($env:APPVEYOR_BUILD_FOLDER + '/deploy', $env:APPVEYOR_BUILD_FOLDER + '/deploy.zip')" -artifacts: - - path: deploy.zip diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index b92928190c..a1080a1064 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -32,7 +32,7 @@ #define testing(msg) #endif -#ifdef UNIT_TESTS +#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) /proc/log_test(text) WRITE_LOG(GLOB.test_log, text) SEND_TEXT(world.log, text) diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 3739ce006e..64b4129024 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -46,11 +46,11 @@ //Update this whenever you need to take advantage of more recent byond features #define MIN_COMPILER_VERSION 513 -#define MIN_COMPILER_BUILD 1508 +#define MIN_COMPILER_BUILD 1514 #if DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD //Don't forget to update this part #error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update. -#error You need version 513.1508 or higher +#error You need version 513.1514 or higher #endif //Additional code for the above flags. @@ -62,11 +62,11 @@ #define FIND_REF_NO_CHECK_TICK #endif -#ifdef TRAVISBUILDING +#ifdef CIBUILDING #define UNIT_TESTS #endif -#ifdef TRAVISTESTING +#ifdef CITESTING #define TESTING #endif diff --git a/code/game/world.dm b/code/game/world.dm index bf04e0b8d1..1eadb810f6 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -95,8 +95,9 @@ GLOBAL_LIST(topic_status_cache) Master.Initialize(10, FALSE, TRUE) - if(TEST_RUN_PARAMETER in params) - HandleTestRun() + #ifdef UNIT_TESTS + HandleTestRun() + #endif /world/proc/InitTgs() TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED) @@ -261,9 +262,10 @@ GLOBAL_LIST(topic_status_cache) TgsReboot() - if(TEST_RUN_PARAMETER in params) - FinishTestRun() - return + #ifdef UNIT_TESTS + FinishTestRun() + return + #endif if(TgsAvailable()) var/do_hard_reboot diff --git a/code/modules/unit_tests/README.md b/code/modules/unit_tests/README.md new file mode 100644 index 0000000000..420c805fbf --- /dev/null +++ b/code/modules/unit_tests/README.md @@ -0,0 +1,70 @@ +# Unit Tests + +## What is unit testing? + +Unit tests are automated code to verify that parts of the game work exactly as they should. For example, [a test to make sure that the amputation surgery actually amputates the limb](https://github.com/tgstation/tgstation/blob/e416283f162b86345a8623125ab866839b1ac40d/code/modules/unit_tests/surgeries.dm#L1-L13). These are ran every time a PR is made, and thus are very helpful for preventing bugs from cropping up in your code that would've otherwise gone unnoticed. For example, would you have thought to check [that beach boys would still work the same after editing pizza](https://github.com/tgstation/tgstation/pull/53641#issuecomment-691384934)? If you value your time, probably not. + +On their most basic level, when `UNIT_TESTS` is defined, all subtypes of `/datum/unit_test` will have their `Run` proc executed. From here, if `Fail` is called at any point, then the tests will report as failed. + +## How do I write one? +1. Find a relevant file. + +All unit test related code is in `code/modules/unit_tests`. If you are adding a new test for a surgery, for example, then you'd open `surgeries.dm`. If a relevant file does not exist, simply create one in this folder, then `#include` it in `_unit_tests.dm`. + +2. Create the unit test. + +To make a new unit test, you simply need to define a `/datum/unit_test`. + +For example, let's suppose that we are creating a test to make sure a proc `square` correctly raises inputs to the power of two. We'd start with first: + +``` +/datum/unit_test/square/Run() +``` + +This defines our new unit test, `/datum/unit_test/square`. Inside this function, we're then going to run through whatever we want to check. Tests provide a few assertion functions to make this easy. For now, we're going to use `TEST_ASSERT_EQUAL`. + +``` +/datum/unit_test/square/Run() + TEST_ASSERT_EQUAL(square(3), 9, "square(3) did not return 9") + TEST_ASSERT_EQUAL(square(4), 16, "square(4) did not return 16") +``` + +As you can hopefully tell, we're simply checking if the output of `square` matches the output we are expecting. If the test fails, it'll report the error message given as well as whatever the actual output was. + +3. Run the unit test + +Open `code/_compile_options.dm` and uncomment the following line. + +``` +//#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between +``` + +Then, run tgstation.dmb in Dream Daemon. Don't bother trying to connect, you won't need to. You'll be able to see the outputs of all the tests. You'll get to see which tests failed and for what reason. If they all pass, you're set! + +## How to think about tests + +Unit tests exist to prevent bugs that would happen in a real game. Thus, they should attempt to emulate the game world wherever possible. For example, the [quick swap sanity test](https://github.com/tgstation/tgstation/blob/e416283f162b86345a8623125ab866839b1ac40d/code/modules/unit_tests/quick_swap_sanity.dm) emulates a *real* scenario of the bug it fixed occurring by creating a character and giving it real items. The unrecommended alternative would be to create special test-only items. This isn't a hard rule, the [reagent method exposure tests](https://github.com/tgstation/tgstation/blob/e416283f162b86345a8623125ab866839b1ac40d/code/modules/unit_tests/reagent_mod_expose.dm) create a test-only reagent for example, but do keep it in mind. + +Unit tests should also be just that--testing *units* of code. For example, instead of having one massive test for reagents, there are instead several smaller tests for testing exposure, metabolization, etc. + +## The unit testing API + +You can find more information about all of these from their respective doc comments, but for a brief overview: + +`/datum/unit_test` - The base for all tests to be ran. Subtypes must override `Run()`. `New()` and `Destroy()` can be used for setup and teardown. To fail, use `Fail(reason)`. + +`/datum/unit_test/proc/allocate(type, ...)` - Allocates an instance of the provided type with the given arguments. Is automatically destroyed when the test is over. Commonly seen in the form of `var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)`. + +`TEST_ASSERT(assertion, reason)` - Stops the unit test and fails if the assertion is not met. For example: `TEST_ASSERT(powered(), "Machine is not powered")`. + +`TEST_ASSERT_EQUAL(a, b, message)` - Same as `TEST_ASSERT`, but checks if `a == b`. If not, gives a helpful message showing what both `a` and `b` were. For example: `TEST_ASSERT_EQUAL(2 + 2, 4, "The universe is falling apart before our eyes!")`. + +`TEST_ASSERT_NOTEQUAL(a, b, message)` - Same as `TEST_ASSERT_EQUAL`, but reversed. + +`TEST_FOCUS(test_path)` - *Only* run the test provided within the parameters. Useful for reducing noise. For example, if we only want to run our example square test, we can add `TEST_FOCUS(/datum/unit_test/square)`. Should *never* be pushed in a pull request--you will be laughed at. + +## Final Notes + +- Writing tests before you attempt to fix the bug can actually speed up development a lot! It means you don't have to go in game and folllow the same exact steps manually every time. This process is known as "TDD" (test driven development). Write the test first, make sure it fails, *then* start work on the fix/feature, and you'll know you're done when your tests pass. If you do try this, do make sure to confirm in a non-testing environment just to double check. +- Make sure that your tests don't accidentally call RNG functions like `prob`. Since RNG is seeded during tests, you may not realize you have until someone else makes a PR and the tests fail! +- Do your best not to change the behavior of non-testing code during tests. While it may sometimes be necessary in the case of situations such as the above, it is still a slippery slope that can lead to the code you're testing being too different from the production environment to be useful. diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 37fa35946c..277ede63d5 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -1,42 +1,80 @@ //include unit test files in this module in this ifdef //Keep this sorted alphabetically -#ifdef UNIT_TESTS +#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) + /// Asserts that a condition is true /// If the condition is not true, fails the test #define TEST_ASSERT(assertion, reason) if (!(assertion)) { return Fail("Assertion failed: [reason || "No reason"]") } /// Asserts that the two parameters passed are equal, fails otherwise /// Optionally allows an additional message in the case of a failure -#define TEST_ASSERT_EQUAL(a, b, message) if ((a) != (b)) { return Fail("Expected [isnull(a) ? "null" : a] to be equal to [isnull(b) ? "null" : b].[message ? " [message]" : ""]") } +#define TEST_ASSERT_EQUAL(a, b, message) do { \ + var/lhs = ##a; \ + var/rhs = ##b; \ + if (lhs != rhs) { \ + return Fail("Expected [isnull(lhs) ? "null" : lhs] to be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]"); \ + } \ +} while (FALSE) + +/// Asserts that the two parameters passed are not equal, fails otherwise +/// Optionally allows an additional message in the case of a failure +#define TEST_ASSERT_NOTEQUAL(a, b, message) do { \ + var/lhs = ##a; \ + var/rhs = ##b; \ + if (lhs == rhs) { \ + return Fail("Expected [isnull(lhs) ? "null" : lhs] to not be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]"); \ + } \ +} while (FALSE) + +/// *Only* run the test provided within the parentheses +/// This is useful for debugging when you want to reduce noise, but should never be pushed +/// Intended to be used in the manner of `TEST_FOCUS(/datum/unit_test/math)` +#define TEST_FOCUS(test_path) ##test_path { focus = TRUE; } #include "anchored_mobs.dm" #include "bespoke_id.dm" -// #include "binary_insert.dm" -// #include "card_mismatch.dm" shame we don't have this! +#include "binary_insert.dm" +// #include "card_mismatch.dm" #include "chain_pull_through_space.dm" -#include "character_saving.dm" +// #include "combat.dm" #include "component_tests.dm" -// #include "confusion.dm" -// #include "keybinding_init.dm" +#include "confusion.dm" +// #include "emoting.dm" +#include "heretic_knowledge.dm" +#include "holidays.dm" +#include "initialize_sanity.dm" +#include "keybinding_init.dm" #include "machine_disassembly.dm" #include "medical_wounds.dm" +#include "merge_type.dm" // #include "metabolizing.dm" // #include "outfit_sanity.dm" +#include "pills.dm" // #include "plantgrowth_tests.dm" -// #include "quick_swap_sanity.dm" - we don't have quick swap yet +#include "projectiles.dm" #include "reagent_id_typos.dm" +#include "reagent_mod_expose.dm" +#include "reagent_mod_procs.dm" #include "reagent_recipe_collisions.dm" #include "resist.dm" -// #include "say.dm" //no saymods, someone update saycode please. -// #include "siunit.dm" +// #include "say.dm" +#include "serving_tray.dm" +#include "siunit.dm" #include "spawn_humans.dm" -// #include "species_whitelists.dm" +#include "species_whitelists.dm" +// #include "stomach.dm" #include "subsystem_init.dm" -// #include "surgeries.dm" // fails at random due to a race condition, commented out for now +#include "surgeries.dm" +#include "teleporters.dm" #include "timer_sanity.dm" #include "unit_test.dm" +/// CIT TESTS +#include "character_saving.dm" + #undef TEST_ASSERT #undef TEST_ASSERT_EQUAL +#undef TEST_ASSERT_NOTEQUAL +#undef TEST_FOCUS #endif diff --git a/code/modules/unit_tests/card_mismatch.dm b/code/modules/unit_tests/card_mismatch.dm new file mode 100644 index 0000000000..506e88f19c --- /dev/null +++ b/code/modules/unit_tests/card_mismatch.dm @@ -0,0 +1,7 @@ +/datum/unit_test/card_mismatch + +/datum/unit_test/card_mismatch/Run() + var/message = checkCardpacks(SStrading_card_game.card_packs) + message += checkCardDatums() + if(message) + Fail(message) diff --git a/code/modules/unit_tests/combat.dm b/code/modules/unit_tests/combat.dm new file mode 100644 index 0000000000..30bad72175 --- /dev/null +++ b/code/modules/unit_tests/combat.dm @@ -0,0 +1,98 @@ +/datum/unit_test/harm_punch/Run() + var/mob/living/carbon/human/puncher = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + + // Avoid all randomness in tests + ADD_TRAIT(puncher, TRAIT_PERFECT_ATTACKER, INNATE_TRAIT) + + puncher.a_intent_change(INTENT_HARM) + victim.attack_hand(puncher) + + TEST_ASSERT(victim.getBruteLoss() > 0, "Victim took no brute damage after being punched") + +/datum/unit_test/harm_melee/Run() + var/mob/living/carbon/human/tider = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + var/obj/item/storage/toolbox/toolbox = allocate(/obj/item/storage/toolbox) + + tider.put_in_active_hand(toolbox, forced = TRUE) + tider.a_intent_change(INTENT_HARM) + victim.attackby(toolbox, tider) + + TEST_ASSERT(victim.getBruteLoss() > 0, "Victim took no brute damage after being hit by a toolbox") + +/datum/unit_test/harm_different_damage/Run() + var/mob/living/carbon/human/attacker = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + var/obj/item/weldingtool/welding_tool = allocate(/obj/item/weldingtool) + + attacker.put_in_active_hand(welding_tool, forced = TRUE) + attacker.a_intent_change(INTENT_HARM) + welding_tool.attack_self(attacker) // Turn it on + victim.attackby(welding_tool, attacker) + + TEST_ASSERT_EQUAL(victim.getBruteLoss(), 0, "Victim took brute damage from a lit welding tool") + TEST_ASSERT(victim.getFireLoss() > 0, "Victim took no burn damage after being hit by a lit welding tool") + +/datum/unit_test/attack_chain + var/attack_hit + var/post_attack_hit + var/pre_attack_hit + +/datum/unit_test/attack_chain/proc/attack_hit() + attack_hit = TRUE + +/datum/unit_test/attack_chain/proc/post_attack_hit() + post_attack_hit = TRUE + +/datum/unit_test/attack_chain/proc/pre_attack_hit() + pre_attack_hit = TRUE + +/datum/unit_test/attack_chain/Run() + var/mob/living/carbon/human/attacker = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + var/obj/item/storage/toolbox/toolbox = allocate(/obj/item/storage/toolbox) + + RegisterSignal(toolbox, COMSIG_ITEM_PRE_ATTACK, .proc/pre_attack_hit) + RegisterSignal(toolbox, COMSIG_ITEM_ATTACK, .proc/attack_hit) + RegisterSignal(toolbox, COMSIG_ITEM_AFTERATTACK, .proc/post_attack_hit) + + attacker.put_in_active_hand(toolbox, forced = TRUE) + attacker.a_intent_change(INTENT_HARM) + toolbox.melee_attack_chain(attacker, victim) + + TEST_ASSERT(pre_attack_hit, "Pre-attack signal was not fired") + TEST_ASSERT(attack_hit, "Attack signal was not fired") + TEST_ASSERT(post_attack_hit, "Post-attack signal was not fired") + +/datum/unit_test/disarm/Run() + var/mob/living/carbon/human/attacker = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + var/obj/item/storage/toolbox/toolbox = allocate(/obj/item/storage/toolbox) + + victim.put_in_active_hand(toolbox, forced = TRUE) + attacker.a_intent_change(INTENT_DISARM) + + var/obj/structure/barricade/dense_object = allocate(/obj/structure/barricade) + + // Attacker --> Victim --> Empty space --> Wall + attacker.forceMove(run_loc_bottom_left) + victim.forceMove(locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z)) + dense_object.forceMove(locate(run_loc_bottom_left.x + 3, run_loc_bottom_left.y, run_loc_bottom_left.z)) + + // First disarm, world should now look like: + // Attacker --> Empty space --> Victim --> Wall + victim.attack_hand(attacker) + + TEST_ASSERT_EQUAL(victim.loc.x, run_loc_bottom_left.x + 2, "Victim wasn't moved back after being pushed") + TEST_ASSERT(!victim.has_status_effect(STATUS_EFFECT_KNOCKDOWN), "Victim was knocked down despite not being against a wall") + TEST_ASSERT_EQUAL(victim.get_active_held_item(), toolbox, "Victim dropped toolbox despite not being against a wall") + + attacker.forceMove(get_step(attacker, EAST)) + + // Second disarm, victim was against wall and should be down + victim.attack_hand(attacker) + + TEST_ASSERT_EQUAL(victim.loc.x, run_loc_bottom_left.x + 2, "Victim was moved after being pushed against a wall") + TEST_ASSERT(victim.has_status_effect(STATUS_EFFECT_KNOCKDOWN), "Victim was not knocked down after being pushed against a wall") + TEST_ASSERT_EQUAL(victim.get_active_held_item(), null, "Victim didn't drop toolbox after being pushed against a wall") diff --git a/code/modules/unit_tests/confusion.dm b/code/modules/unit_tests/confusion.dm new file mode 100644 index 0000000000..8282493c96 --- /dev/null +++ b/code/modules/unit_tests/confusion.dm @@ -0,0 +1,16 @@ +// Checks that the confusion symptom correctly gives, and removes, confusion +/datum/unit_test/confusion_symptom/Run() + var/mob/living/carbon/human/H = allocate(/mob/living/carbon/human) + var/datum/disease/advance/confusion/disease = allocate(/datum/disease/advance/confusion) + var/datum/symptom/confusion/confusion = disease.symptoms[1] + disease.processing = TRUE + disease.update_stage(5) + disease.infect(H, make_copy = FALSE) + confusion.Activate(disease) + TEST_ASSERT(H.get_confusion() > 0, "Human is not confused after getting symptom.") + disease.cure() + TEST_ASSERT_EQUAL(H.get_confusion(), 0, "Human is still confused after curing confusion.") + +/datum/disease/advance/confusion/New() + symptoms += new /datum/symptom/confusion + Refresh() diff --git a/code/modules/unit_tests/emoting.dm b/code/modules/unit_tests/emoting.dm new file mode 100644 index 0000000000..5795ab3437 --- /dev/null +++ b/code/modules/unit_tests/emoting.dm @@ -0,0 +1,25 @@ +/datum/unit_test/emoting + var/emotes_used = 0 + +/datum/unit_test/emoting/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + RegisterSignal(human, COMSIG_MOB_EMOTE, .proc/on_emote_used) + + human.say("*shrug") + TEST_ASSERT_EQUAL(emotes_used, 1, "Human did not shrug") + + human.say("*beep") + TEST_ASSERT_EQUAL(emotes_used, 1, "Human beeped, when that should be restricted to silicons") + + human.setOxyLoss(140) + + TEST_ASSERT(human.stat != CONSCIOUS, "Human is somehow conscious after receiving suffocation damage") + + human.say("*shrug") + TEST_ASSERT_EQUAL(emotes_used, 1, "Human shrugged while unconscious") + + human.say("*deathgasp") + TEST_ASSERT_EQUAL(emotes_used, 2, "Human could not deathgasp while unconscious") + +/datum/unit_test/emoting/proc/on_emote_used() + emotes_used += 1 diff --git a/code/modules/unit_tests/heretic_knowledge.dm b/code/modules/unit_tests/heretic_knowledge.dm new file mode 100644 index 0000000000..a433bce1ec --- /dev/null +++ b/code/modules/unit_tests/heretic_knowledge.dm @@ -0,0 +1,21 @@ +/// This test checks all heretic knowledge nodes - excluding the ones which are unreachable on purpose - and ensures players can reach them in game. +/// If it finds a node that is unreachable, it throws an error. +/datum/unit_test/heretic_knowledge/Run() + ///List of all knowledge excluding the unreachable base types. + var/list/blacklist = list(/datum/eldritch_knowledge/spell,/datum/eldritch_knowledge/curse,/datum/eldritch_knowledge/final,/datum/eldritch_knowledge/summon) + var/list/all_possible_knowledge = subtypesof(/datum/eldritch_knowledge) - blacklist + + var/list/list_to_check = GLOB.heretic_start_knowledge.Copy() + var/i = 0 + while(i < length(list_to_check)) + var/datum/eldritch_knowledge/eldritch_knowledge = allocate(list_to_check[++i]) + for(var/next_knowledge in eldritch_knowledge.next_knowledge) + if(next_knowledge in list_to_check) + continue + list_to_check += next_knowledge + + if(length(all_possible_knowledge) != length(all_possible_knowledge & list_to_check)) + var/list/unreachables = all_possible_knowledge - list_to_check + for(var/X in unreachables) + var/datum/eldritch_knowledge/eldritch_knowledge = X + Fail("[initial(eldritch_knowledge.name)] is unreachable by players! Add it to the blacklist in /code/modules/unit_tests/heretic_knowledge.dm if it is purposeful!") diff --git a/code/modules/unit_tests/holidays.dm b/code/modules/unit_tests/holidays.dm new file mode 100644 index 0000000000..4df5443e2e --- /dev/null +++ b/code/modules/unit_tests/holidays.dm @@ -0,0 +1,33 @@ +// test Jewish holiday +/datum/unit_test/hanukkah_2123/Run() + var/datum/holiday/hebrew/hanukkah/hanukkah = new + TEST_ASSERT(hanukkah.shouldCelebrate(14, DECEMBER, 2123, 2, TUESDAY), "December 14, 2123 was not Hanukkah.") + +// test Islamic holiday +/datum/unit_test/ramadan_2165/Run() + var/datum/holiday/islamic/ramadan/ramadan = new + TEST_ASSERT(ramadan.shouldCelebrate(6, NOVEMBER, 2165, 1, WEDNESDAY), "November 6, 2165 was not Ramadan.") + +// nth day of week +/datum/unit_test/thanksgiving_2020/Run() + var/datum/holiday/nth_week/thanksgiving/thanksgiving = new + TEST_ASSERT(thanksgiving.shouldCelebrate(26, NOVEMBER, 2020, 4, THURSDAY), "November 26, 2020 was not Thanksgiving.") + +// another nth day of week +/datum/unit_test/indigenous_3683/Run() + var/datum/holiday/nth_week/indigenous/indigenous = new + TEST_ASSERT(indigenous.shouldCelebrate(11, OCTOBER, 3683, 2, MONDAY), "October 11, 3683 was not Indigenous Peoples' Day.") + +// plain old simple holiday +/datum/unit_test/hello_2020/Run() + var/datum/holiday/hello/hello = new + TEST_ASSERT(hello.shouldCelebrate(21, NOVEMBER, 2020, 3, SATURDAY), "November 21, 2020 was not Hello day.") + +// holiday which goes across months +/datum/unit_test/new_year_1983/Run() + var/datum/holiday/new_year/new_year = new + TEST_ASSERT(new_year.shouldCelebrate(2, JANUARY, 1983, 1, SUNDAY), "January 2, 1983 was not New Year.") + +/datum/unit_test/moth_week_2020/Run() + var/datum/holiday/moth/moth = new + TEST_ASSERT(moth.shouldCelebrate(19, JULY, 2020, 3, SATURDAY), "July 19, 2020 was not Moth Week.") diff --git a/code/modules/unit_tests/initialize_sanity.dm b/code/modules/unit_tests/initialize_sanity.dm new file mode 100644 index 0000000000..d183f530c8 --- /dev/null +++ b/code/modules/unit_tests/initialize_sanity.dm @@ -0,0 +1,11 @@ +/datum/unit_test/initialize_sanity/Run() + if(length(SSatoms.BadInitializeCalls)) + Fail("Bad Initialize() calls detected. Please read logs.") + var/list/init_failures_to_text = list( + "[BAD_INIT_QDEL_BEFORE]" = "Qdeleted Before Initialized", + "[BAD_INIT_DIDNT_INIT]" = "Did Not Initialize", + "[BAD_INIT_SLEPT]" = "Initialize() Slept", + "[BAD_INIT_NO_HINT]" = "No Initialize() Hint Returned", + ) + for(var/failure in SSatoms.BadInitializeCalls) + log_world("[failure]: [init_failures_to_text["[SSatoms.BadInitializeCalls[failure]]"]]") // You like stacked brackets? diff --git a/code/modules/unit_tests/keybinding_init.dm b/code/modules/unit_tests/keybinding_init.dm new file mode 100644 index 0000000000..2bd2fdee1e --- /dev/null +++ b/code/modules/unit_tests/keybinding_init.dm @@ -0,0 +1,6 @@ +/datum/unit_test/keybinding_init/Run() + for(var/i in subtypesof(/datum/keybinding)) + var/datum/keybinding/KB = i + if(initial(KB.keybind_signal) || !initial(KB.name)) + continue + Fail("[KB.name] does not have a keybind signal defined.") diff --git a/code/modules/unit_tests/machine_disassembly.dm b/code/modules/unit_tests/machine_disassembly.dm index bcc769bcf2..59edb4ae9d 100644 --- a/code/modules/unit_tests/machine_disassembly.dm +++ b/code/modules/unit_tests/machine_disassembly.dm @@ -3,11 +3,10 @@ var/obj/machinery/freezer = allocate(/obj/machinery/atmospherics/components/unary/thermomachine/freezer) var/turf/freezer_location = freezer.loc - freezer_location.ChangeTurf(/turf/open/floor/plasteel) freezer.deconstruct() // Check that the components are created TEST_ASSERT(locate(/obj/item/stock_parts/micro_laser) in freezer_location, "Couldn't find micro-laser when disassembling freezer") // Check that the circuit board itself is created - TEST_ASSERT(locate(/obj/item/circuitboard/machine/thermomachine/freezer) in freezer_location, "Couldn't find the circuit board when disassembling freezer") + TEST_ASSERT(locate(/obj/item/circuitboard/machine/thermomachine) in freezer_location, "Couldn't find the circuit board when disassembling freezer") diff --git a/code/modules/unit_tests/merge_type.dm b/code/modules/unit_tests/merge_type.dm new file mode 100644 index 0000000000..bd653bf126 --- /dev/null +++ b/code/modules/unit_tests/merge_type.dm @@ -0,0 +1,15 @@ +/datum/unit_test/merge_type/Run() + var/list/blacklist = list(/obj/item/stack/sheet, + /obj/item/stack/sheet/mineral, + /obj/item/stack/ore, + /obj/item/stack/spacecash, + /obj/item/stack/license_plates, + /obj/item/stack/tile/mineral, + /obj/item/stack/tile) + + var/list/paths = subtypesof(/obj/item/stack) - blacklist + + for(var/stackpath in paths) + var/obj/item/stack/stack = stackpath + if(!initial(stack.merge_type)) + Fail("([stack]) lacks set merge_type variable!") diff --git a/code/modules/unit_tests/metabolizing.dm b/code/modules/unit_tests/metabolizing.dm index 895762c0ec..b7f8fc4f6a 100644 --- a/code/modules/unit_tests/metabolizing.dm +++ b/code/modules/unit_tests/metabolizing.dm @@ -17,3 +17,22 @@ /datum/unit_test/metabolization/Destroy() SSmobs.ignite() return ..() + +/datum/unit_test/on_mob_end_metabolize/Run() + var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human) + var/obj/item/reagent_containers/pill/pill = allocate(/obj/item/reagent_containers/pill) + var/datum/reagent/drug/methamphetamine/meth = /datum/reagent/drug/methamphetamine + + // Give them enough meth to be consumed in 2 metabolizations + pill.reagents.add_reagent(meth, initial(meth.metabolization_rate) * 1.9) + pill.attack(user, user) + + user.Life() + + TEST_ASSERT(user.reagents.has_reagent(meth), "User does not have meth in their system after consuming it") + TEST_ASSERT(user.has_movespeed_modifier(/datum/movespeed_modifier/reagent/methamphetamine), "User consumed meth, but did not gain movespeed modifier") + + user.Life() + + TEST_ASSERT(!user.reagents.has_reagent(meth), "User still has meth in their system when it should've finished metabolizing") + TEST_ASSERT(!user.has_movespeed_modifier(/datum/movespeed_modifier/reagent/methamphetamine), "User still has movespeed modifier despite not containing any more meth") diff --git a/code/modules/unit_tests/outfit_sanity.dm b/code/modules/unit_tests/outfit_sanity.dm index 235820f9e9..57ce22434e 100644 --- a/code/modules/unit_tests/outfit_sanity.dm +++ b/code/modules/unit_tests/outfit_sanity.dm @@ -30,8 +30,8 @@ CHECK_OUTFIT_SLOT(glasses, ITEM_SLOT_EYES) CHECK_OUTFIT_SLOT(id, ITEM_SLOT_ID) CHECK_OUTFIT_SLOT(suit_store, ITEM_SLOT_SUITSTORE) - CHECK_OUTFIT_SLOT(l_pocket, ITEM_SLOT_POCKET) - CHECK_OUTFIT_SLOT(r_pocket, ITEM_SLOT_POCKET) + CHECK_OUTFIT_SLOT(l_pocket, ITEM_SLOT_LPOCKET) + CHECK_OUTFIT_SLOT(r_pocket, ITEM_SLOT_RPOCKET) if (outfit.backpack_contents || outfit.box) var/list/backpack_contents = outfit.backpack_contents?.Copy() diff --git a/code/modules/unit_tests/pills.dm b/code/modules/unit_tests/pills.dm new file mode 100644 index 0000000000..ed8f64ce95 --- /dev/null +++ b/code/modules/unit_tests/pills.dm @@ -0,0 +1,10 @@ +/datum/unit_test/pills/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + var/obj/item/reagent_containers/pill/iron/pill = allocate(/obj/item/reagent_containers/pill/iron) + + TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/iron), FALSE, "Human somehow has iron before taking pill") + + pill.attack(human, human) + human.Life() + + TEST_ASSERT(human.has_reagent(/datum/reagent/iron), "Human doesn't have iron after taking pill") diff --git a/code/modules/unit_tests/projectiles.dm b/code/modules/unit_tests/projectiles.dm new file mode 100644 index 0000000000..06a8fb0780 --- /dev/null +++ b/code/modules/unit_tests/projectiles.dm @@ -0,0 +1,5 @@ +/datum/unit_test/projectile_movetypes/Run() + for(var/path in typesof(/obj/projectile)) + var/obj/projectile/projectile = path + if(initial(projectile.movement_type) & PHASING) + Fail("[path] has default movement type PHASING. Piercing projectiles should be done using the projectile piercing system, not movement_types!") diff --git a/code/modules/unit_tests/reagent_mod_expose.dm b/code/modules/unit_tests/reagent_mod_expose.dm new file mode 100644 index 0000000000..3fe02e044d --- /dev/null +++ b/code/modules/unit_tests/reagent_mod_expose.dm @@ -0,0 +1,59 @@ +// testing the mob expose procs are working + +/datum/reagent/method_patch_test + name = "method patch test" + +/datum/reagent/method_patch_test/expose_mob(mob/living/target, methods = PATCH, reac_volume, show_message = TRUE) + . = ..() + if(methods & PATCH) + target.health = 90 + if(methods & INJECT) + target.health = 80 + +/datum/unit_test/reagent_mob_expose/Run() + // Life() is handled just by tests + SSmobs.pause() + + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + var/obj/item/reagent_containers/dropper/dropper = allocate(/obj/item/reagent_containers/dropper) + var/obj/item/reagent_containers/food/drinks/drink = allocate(/obj/item/reagent_containers/food/drinks/bottle) + var/obj/item/reagent_containers/pill/patch/patch = allocate(/obj/item/reagent_containers/pill/patch) + var/obj/item/reagent_containers/syringe/syringe = allocate(/obj/item/reagent_containers/syringe) + + // INGEST + TEST_ASSERT_EQUAL(human.fire_stacks, 0, "Human has fire stacks before taking phlogiston") + drink.reagents.add_reagent(/datum/reagent/phlogiston, 10) + drink.attack(human, human) + TEST_ASSERT_EQUAL(human.fire_stacks, 1, "Human does not have fire stacks after taking phlogiston") + human.Life() + TEST_ASSERT(human.fire_stacks > 1, "Human fire stacks did not increase after life tick") + + // TOUCH + dropper.reagents.add_reagent(/datum/reagent/water, 1) + dropper.afterattack(human, human, TRUE) + TEST_ASSERT_EQUAL(human.fire_stacks, 0, "Human still has fire stacks after touching water") + + // VAPOR + TEST_ASSERT_EQUAL(human.drowsyness, 0, "Human is drowsy at the start of testing") + drink.reagents.clear_reagents() + drink.reagents.add_reagent(/datum/reagent/nitrous_oxide, 10) + drink.reagents.trans_to(human, 10, methods = VAPOR) + TEST_ASSERT_NOTEQUAL(human.drowsyness, 0, "Human is not drowsy after exposure to vapors") + + // PATCH + human.health = 100 + TEST_ASSERT_EQUAL(human.health, 100, "Human health did not set properly") + patch.reagents.add_reagent(/datum/reagent/method_patch_test, 1) + patch.self_delay = 0 + patch.attack(human, human) + TEST_ASSERT_EQUAL(human.health, 90, "Human health did not update after patch was applied") + + // INJECT + syringe.reagents.add_reagent(/datum/reagent/method_patch_test, 1) + syringe.mode = SYRINGE_INJECT + syringe.afterattack(human, human, TRUE) + TEST_ASSERT_EQUAL(human.health, 80, "Human health did not update after injection from syringe") + +/datum/unit_test/reagent_mob_expose/Destroy() + SSmobs.ignite() + return ..() diff --git a/code/modules/unit_tests/reagent_mod_procs.dm b/code/modules/unit_tests/reagent_mod_procs.dm new file mode 100644 index 0000000000..a2087f8624 --- /dev/null +++ b/code/modules/unit_tests/reagent_mod_procs.dm @@ -0,0 +1,12 @@ +/datum/unit_test/reagent_mob_procs/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + var/obj/item/food/hotdog/debug/fooditem = allocate(/obj/item/food/hotdog/debug) + + TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human somehow has ketchup before eating") + TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/medicine/epinephrine), FALSE, "Human somehow has epinephrine before injecting") + + fooditem.attack(human, human) + human.reagents.add_reagent(/datum/reagent/medicine/epinephrine, 5) + + TEST_ASSERT(human.has_reagent(/datum/reagent/consumable/ketchup), "Human doesn't have ketchup after eating") + TEST_ASSERT(human.has_reagent(/datum/reagent/medicine/epinephrine), "Human doesn't have epinephrine after injecting") diff --git a/code/modules/unit_tests/say.dm b/code/modules/unit_tests/say.dm index 3fe6675ab4..a7df5ad624 100644 --- a/code/modules/unit_tests/say.dm +++ b/code/modules/unit_tests/say.dm @@ -10,7 +10,6 @@ test(";%Never gonna give you up", "Never gonna give you up", list(MODE_HEADSET = TRUE, MODE_SING = TRUE)) test(".s Gun plz", "Gun plz", list(RADIO_KEY = RADIO_KEY_SECURITY, RADIO_EXTENSION = RADIO_CHANNEL_SECURITY)) test("...What", "...What", list()) - //note to lettern: add the ++, ||, __, and the verb*text checks /datum/unit_test/get_message_mods/proc/test(message, expected_message, list/expected_mods) var/list/mods = list() diff --git a/code/modules/unit_tests/serving_tray.dm b/code/modules/unit_tests/serving_tray.dm new file mode 100644 index 0000000000..00e911ae50 --- /dev/null +++ b/code/modules/unit_tests/serving_tray.dm @@ -0,0 +1,47 @@ +/** + * Check that standard food items fit on the serving tray + */ +/datum/unit_test/servingtray/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + var/obj/structure/table/the_table = allocate(/obj/structure/table) + var/obj/item/storage/bag/tray/test_tray = allocate(/obj/item/storage/bag/tray) + var/obj/item/reagent_containers/food/banana = allocate(/obj/item/food/rationpack) + var/obj/item/food/the_bread = allocate(/obj/item/food/breadslice) + var/obj/item/reagent_containers/food/sugarcookie = allocate(/obj/item/food/cookie/sugar) + var/obj/item/clothing/under/jumpsuit = allocate(/obj/item/clothing/under/color/black) + + TEST_ASSERT_EQUAL((the_bread in test_tray.contents), FALSE, "The bread is on the serving tray at test start") + + // set the tray to single item mode the dirty way + var/datum/component/storage/tray_storage = test_tray.GetComponent(/datum/component/storage) + tray_storage.collection_mode = COLLECT_ONE + + test_tray.pre_attack(the_bread, human) + + TEST_ASSERT_EQUAL((the_bread in test_tray.contents), TRUE, "The bread did not get picked up by the serving tray") + + test_tray.pre_attack(banana, human) + + TEST_ASSERT_EQUAL((banana in test_tray.contents), TRUE, "The banana did not get picked up by the serving tray") + + the_table.attackby(test_tray, human) + + TEST_ASSERT_EQUAL(test_tray.contents.len, 0, "The serving tray did not drop all items on hitting the table") + + test_tray.pre_attack(sugarcookie, human) + + TEST_ASSERT_EQUAL((sugarcookie in test_tray.contents), TRUE, "The sugarcookie did not get picked up by the serving tray") + + human.equip_to_slot(jumpsuit, ITEM_SLOT_ICLOTHING) + TEST_ASSERT(human.get_item_by_slot(ITEM_SLOT_ICLOTHING), "Human does not have jumpsuit on") + + human.equip_to_slot(test_tray, ITEM_SLOT_LPOCKET) + TEST_ASSERT(human.get_item_by_slot(ITEM_SLOT_LPOCKET), "Serving tray failed to fit in the Left Pocket") + + human.equip_to_slot(test_tray, ITEM_SLOT_RPOCKET) + TEST_ASSERT(human.get_item_by_slot(ITEM_SLOT_RPOCKET), "Serving tray failed to fit in the Right Pocket") + + test_tray.attack(human, human) + + TEST_ASSERT_EQUAL(test_tray.contents.len, 0, "The serving tray did not drop all items on hitting a human") + diff --git a/code/modules/unit_tests/siunit.dm b/code/modules/unit_tests/siunit.dm new file mode 100644 index 0000000000..3a7a25a98d --- /dev/null +++ b/code/modules/unit_tests/siunit.dm @@ -0,0 +1,15 @@ +/datum/unit_test/siunit/Run() + TEST_ASSERT_EQUAL(siunit(0.5345, "A", 0), "535 mA", "") + TEST_ASSERT_EQUAL(siunit(0.5344, "A", 0), "534 mA", "") + TEST_ASSERT_EQUAL(siunit(-0.5344, "A", 0), "-534 mA", "") + TEST_ASSERT_EQUAL(siunit_pressure(1.234, 1), "1.2 kPa", "") // test for pascal require *10e-3, as the game thinks in kPa, the proc siunit in Pa + TEST_ASSERT_EQUAL(siunit_pressure(1.234, 2), "1.23 kPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(1.234, 3), "1.234 kPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(1, 4), "1 kPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(0), "0 Pa", "") + TEST_ASSERT_EQUAL(siunit_pressure(1e3), "1 MPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(999e3), "999 MPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(999.9e3), "999.9 MPa" , "") + TEST_ASSERT_EQUAL(siunit_pressure(999.9e3, 0), "1 GPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(1e6), "1 GPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(3e17), "300000 PPa", "") diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm index 7189e87277..0500deae0a 100644 --- a/code/modules/unit_tests/spawn_humans.dm +++ b/code/modules/unit_tests/spawn_humans.dm @@ -1,7 +1,7 @@ /datum/unit_test/spawn_humans/Run() - var/locs = block(run_loc_bottom_left, run_loc_top_right) + var/locs = block(run_loc_bottom_left, run_loc_top_right) - for(var/I in 1 to 5) - new /mob/living/carbon/human(pick(locs)) + for(var/I in 1 to 5) + new /mob/living/carbon/human(pick(locs)) - sleep(50) + sleep(50) diff --git a/code/modules/unit_tests/species_whitelists.dm b/code/modules/unit_tests/species_whitelists.dm new file mode 100644 index 0000000000..145f3a259f --- /dev/null +++ b/code/modules/unit_tests/species_whitelists.dm @@ -0,0 +1,5 @@ +/datum/unit_test/species_whitelist_check/Run() + for(var/typepath in subtypesof(/datum/species)) + var/datum/species/S = typepath + if(initial(S.changesource_flags) == NONE) + Fail("A species type was detected with no changesource flags: [S]") diff --git a/code/modules/unit_tests/stomach.dm b/code/modules/unit_tests/stomach.dm new file mode 100644 index 0000000000..06fdc71dd4 --- /dev/null +++ b/code/modules/unit_tests/stomach.dm @@ -0,0 +1,40 @@ +/datum/unit_test/stomach/Run() + + // Pause natural mob life so it can be handled entirely by the test + SSmobs.pause() + + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + var/obj/item/food/hotdog/debug/fooditem = allocate(/obj/item/food/hotdog/debug) + var/obj/item/organ/stomach/belly = human.getorganslot(ORGAN_SLOT_STOMACH) + var/obj/item/reagent_containers/pill/pill = allocate(/obj/item/reagent_containers/pill) + var/datum/reagent/drug/methamphetamine/meth = /datum/reagent/drug/methamphetamine + + TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human somehow has ketchup before eating") + + fooditem.attack(human, human) + + TEST_ASSERT(belly.reagents.has_reagent(/datum/reagent/consumable/ketchup), "Stomach doesn't have ketchup after eating") + TEST_ASSERT_EQUAL(human.reagents.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human body has ketchup after eating it should only be in the stomach") + + //Give them meth and let it kick in + pill.reagents.add_reagent(meth, initial(meth.metabolization_rate) * 1.9) + pill.attack(human, human) + human.Life() + + TEST_ASSERT(human.reagents.has_reagent(meth), "Human body does not have meth after life tick") + TEST_ASSERT(human.has_movespeed_modifier(/datum/movespeed_modifier/reagent/methamphetamine), "Human consumed meth, but did not gain movespeed modifier") + + belly.Remove(human) + human.reagents.remove_all(human.reagents.total_volume) + + TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human has reagents after clearing") + + fooditem.attack(human, human) + + TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human has ketchup without a stomach") + + + +/datum/unit_test/stomach/Destroy() + SSmobs.ignite() + return ..() diff --git a/code/modules/unit_tests/surgeries.dm b/code/modules/unit_tests/surgeries.dm index 7b8145ac19..b9ebb586f3 100644 --- a/code/modules/unit_tests/surgeries.dm +++ b/code/modules/unit_tests/surgeries.dm @@ -27,6 +27,33 @@ TEST_ASSERT(!patient.has_trauma_type(), "Patient kept their brain trauma after brain surgery") TEST_ASSERT(patient.getOrganLoss(ORGAN_SLOT_BRAIN) < 20, "Patient did not heal their brain damage after brain surgery") +/datum/unit_test/head_transplant/Run() + var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/alice = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/bob = allocate(/mob/living/carbon/human) + + alice.fully_replace_character_name(null, "Alice") + bob.fully_replace_character_name(null, "Bob") + + var/obj/item/bodypart/head/alices_head = alice.get_bodypart(BODY_ZONE_HEAD) + alices_head.drop_limb() + + var/obj/item/bodypart/head/bobs_head = bob.get_bodypart(BODY_ZONE_HEAD) + bobs_head.drop_limb() + + TEST_ASSERT_EQUAL(alice.get_bodypart(BODY_ZONE_HEAD), null, "Alice still has a head after dismemberment") + TEST_ASSERT_EQUAL(alice.get_visible_name(), "Unknown", "Alice's head was dismembered, but they are not Unknown") + + TEST_ASSERT_EQUAL(bobs_head.real_name, "Bob", "Bob's head does not remember that it is from Bob") + + // Put Bob's head onto Alice's body + var/datum/surgery_step/add_prosthetic/add_prosthetic = new + user.put_in_active_hand(bobs_head) + add_prosthetic.success(user, alice, BODY_ZONE_HEAD, bobs_head) + + TEST_ASSERT(!isnull(alice.get_bodypart(BODY_ZONE_HEAD)), "Alice has no head after prosthetic replacement") + TEST_ASSERT_EQUAL(alice.get_visible_name(), "Bob", "Bob's head was transplanted onto Alice's body, but their name is not Bob") + /datum/unit_test/multiple_surgeries/Run() var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human) var/mob/living/carbon/human/patient_zero = allocate(/mob/living/carbon/human) @@ -41,8 +68,6 @@ TEST_ASSERT(surgery_for_zero.step_in_progress, "Surgery on patient zero was not initiated") var/datum/surgery/organ_manipulation/surgery_for_one = new - - sleep(0.2) // if we don't have this, then the next surgery step can start *before* the previous one does, which is no good // Without waiting for the incision to complete, try to start a new surgery TEST_ASSERT(!surgery_step.initiate(user, patient_one, BODY_ZONE_CHEST, scalpel, surgery_for_one), "Was allowed to start a second surgery without the rod of asclepius") diff --git a/code/modules/unit_tests/teleporters.dm b/code/modules/unit_tests/teleporters.dm new file mode 100644 index 0000000000..fa2624adaa --- /dev/null +++ b/code/modules/unit_tests/teleporters.dm @@ -0,0 +1,10 @@ +/datum/unit_test/auto_teleporter_linking/Run() + // Put down the teleporter machinery + var/obj/machinery/teleport/hub/hub = allocate(/obj/machinery/teleport/hub) + var/obj/machinery/teleport/station/station = allocate(/obj/machinery/teleport/station, locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z)) + var/obj/machinery/computer/teleporter/computer = allocate(/obj/machinery/computer/teleporter, locate(run_loc_bottom_left.x + 2, run_loc_bottom_left.y, run_loc_bottom_left.z)) + + TEST_ASSERT_EQUAL(hub.power_station, station, "Hub didn't link to the station") + TEST_ASSERT_EQUAL(station.teleporter_console, computer, "Station didn't link to the teleporter console") + TEST_ASSERT_EQUAL(station.teleporter_hub, hub, "Station didn't link to the hub") + TEST_ASSERT_EQUAL(computer.power_station, station, "Teleporter console didn't link to the hub") diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index 36b406e75e..15fe6b466c 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -1,9 +1,14 @@ /* + Usage: Override /Run() to run your test code + Call Fail() to fail the test (You should specify a reason) + You may use /New() and /Destroy() for setup/teardown respectively + You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testing + */ GLOBAL_DATUM(current_test, /datum/unit_test) @@ -14,19 +19,33 @@ GLOBAL_VAR(test_log) //Bit of metadata for the future maybe var/list/procs_tested - //usable vars + /// The bottom left turf of the testing zone var/turf/run_loc_bottom_left + + /// The top right turf of the testing zone var/turf/run_loc_top_right + /// The type of turf to allocate for the testing zone + var/test_turf_type = /turf/open/floor/plasteel + //internal shit + var/focus = FALSE var/succeeded = TRUE var/list/allocated var/list/fail_reasons + var/static/datum/turf_reservation/turf_reservation + /datum/unit_test/New() + if (isnull(turf_reservation)) + turf_reservation = SSmapping.RequestBlockReservation(5, 5) + + for (var/turf/reserved_turf in turf_reservation.reserved_turfs) + reserved_turf.ChangeTurf(test_turf_type) + allocated = new - run_loc_bottom_left = locate(1, 1, 1) - run_loc_top_right = locate(5, 5, 1) + run_loc_bottom_left = locate(turf_reservation.bottom_left_coords[1], turf_reservation.bottom_left_coords[2], turf_reservation.bottom_left_coords[3]) + run_loc_top_right = locate(turf_reservation.top_right_coords[1], turf_reservation.top_right_coords[2], turf_reservation.top_right_coords[3]) /datum/unit_test/Destroy() //clear the test area @@ -61,7 +80,14 @@ GLOBAL_VAR(test_log) /proc/RunUnitTests() CHECK_TICK - for(var/I in subtypesof(/datum/unit_test)) + var/tests_to_run = subtypesof(/datum/unit_test) + for (var/_test_to_run in tests_to_run) + var/datum/unit_test/test_to_run = _test_to_run + if (initial(test_to_run.focus)) + tests_to_run = list(test_to_run) + break + + for(var/I in tests_to_run) var/datum/unit_test/test = new I GLOB.current_test = test diff --git a/dependencies.sh b/dependencies.sh index 75e49f3fe1..e8709d10b1 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -11,16 +11,13 @@ export BYOND_MINOR=${LIST[1]} unset LIST #rust_g git tag -export RUST_G_VERSION=0.4.4 - -#bsql git tag -export BSQL_VERSION=v1.4.0.0 +export RUST_G_VERSION=0.4.7 #node version export NODE_VERSION=12 -# PHP version -export PHP_VERSION=5.6 - # SpacemanDMM git tag -export SPACEMAN_DMM_VERSION=suite-1.4 +export SPACEMAN_DMM_VERSION=suite-1.6 + +# Extools git tag +export EXTOOLS_VERSION=v0.0.6 diff --git a/tools/appveyor/build.ps1 b/tools/ci/build.ps1 old mode 100644 new mode 100755 similarity index 66% rename from tools/appveyor/build.ps1 rename to tools/ci/build.ps1 index 6bda079977..8c9906dfe5 --- a/tools/appveyor/build.ps1 +++ b/tools/ci/build.ps1 @@ -1,10 +1,8 @@ if(!(Test-Path -Path "C:/byond")){ - bash tools/appveyor/download_byond.sh + bash tools/ci/download_byond.sh [System.IO.Compression.ZipFile]::ExtractToDirectory("C:/byond.zip", "C:/") Remove-Item C:/byond.zip } -Set-Location $env:APPVEYOR_BUILD_FOLDER - &"C:/byond/bin/dm.exe" -max_errors 0 tgstation.dme -exit $LASTEXITCODE \ No newline at end of file +exit $LASTEXITCODE diff --git a/tools/ci/build_spaceman_dmm.sh b/tools/ci/build_spaceman_dmm.sh new file mode 100755 index 0000000000..d63aeac2cc --- /dev/null +++ b/tools/ci/build_spaceman_dmm.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -euo pipefail + +source dependencies.sh + +cd $HOME/SpacemanDMM + +if [ ! -d .git ] +then + git init + git remote add origin https://github.com/SpaceManiac/SpacemanDMM.git +fi + +git fetch origin --depth=1 $SPACEMAN_DMM_COMMIT_HASH +git reset --hard FETCH_HEAD + +cargo build --release --bin $1 +cp target/release/$1 ~ +~/$1 --version diff --git a/tools/travis/build_tgui.sh b/tools/ci/build_tgui.sh similarity index 100% rename from tools/travis/build_tgui.sh rename to tools/ci/build_tgui.sh diff --git a/tools/travis/check_changelogs.sh b/tools/ci/check_changelogs.sh similarity index 64% rename from tools/travis/check_changelogs.sh rename to tools/ci/check_changelogs.sh index e33c0e6df3..54876d3233 100755 --- a/tools/travis/check_changelogs.sh +++ b/tools/ci/check_changelogs.sh @@ -1,5 +1,5 @@ #!/bin/bash set -euo pipefail -md5sum -c - <<< "49bc6b1b9ed56c83cceb6674bd97cb34 *html/changelogs/example.yml" +md5sum -c - <<< "364a16f858a957486eaeb1e12673c39c *html/changelogs/example.yml" python3 tools/ss13_genchangelog.py html/changelog.html html/changelogs diff --git a/tools/travis/check_filedirs.sh b/tools/ci/check_filedirs.sh similarity index 100% rename from tools/travis/check_filedirs.sh rename to tools/ci/check_filedirs.sh index b7bac4b7a2..59f05d5f5b 100755 --- a/tools/travis/check_filedirs.sh +++ b/tools/ci/check_filedirs.sh @@ -1,4 +1,3 @@ - #!/bin/bash if [ -n "$1" ] then @@ -13,3 +12,4 @@ then echo "ERROR: File DIR was ticked, please untick it, see: https://tgstation13.org/phpBB/viewtopic.php?f=5&t=321 for more" exit 1 fi + diff --git a/tools/travis/check_grep.sh b/tools/ci/check_grep.sh similarity index 59% rename from tools/travis/check_grep.sh rename to tools/ci/check_grep.sh index b85f775eb9..7573398b36 100755 --- a/tools/travis/check_grep.sh +++ b/tools/ci/check_grep.sh @@ -22,13 +22,27 @@ if grep -P 'pixel_[^xy]' _maps/**/*.dmm; then echo "ERROR: incorrect pixel offset variables detected in maps, please remove them." st=1 fi; +echo "Checking for cable varedits" +if grep -P '/obj/structure/cable(/\w+)+\{' _maps/**/*.dmm; then + echo "ERROR: vareditted cables detected, please remove them." + st=1 +fi; if grep -P '\td[1-2] =' _maps/**/*.dmm; then echo "ERROR: d1/d2 cable variables detected in maps, please remove them." st=1 fi; +echo "Checking for pixel_[xy]" +if grep -P 'pixel_[xy] = 0' _maps/**/*.dmm; then + echo "pixel_x/pixel_y = 0 variables detected in maps, please review to ensure they are not dirty varedits." +fi; +echo "Checking for stacked cables" +if grep -P '"\w+" = \(\n([^)]+\n)*/obj/structure/cable,\n([^)]+\n)*/obj/structure/cable,\n([^)]+\n)*/area/.+\)' _maps/**/*.dmm; then + echo "found multiple cables on the same tile, please remove them." + st=1 +fi; if grep -P '^/area/.+[\{]' _maps/**/*.dmm; then - echo "WARNING: Vareditted /area path use detected in maps, please replace with proper paths." - #st=1 + echo "ERROR: Vareditted /area path use detected in maps, please replace with proper paths." + st=1 fi; if grep -P '\W\/turf\s*[,\){]' _maps/**/*.dmm; then echo "ERROR: base /turf path use detected in maps, please replace with proper paths." @@ -38,6 +52,16 @@ if grep -P '^/*var/' code/**/*.dm; then echo "ERROR: Unmanaged global var use detected in code, please use the helpers." st=1 fi; +echo "Checking for space indentation" +if grep -P '(^ {2})|(^ [^ * ])|(^ +)' code/**/*.dm; then + echo "space indentation detected" + st=1 +fi; +echo "Checking for mixed indentation" +if grep -P '^\t+ [^ *]' code/**/*.dm; then + echo "mixed indentation detected" + st=1 +fi; nl=' ' nl=$'\n' @@ -45,9 +69,13 @@ while read f; do t=$(tail -c2 "$f"; printf x); r1="${nl}$"; r2="${nl}${r1}" if [[ ! ${t%x} =~ $r1 ]]; then echo "file $f is missing a trailing newline" - #st=1 + st=1 fi; done < <(find . -type f -name '*.dm') +if grep -P '^/[\w/]\S+\(.*(var/|, ?var/.*).*\)' code/**/*.dm; then + echo "changed files contains proc argument starting with 'var'" + st=1 +fi; if grep -i 'centcomm' code/**/*.dm; then echo "ERROR: Misspelling(s) of CENTCOM detected in code, please remove the extra M(s)." st=1 @@ -56,6 +84,14 @@ if grep -i 'centcomm' _maps/**/*.dmm; then echo "ERROR: Misspelling(s) of CENTCOM detected in maps, please remove the extra M(s)." st=1 fi; +if grep -ni 'nanotransen' code/**/*.dm; then + echo "Misspelling(s) of nanotrasen detected in code, please remove the extra N(s)." + st=1 +fi; +if grep -ni 'nanotransen' _maps/**/*.dmm; then + echo "Misspelling(s) of nanotrasen detected in maps, please remove the extra N(s)." + st=1 +fi; if ls _maps/*.json | grep -P "[A-Z]"; then echo "Uppercase in a map json detected, these must be all lowercase." st=1 diff --git a/tools/travis/travis_config.txt b/tools/ci/ci_config.txt similarity index 82% rename from tools/travis/travis_config.txt rename to tools/ci/ci_config.txt index ff6bf9a793..4925d781bb 100644 --- a/tools/travis/travis_config.txt +++ b/tools/ci/ci_config.txt @@ -1,7 +1,7 @@ SQL_ENABLED ADDRESS 127.0.0.1 PORT 3306 -FEEDBACK_DATABASE tg_travis +FEEDBACK_DATABASE tg_ci FEEDBACK_TABLEPREFIX FEEDBACK_LOGIN root FEEDBACK_PASSWORD diff --git a/tools/travis/dm.sh b/tools/ci/dm.sh similarity index 100% rename from tools/travis/dm.sh rename to tools/ci/dm.sh diff --git a/tools/appveyor/download_byond.sh b/tools/ci/download_byond.sh old mode 100644 new mode 100755 similarity index 100% rename from tools/appveyor/download_byond.sh rename to tools/ci/download_byond.sh diff --git a/tools/travis/install_build_tools.sh b/tools/ci/install_build_tools.sh similarity index 88% rename from tools/travis/install_build_tools.sh rename to tools/ci/install_build_tools.sh index c36cd571ba..6c3e267fca 100755 --- a/tools/travis/install_build_tools.sh +++ b/tools/ci/install_build_tools.sh @@ -10,5 +10,3 @@ npm install --global yarn pip3 install --user PyYaml pip3 install --user beautifulsoup4 - -phpenv global $PHP_VERSION diff --git a/tools/travis/install_byond.sh b/tools/ci/install_byond.sh similarity index 100% rename from tools/travis/install_byond.sh rename to tools/ci/install_byond.sh diff --git a/tools/ci/install_rust_g.sh b/tools/ci/install_rust_g.sh new file mode 100755 index 0000000000..2309f9d952 --- /dev/null +++ b/tools/ci/install_rust_g.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +source dependencies.sh + +mkdir -p ~/.byond/bin +wget -O ~/.byond/bin/librust_g.so "https://github.com/tgstation/rust-g/releases/download/$RUST_G_VERSION/librust_g.so" +chmod +x ~/.byond/bin/librust_g.so +ldd ~/.byond/bin/librust_g.so diff --git a/tools/ci/install_spaceman_dmm.sh b/tools/ci/install_spaceman_dmm.sh new file mode 100755 index 0000000000..d92dca4e95 --- /dev/null +++ b/tools/ci/install_spaceman_dmm.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -euo pipefail + +source dependencies.sh + +if [ ! -f ~/$1 ]; then + mkdir -p "$HOME/SpacemanDMM" + CACHEFILE="$HOME/SpacemanDMM/$1" + + if ! [ -f "$CACHEFILE.version" ] || ! grep -Fxq "$SPACEMAN_DMM_VERSION" "$CACHEFILE.version"; then + wget -O "$CACHEFILE" "https://github.com/SpaceManiac/SpacemanDMM/releases/download/$SPACEMAN_DMM_VERSION/$1" + chmod +x "$CACHEFILE" + echo "$SPACEMAN_DMM_VERSION" >"$CACHEFILE.version" + fi + + ln -s "$CACHEFILE" ~/$1 +fi + +~/$1 --version diff --git a/tools/ci/run_server.sh b/tools/ci/run_server.sh new file mode 100755 index 0000000000..f54c541bf6 --- /dev/null +++ b/tools/ci/run_server.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail + +tools/deploy.sh ci_test +rm ci_test/*.dll +mkdir ci_test/config + +#test config +cp tools/ci/ci_config.txt ci_test/config/config.txt + +cd ci_test +DreamDaemon tgstation.dmb -close -trusted -verbose -params "log-directory=ci" +cd .. +cat ci_test/data/logs/ci/clean_run.lk diff --git a/tools/travis/template_dm_generator.py b/tools/ci/template_dm_generator.py similarity index 100% rename from tools/travis/template_dm_generator.py rename to tools/ci/template_dm_generator.py diff --git a/tools/travis/build_bsql.sh b/tools/travis/build_bsql.sh deleted file mode 100755 index e2b281efc6..0000000000 --- a/tools/travis/build_bsql.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -source dependencies.sh - -mkdir -p BSQL -cd BSQL -git init -git remote add origin https://github.com/tgstation/BSQL -git fetch --depth 1 origin $BSQL_VERSION -git checkout FETCH_HEAD - -mkdir -p artifacts -cd artifacts -export CXX=g++-7 -# The -D will be unnecessary past BSQL v1.4.0.0 -cmake .. -DMARIA_LIBRARY=/usr/lib/i386-linux-gnu/libmariadb.so -make - -mkdir -p ~/.byond/bin -ln -s $PWD/src/BSQL/libBSQL.so ../../libBSQL.so diff --git a/tools/travis/check_line_endings.py b/tools/travis/check_line_endings.py deleted file mode 100644 index fbbbc41426..0000000000 --- a/tools/travis/check_line_endings.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -import glob - -WINDOWS_NEWLINE = b'\r\n' - -FILES_TO_READ = [] -FILES_TO_READ.extend(glob.glob(r"**/*.dm", recursive=True)) -FILES_TO_READ.extend(glob.glob(r"**/*.dmm", recursive=True)) -FILES_TO_READ.extend(glob.glob(r"*.dme")) -#for i in FILES_TO_READ: -# if os.path.isdir(i): -# FILES_TO_READ.remove(i) - -def _reader(filepath): - data = open(filepath, "rb") - return data - -def main(): - filelist = [] - foundfiles = False - - for file in FILES_TO_READ: - data = _reader(file) - lines = data.readlines() - for line in lines: - if line.endswith(WINDOWS_NEWLINE): - filelist.append(file) - foundfiles = True - break - data.close() - - if not foundfiles: - print("No CRLF files found.") - sys.exit(0) - else: - print("Found files with suspected CRLF type.") - for i in filelist: - print(i) - sys.exit(1) - - - -if __name__ == "__main__": - main() diff --git a/tools/travis/install_libmariadb.sh b/tools/travis/install_libmariadb.sh deleted file mode 100755 index d0ce4adc14..0000000000 --- a/tools/travis/install_libmariadb.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# get libmariadb, cache it so limmex doesn't get angery -if [ -f $HOME/libmariadb ]; then - #travis likes to interpret the cache command as it being a file for some reason - rm $HOME/libmariadb -fi -mkdir -p $HOME/libmariadb -if [ ! -f $HOME/libmariadb/libmariadb.so ]; then - wget http://www.byond.com/download/db/mariadb_client-2.0.0-linux.tgz - tar -xvf mariadb_client-2.0.0-linux.tgz - mv mariadb_client-2.0.0-linux/libmariadb.so $HOME/libmariadb/libmariadb.so - rm -rf mariadb_client-2.0.0-linux.tgz mariadb_client-2.0.0-linux -fi diff --git a/tools/travis/install_rust_g.sh b/tools/travis/install_rust_g.sh deleted file mode 100755 index 227034af66..0000000000 --- a/tools/travis/install_rust_g.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -source dependencies.sh - -mkdir -p ~/.byond/bin -wget -O ~/.byond/bin/rust_g "https://github.com/tgstation/rust-g/releases/download/$RUST_G_VERSION/librust_g.so" -chmod +x ~/.byond/bin/rust_g diff --git a/tools/travis/install_spaceman_dmm.sh b/tools/travis/install_spaceman_dmm.sh deleted file mode 100755 index 39464193f8..0000000000 --- a/tools/travis/install_spaceman_dmm.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -euo pipefail - -source dependencies.sh - -wget -O ~/$1 "https://github.com/SpaceManiac/SpacemanDMM/releases/download/$SPACEMAN_DMM_VERSION/$1" -chmod +x ~/$1 -~/$1 --version diff --git a/tools/travis/run_server.sh b/tools/travis/run_server.sh deleted file mode 100755 index 04c491cd5c..0000000000 --- a/tools/travis/run_server.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -euo pipefail - -tools/deploy.sh travis_test -rm travis_test/*.dll -mkdir travis_test/config - -#test config -cp tools/travis/travis_config.txt travis_test/config/config.txt - -cd travis_test -ln -s $HOME/libmariadb/libmariadb.so libmariadb.so -DreamDaemon tgstation.dmb -close -trusted -verbose -params "test-run&log-directory=travis" -cd .. -cat travis_test/data/logs/travis/clean_run.lk