From c1081134d65f238b22d9ab89a28344f81b41bfae Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 13 Apr 2024 20:59:06 -0400 Subject: [PATCH 1/7] feat: add unit testing and standardize to org --- .clang-format | 74 ++++++++ .flake8 | 7 + .github/dependabot.yml | 48 +++++ .github/label-actions.yml | 49 +++++ .github/workflows/auto-create-pr.yml | 35 ++++ .github/workflows/automerge.yml | 64 +++++++ .github/workflows/ci.yml | 149 +++++++++++++++ .github/workflows/codeql.yml | 147 +++++++++++++++ .github/workflows/cpp-lint.yml | 120 ++++++++++++ .github/workflows/issues-stale.yml | 61 ++++++ .github/workflows/issues.yml | 25 +++ .github/workflows/python-flake8.yml | 38 ++++ .github/workflows/yaml-lint.yml | 66 +++++++ .gitignore | 47 +++-- .gitmodules | 4 + CMakeLists.txt | 116 +++++++----- README.md | 13 +- cmake/FindAPPINDICATOR.cmake | 14 +- ...indLIBNOTIFY.cmake => FindLibNotify.cmake} | 0 codecov.yml | 19 ++ .../images/screenshot_linux.png | Bin .../images/screenshot_macosx.png | Bin .../images/screenshot_windows.png | Bin icon.ico => icons/icon.ico | Bin icon.png => icons/icon.png | Bin scripts/requirements.txt | 1 + scripts/update_clang_format.py | 37 ++++ example.c => src/example.c | 0 tray.h => src/tray.h | 0 tray_darwin.m => src/tray_darwin.m | 1 - tray_linux.c => src/tray_linux.c | 0 tray_windows.c => src/tray_windows.c | 1 - tests/CMakeLists.txt | 46 +++++ tests/conftest.cpp | 174 ++++++++++++++++++ tests/unit/test_tray.cpp | 133 +++++++++++++ tests/utils.cpp | 21 +++ tests/utils.h | 11 ++ third-party/googletest | 1 + 38 files changed, 1436 insertions(+), 86 deletions(-) create mode 100644 .clang-format create mode 100644 .flake8 create mode 100644 .github/dependabot.yml create mode 100644 .github/label-actions.yml create mode 100644 .github/workflows/auto-create-pr.yml create mode 100644 .github/workflows/automerge.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/cpp-lint.yml create mode 100644 .github/workflows/issues-stale.yml create mode 100644 .github/workflows/issues.yml create mode 100644 .github/workflows/python-flake8.yml create mode 100644 .github/workflows/yaml-lint.yml create mode 100644 .gitmodules rename cmake/{FindLIBNOTIFY.cmake => FindLibNotify.cmake} (100%) create mode 100644 codecov.yml rename screenshot_linux.png => docs/images/screenshot_linux.png (100%) rename screenshot_macosx.png => docs/images/screenshot_macosx.png (100%) rename screenshot_windows.png => docs/images/screenshot_windows.png (100%) rename icon.ico => icons/icon.ico (100%) rename icon.png => icons/icon.png (100%) create mode 100644 scripts/requirements.txt create mode 100644 scripts/update_clang_format.py rename example.c => src/example.c (100%) rename tray.h => src/tray.h (100%) rename tray_darwin.m => src/tray_darwin.m (99%) rename tray_linux.c => src/tray_linux.c (100%) rename tray_windows.c => src/tray_windows.c (99%) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/conftest.cpp create mode 100644 tests/unit/test_tray.cpp create mode 100644 tests/utils.cpp create mode 100644 tests/utils.h create mode 160000 third-party/googletest diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e72a1e2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,74 @@ +--- +# This file is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlignTrailingComments: false +AlwaysBreakAfterReturnType: All +AlwaysBreakTemplateDeclarations: MultiLine +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterUnion: false + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 2 +Cpp11BracedListStyle: false +UseTab: Never diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..2d028b2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,7 @@ +[flake8] +filename = + *.py, + *.pys +max-line-length = 120 +extend-exclude = + venv/ diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6eb0cda --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,48 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +version: 2 +updates: + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "daily" + time: "08:00" + open-pull-requests-limit: 10 + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + time: "08:30" + open-pull-requests-limit: 10 + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + time: "09:00" + open-pull-requests-limit: 10 + + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "daily" + time: "09:30" + open-pull-requests-limit: 10 + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + time: "10:00" + open-pull-requests-limit: 10 + + - package-ecosystem: "gitsubmodule" + directory: "/" + schedule: + interval: "daily" + time: "10:30" + open-pull-requests-limit: 10 diff --git a/.github/label-actions.yml b/.github/label-actions.yml new file mode 100644 index 0000000..2949601 --- /dev/null +++ b/.github/label-actions.yml @@ -0,0 +1,49 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Configuration for Label Actions - https://github.com/dessant/label-actions + +added: + comment: > + This feature has been added and will be available in the next release. +fixed: + comment: > + This issue has been fixed and will be available in the next release. +invalid:duplicate: + comment: > + :wave: @{issue-author}, this appears to be a duplicate of a pre-existing issue. + close: true + lock: true + unlabel: 'status:awaiting-triage' + +-invalid:duplicate: + reopen: true + unlock: true + +invalid:support: + comment: > + :wave: @{issue-author}, we use the issue tracker exclusively for bug reports. + However, this issue appears to be a support request. Please use our + [Support Center](https://app.lizardbyte.dev/support) for support issues. Thanks. + close: true + lock: true + lock-reason: 'off-topic' + unlabel: 'status:awaiting-triage' + +-invalid:support: + reopen: true + unlock: true + +invalid:template-incomplete: + issues: + comment: > + :wave: @{issue-author}, please edit your issue to complete the template with + all the required info. Your issue will be automatically closed in 5 days if + the template is not completed. Thanks. + prs: + comment: > + :wave: @{issue-author}, please edit your PR to complete the template with + all the required info. Your PR will be automatically closed in 5 days if + the template is not completed. Thanks. diff --git a/.github/workflows/auto-create-pr.yml b/.github/workflows/auto-create-pr.yml new file mode 100644 index 0000000..13705dd --- /dev/null +++ b/.github/workflows/auto-create-pr.yml @@ -0,0 +1,35 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# This workflow creates a PR automatically when anything is merged/pushed into the `nightly` branch. The PR is created +# against the `master` (default) branch. + +name: Auto create PR + +on: + push: + branches: + - 'nightly' + +jobs: + create_pr: + if: startsWith(github.repository, 'LizardByte/') + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create Pull Request + uses: repo-sync/pull-request@v2 + with: + source_branch: "" # should be "nightly" as it's the triggering branch + destination_branch: "master" + pr_title: "Pulling ${{ github.ref_name }} into master" + pr_template: ".github/pr_release_template.md" + pr_assignee: "${{ secrets.GH_BOT_NAME }}" + pr_draft: true + pr_allow_empty: false + github_token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000..733b4de --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,64 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# This workflow will, first, automatically approve PRs created by @LizardByte-bot. Then it will automerge relevant PRs. + +name: Automerge PR + +on: + pull_request: + types: + - opened + - synchronize + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + autoapprove: + if: >- + contains(fromJson('["LizardByte-bot"]'), github.event.pull_request.user.login) && + contains(fromJson('["LizardByte-bot"]'), github.actor) && + startsWith(github.repository, 'LizardByte/') + runs-on: ubuntu-latest + steps: + - name: Autoapproving + uses: hmarr/auto-approve-action@v3 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Label autoapproved + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GH_BOT_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['autoapproved', 'autoupdate'] + }) + + automerge: + if: startsWith(github.repository, 'LizardByte/') + needs: [autoapprove] + runs-on: ubuntu-latest + + steps: + - name: Automerging + uses: pascalgn/automerge-action@v0.15.6 + env: + BASE_BRANCHES: nightly + GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }} + GITHUB_LOGIN: ${{ secrets.GH_BOT_NAME }} + MERGE_LABELS: "!dependencies" + MERGE_METHOD: "squash" + MERGE_COMMIT_MESSAGE: "{pullRequest.title} (#{pullRequest.number})" + MERGE_DELETE_BRANCH: true + MERGE_ERROR_FAIL: true + MERGE_FILTER_AUTHOR: ${{ secrets.GH_BOT_NAME }} + MERGE_RETRIES: "240" # 1 hour + MERGE_RETRY_SLEEP: "15000" # 15 seconds diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a70b346 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,149 @@ +--- +name: CI + +on: + pull_request: + branches: + - master + types: + - opened + - synchronize + - reopened + push: + branches: + - master + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + cmake_generator: "Unix Makefiles" + make_command: "make" + shell: "bash" + - os: ubuntu-latest + appindicator: "libayatana-appindicator3-dev" + cmake_generator: "Unix Makefiles" + make_command: "make" + shell: "bash" + - os: ubuntu-latest + appindicator: "libappindicator3-dev" + cmake_generator: "Unix Makefiles" + make_command: "make" + shell: "bash" + - os: windows-latest + cmake_generator: "MinGW Makefiles" + make_command: "mingw32-make" + shell: "msys2 {0}" + + name: Build (${{ matrix.os }} - ${{ matrix.appindicator || 'default' }}) + runs-on: ${{ matrix.os }} + defaults: + run: + shell: ${{ matrix.shell }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Dependencies Linux + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + ${{ matrix.appindicator }} \ + libglib2.0-dev \ + libnotify-dev \ + xvfb + + - name: Setup Dependencies Windows + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + update: true + install: >- + base-devel + make + mingw-w64-x86_64-binutils + mingw-w64-x86_64-cmake + mingw-w64-x86_64-toolchain + + - name: Setup python + id: setup-python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Python Path + id: python-path + run: | + if [ "${{ runner.os }}" = "Windows" ]; then + # replace backslashes with double backslashes + python_path=$(echo "${{ steps.setup-python.outputs.python-path }}" | sed 's/\\/\\\\/g') + else + python_path=${{ steps.setup-python.outputs.python-path }} + fi + + # step output + echo "python-path=${python_path}" + echo "python-path=${python_path}" >> $GITHUB_OUTPUT + + - name: Build + run: | + mkdir build + cd build + cmake \ + -G "${{ matrix.cmake_generator }}" \ + .. + ${{ matrix.make_command }} -j + + - name: Run tests + id: test + working-directory: build/tests + run: | + if [ "${{ runner.os }}" = "Linux" ]; then + export DISPLAY=:1 + Xvfb ${DISPLAY} -screen 0 1024x768x24 & + fi + + ./test_tray --gtest_color=yes + + - name: Generate gcov report + # any except canceled or skipped + if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure') + id: test_report + working-directory: build + run: | + ${{ steps.python-path.outputs.python-path }} -m pip install gcovr + ${{ steps.python-path.outputs.python-path }} -m gcovr -r .. \ + --exclude '.*tests/.*' \ + --exclude '.*tests/.*' \ + --xml-pretty \ + -o coverage.xml + + - name: Set codecov flags + id: codecov_flags + run: | + flags="${{ runner.os }}" + if [ -n "${{ matrix.appindicator }}" ]; then + flags="${flags},${{ matrix.appindicator }}" + fi + echo "flags=${flags}" >> $GITHUB_OUTPUT + + - name: Upload coverage + # any except canceled or skipped + if: always() && (steps.test_report.outcome == 'success') + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + files: ./build/coverage.xml + flags: "${{ steps.codecov_flags.outputs.flags }}" + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..ae52487 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,147 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# This workflow will analyze all supported languages in the repository using CodeQL Analysis. + +name: "CodeQL" + +on: + push: + branches: ["master", "nightly"] + pull_request: + branches: ["master", "nightly"] + schedule: + - cron: '00 12 * * 0' # every Sunday at 12:00 UTC + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + languages: + name: Get language matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.lang.outputs.result }} + continue: ${{ steps.continue.outputs.result }} + steps: + - name: Get repo languages + uses: actions/github-script@v7 + id: lang + with: + script: | + // CodeQL supports ['cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift'] + // Use only 'java' to analyze code written in Java, Kotlin or both + // Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + // Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + const supported_languages = ['cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift'] + + const remap_languages = { + 'c++': 'cpp', + 'c#': 'csharp', + 'kotlin': 'java', + 'typescript': 'javascript', + } + + const repo = context.repo + const response = await github.rest.repos.listLanguages(repo) + let matrix = { + "include": [] + } + + for (let [key, value] of Object.entries(response.data)) { + // remap language + if (remap_languages[key.toLowerCase()]) { + console.log(`Remapping language: ${key} to ${remap_languages[key.toLowerCase()]}`) + key = remap_languages[key.toLowerCase()] + } + if (supported_languages.includes(key.toLowerCase()) && + !matrix['include'].includes({"language": key.toLowerCase()})) { + console.log(`Found supported language: ${key}`) + matrix['include'].push({"language": key.toLowerCase()}) + } + } + + // print languages + console.log(`matrix: ${JSON.stringify(matrix)}`) + + return matrix + + - name: Continue + uses: actions/github-script@v7 + id: continue + with: + script: | + // if matrix['include'] is an empty list return false, otherwise true + const matrix = ${{ steps.lang.outputs.result }} // this is already json encoded + + if (matrix['include'].length == 0) { + return false + } else { + return true + } + + analyze: + name: Analyze + if: ${{ needs.languages.outputs.continue == 'true' }} + needs: [languages] + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.languages.outputs.matrix) }} + + steps: + - name: Maximize build space + uses: easimon/maximize-build-space@v8 + with: + root-reserve-mb: 20480 + remove-dotnet: ${{ (matrix.language == 'csharp' && 'false') || 'true' }} + remove-android: 'true' + remove-haskell: 'true' + remove-codeql: 'false' + remove-docker-images: 'true' + + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # yamllint disable-line rule:line-length + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # Pre autobuild + # create a file named .codeql-prebuild-${{ matrix.language }}.sh in the root of your repository + - name: Prebuild + run: | + # check if .qodeql-prebuild-${{ matrix.language }}.sh exists + if [ -f "./.codeql-prebuild-${{ matrix.language }}.sh" ]; then + echo "Running .codeql-prebuild-${{ matrix.language }}.sh" + ./.codeql-prebuild-${{ matrix.language }}.sh + fi + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/cpp-lint.yml b/.github/workflows/cpp-lint.yml new file mode 100644 index 0000000..921641c --- /dev/null +++ b/.github/workflows/cpp-lint.yml @@ -0,0 +1,120 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Lint c++ source files and cmake files. + +name: C++ Lint + +on: + pull_request: + branches: [master, nightly] + types: [opened, synchronize, reopened] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + clang-format: + name: Clang Format Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Find cpp files + id: find_files + run: | + # find files + found_files=$(find . -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.m" -o -iname "*.mm") + ignore_files=$(find . -type f -iname ".clang-format-ignore") + + # Loop through each C++ file + for file in $found_files; do + for ignore_file in $ignore_files; do + ignore_directory=$(dirname "$ignore_file") + # if directory of ignore_file is beginning of file + if [[ "$file" == "$ignore_directory"* ]]; then + echo "ignoring file: ${file}" + found_files="${found_files//${file}/}" + break 1 + fi + done + done + + # remove empty lines + found_files=$(echo "$found_files" | sed '/^\s*$/d') + + echo "found cpp files: ${found_files}" + + # do not quote to keep this as a single line + echo found_files=${found_files} >> $GITHUB_OUTPUT + + - name: Clang format lint + if: ${{ steps.find_files.outputs.found_files }} + uses: DoozyX/clang-format-lint-action@v0.16.2 + with: + source: ${{ steps.find_files.outputs.found_files }} + extensions: 'cpp,h,m,mm' + clangFormatVersion: 16 + style: file + inplace: false + + - name: Upload Artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: clang-format-fixes + path: ${{ steps.find_files.outputs.found_files }} + + cmake-lint: + name: CMake Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools cmakelang + + - name: Find cmake files + id: find_files + run: | + # find files + found_files=$(find . -type f -iname "CMakeLists.txt" -o -iname "*.cmake") + ignore_files=$(find . -type f -iname ".cmake-lint-ignore") + + # Loop through each C++ file + for file in $found_files; do + for ignore_file in $ignore_files; do + ignore_directory=$(dirname "$ignore_file") + # if directory of ignore_file is beginning of file + if [[ "$file" == "$ignore_directory"* ]]; then + echo "ignoring file: ${file}" + found_files="${found_files//${file}/}" + break 1 + fi + done + done + + # remove empty lines + found_files=$(echo "$found_files" | sed '/^\s*$/d') + + echo "found cmake files: ${found_files}" + + # do not quote to keep this as a single line + echo found_files=${found_files} >> $GITHUB_OUTPUT + + - name: Test with cmake-lint + run: | + cmake-lint --line-width 120 --tab-size 4 ${{ steps.find_files.outputs.found_files }} diff --git a/.github/workflows/issues-stale.yml b/.github/workflows/issues-stale.yml new file mode 100644 index 0000000..deb3d74 --- /dev/null +++ b/.github/workflows/issues-stale.yml @@ -0,0 +1,61 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Manage stale issues and PRs. + +name: Stale Issues / PRs + +on: + schedule: + - cron: '00 10 * * *' + +jobs: + stale: + name: Check Stale Issues / PRs + if: startsWith(github.repository, 'LizardByte/') + runs-on: ubuntu-latest + steps: + - name: Stale + uses: actions/stale@v9 + with: + close-issue-message: > + This issue was closed because it has been stalled for 10 days with no activity. + close-pr-message: > + This PR was closed because it has been stalled for 10 days with no activity. + days-before-stale: 90 + days-before-close: 10 + exempt-all-assignees: true + exempt-issue-labels: 'added,fixed' + exempt-pr-labels: 'dependencies,l10n' + stale-issue-label: 'stale' + stale-issue-message: > + It seems this issue hasn't had any activity in the past 90 days. + If it's still something you'd like addressed, please let us know by leaving a comment. + Otherwise, to help keep our backlog tidy, we'll be closing this issue in 10 days. Thanks! + stale-pr-label: 'stale' + stale-pr-message: > + It looks like this PR has been idle for 90 days. + If it's still something you're working on or would like to pursue, + please leave a comment or update your branch. + Otherwise, we'll be closing this PR in 10 days to reduce our backlog. Thanks! + repo-token: ${{ secrets.GH_BOT_TOKEN }} + + - name: Invalid Template + uses: actions/stale@v9 + with: + close-issue-message: > + This issue was closed because the the template was not completed after 5 days. + close-pr-message: > + This PR was closed because the the template was not completed after 5 days. + days-before-stale: 0 + days-before-close: 5 + only-labels: 'invalid:template-incomplete' + stale-issue-label: 'invalid:template-incomplete' + stale-issue-message: > + Invalid issues template. + stale-pr-label: 'invalid:template-incomplete' + stale-pr-message: > + Invalid PR template. + repo-token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 0000000..aec6006 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,25 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Label and un-label actions using `../label-actions.yml`. + +name: Issues + +on: + issues: + types: [labeled, unlabeled] + discussion: + types: [labeled, unlabeled] + +jobs: + label: + name: Label Actions + if: startsWith(github.repository, 'LizardByte/') + runs-on: ubuntu-latest + steps: + - name: Label Actions + uses: dessant/label-actions@v4 + with: + github-token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/python-flake8.yml b/.github/workflows/python-flake8.yml new file mode 100644 index 0000000..e08ab10 --- /dev/null +++ b/.github/workflows/python-flake8.yml @@ -0,0 +1,38 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Lint python files with flake8. + +name: flake8 + +on: + pull_request: + branches: [master, nightly] + types: [opened, synchronize, reopened] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + flake8: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 # https://github.com/actions/setup-python + with: + python-version: '3.10' + + - name: Install dependencies + run: | + # pin flake8 before v6.0.0 due to removal of support for type comments (required for Python 2.7 type hints) + python -m pip install --upgrade pip setuptools "flake8<6" + + - name: Test with flake8 + run: | + python -m flake8 --verbose diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml new file mode 100644 index 0000000..7e1fd46 --- /dev/null +++ b/.github/workflows/yaml-lint.yml @@ -0,0 +1,66 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Lint yaml files. + +name: yaml lint + +on: + pull_request: + branches: [master, nightly] + types: [opened, synchronize, reopened] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + yaml-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Find additional files + id: find-files + run: | + # space separated list of files + FILES=.clang-format + + # empty placeholder + FOUND="" + + for FILE in ${FILES}; do + if [ -f "$FILE" ] + then + FOUND="$FOUND $FILE" + fi + done + + echo "found=${FOUND}" >> $GITHUB_OUTPUT + + - name: yaml lint + id: yaml-lint + uses: ibiqlik/action-yamllint@v3 + with: + # https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration + config_data: | + extends: default + rules: + comments: + level: error + line-length: + max: 120 + truthy: + # GitHub uses "on" for workflow event triggers + # .clang-format file has options of "Yes" "No" that will be caught by this, so changed to "warning" + allowed-values: ['true', 'false', 'on'] + check-keys: true + level: warning + file_or_dir: . ${{ steps.find-files.outputs.found }} + + - name: Log + run: | + cat "${{ steps.yaml-lint.outputs.logfile }}" >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index 8ea3744..cab683a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,40 +1,39 @@ -# Object files +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo *.o -*.ko *.obj -*.elf # Precompiled Headers *.gch *.pch -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll +# Compiled Dynamic libraries *.so -*.so.* *.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib # Executables *.exe *.out *.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -# Binary -example -example.exe +# JetBrains IDE +.idea/ -.vscode/ -build/ \ No newline at end of file +# build directories +build/ +cmake-*/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6cbf29c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "third-party/googletest"] + path = third-party/googletest + url = https://github.com/google/googletest.git + branch = v1.14.x diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fecb9f..1df79fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,77 +1,99 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) # target_link_directories -project(tray C) +project(tray + LANGUAGES C + DESCRIPTION "A cross-platform system tray library") -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +set(PROJECT_LICENSE "MIT") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Release' as none was specified.") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) +endif() + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +# options +option(BUILD_TESTS "Build tests" ON) # Generate 'compile_commands.json' for clang_complete set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - find_package (PkgConfig REQUIRED) - -file(GLOB SRCS - ${CMAKE_CURRENT_LIST_DIR}/*.h - ${CMAKE_CURRENT_LIST_DIR}/*.ico - ${CMAKE_CURRENT_LIST_DIR}/*.png) +file(GLOB TRAY_SOURCES + "${CMAKE_SOURCE_DIR}/src/*.h" + "${CMAKE_SOURCE_DIR}/icons/*.ico" + "${CMAKE_SOURCE_DIR}/icons/*.png") if(WIN32) - list(APPEND SRCS ${CMAKE_CURRENT_SOURCE_DIR}/tray_windows.c) + list(APPEND TRAY_SOURCES "${CMAKE_SOURCE_DIR}/src/tray_windows.c") else() - if(UNIX) - if(APPLE) - find_library(COCOA Cocoa REQUIRED) - list(APPEND SRCS ${CMAKE_CURRENT_SOURCE_DIR}/tray_darwin.m) - else() - find_package(APPINDICATOR REQUIRED) - find_package(LIBNOTIFY REQUIRED) - list(APPEND SRCS ${CMAKE_CURRENT_SOURCE_DIR}/tray_linux.c) - endif() - endif() + if(UNIX) + if(APPLE) + find_library(COCOA Cocoa REQUIRED) + list(APPEND TRAY_SOURCES "${CMAKE_SOURCE_DIR}/src/tray_darwin.m") + else() + find_package(APPINDICATOR REQUIRED) + find_package(LibNotify REQUIRED) + list(APPEND TRAY_SOURCES "${CMAKE_SOURCE_DIR}/src/tray_linux.c") + endif() + endif() endif() -add_library(tray STATIC ${SRCS}) -set_property(TARGET tray PROPERTY C_STANDARD 99) +add_library(${PROJECT_NAME} STATIC ${TRAY_SOURCES}) +set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99) + if(WIN32) - target_compile_definitions(tray PRIVATE TRAY_WINAPI=1 WIN32_LEAN_AND_MEAN NOMINMAX) - if(MSVC) - target_compile_options(tray PRIVATE "/MT$<$:d>") - endif() + list(APPEND TRAY_DEFINITIONS TRAY_WINAPI=1 WIN32_LEAN_AND_MEAN NOMINMAX) + if(MSVC) + list(APPEND TRAY_COMPILE_OPTIONS "/MT$<$:d>") + endif() else() - if(UNIX) - if(APPLE) - target_compile_definitions(tray PRIVATE TRAY_APPKIT=1) - target_link_libraries(tray PRIVATE ${COCOA}) - else() - target_compile_options(tray PRIVATE ${APPINDICATOR_CFLAGS}) - target_link_directories(tray PRIVATE ${APPINDICATOR_LIBRARY_DIRS}) - target_compile_definitions(tray PRIVATE TRAY_APPINDICATOR=1) - if(APPINDICATOR_AYATANA) - target_compile_definitions(tray PRIVATE TRAY_AYATANA_APPINDICATOR=1) - endif() - if(APPINDICATOR_LEGACY) - target_compile_definitions(tray PRIVATE TRAY_LEGACY_APPINDICATOR=1) - endif() - target_compile_definitions(tray PRIVATE TRAY_LIBNOTIFY=1) - target_link_libraries(tray PRIVATE ${APPINDICATOR_LIBRARIES} ${LIBNOTIFY_LIBRARIES}) - endif() + if(UNIX) + if(APPLE) + list(APPEND TRAY_DEFINITIONS TRAY_APPKIT=1) + list(APPEND TRAY_EXTERNAL_LIBRARIES ${COCOA}) + else() + list(APPEND TRAY_COMPILE_OPTIONS ${APPINDICATOR_CFLAGS}) + list(APPEND TRAY_EXTERNAL_DIRECTORIES ${APPINDICATOR_LIBRARY_DIRS}) + list(APPEND TRAY_DEFINITIONS TRAY_APPINDICATOR=1) + if(APPINDICATOR_AYATANA) + list(APPEND TRAY_DEFINITIONS TRAY_AYATANA_APPINDICATOR=1) + endif() + if(APPINDICATOR_LEGACY) + list(APPEND TRAY_DEFINITIONS TRAY_LEGACY_APPINDICATOR=1) + endif() + list(APPEND TRAY_LIBNOTIFY=1) + list(APPEND TRAY_EXTERNAL_LIBRARIES ${APPINDICATOR_LIBRARIES} ${LIBNOTIFY_LIBRARIES}) + + include_directories(SYSTEM ${APPINDICATOR_INCLUDE_DIRS} ${LIBNOTIFY_INCLUDE_DIRS}) + link_directories(${APPINDICATOR_LIBRARY_DIRS} ${LIBNOTIFY_LIBRARY_DIRS}) endif() + endif() endif() -add_library(tray::tray ALIAS tray) +add_library(tray::tray ALIAS ${PROJECT_NAME}) -add_executable(tray_example ${CMAKE_CURRENT_SOURCE_DIR}/example.c) +add_executable(tray_example "${CMAKE_SOURCE_DIR}/src/example.c") target_link_libraries(tray_example tray::tray) -configure_file(${CMAKE_CURRENT_LIST_DIR}/icon.ico ${CMAKE_BINARY_DIR}/icon.ico COPYONLY) -configure_file(${CMAKE_CURRENT_LIST_DIR}/icon.png ${CMAKE_BINARY_DIR}/icon.png COPYONLY) +configure_file("${CMAKE_SOURCE_DIR}/icons/icon.ico" "${CMAKE_BINARY_DIR}/icon.ico" COPYONLY) +configure_file("${CMAKE_SOURCE_DIR}/icons/icon.png" "${CMAKE_BINARY_DIR}/icon.png" COPYONLY) INSTALL(TARGETS tray tray DESTINATION lib) IF(NOT WIN32) - INSTALL(FILES tray.h DESTINATION include) + INSTALL(FILES tray.h DESTINATION include) ENDIF() +target_compile_definitions(${PROJECT_NAME} PRIVATE ${TRAY_DEFINITIONS}) +target_compile_options(${PROJECT_NAME} PRIVATE ${TRAY_COMPILE_OPTIONS}) +target_link_directories(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_DIRECTORIES}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_LIBRARIES}) + +# tests +if(BUILD_TESTS) + add_subdirectory(tests) +endif() diff --git a/README.md b/README.md index 08a928f..7359deb 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@ -# Cross-platform Linux/Windows/MacOS Tray +# Cross-platform Linux/macOS/Windows Tray - +[![codecov](https://img.shields.io/codecov/c/gh/LizardByte/tray?token=HSX66JNEOL&style=for-the-badge&logo=codecov&label=codecov)](https://codecov.io/gh/LizardByte/tray) - + - + + + Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu. Works well on: -* Linux/Gtk (libappindicator) +* Linux/Gtk (libayatana-appindicator3 or libappindicator3) * Windows XP or newer (shellapi.h) * MacOS (Cocoa/AppKit) @@ -90,4 +92,3 @@ array must have text field set to NULL. This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php), so feel free to integrate it in your commercial products. - diff --git a/cmake/FindAPPINDICATOR.cmake b/cmake/FindAPPINDICATOR.cmake index 6bfeb66..94f6203 100644 --- a/cmake/FindAPPINDICATOR.cmake +++ b/cmake/FindAPPINDICATOR.cmake @@ -21,14 +21,14 @@ include(FindPackageHandleStandardArgs) -PKG_CHECK_MODULES(APPINDICATOR ayatana-appindicator3-0.1) -IF( APPINDICATOR_FOUND ) +pkg_check_modules(APPINDICATOR ayatana-appindicator3-0.1) +if(APPINDICATOR_FOUND) SET(APPINDICATOR_AYATANA 1) -ELSE() - PKG_CHECK_MODULES(APPINDICATOR appindicator3-0.1) - IF( APPINDICATOR_FOUND ) +else() + pkg_check_modules(APPINDICATOR appindicator3-0.1) + if(APPINDICATOR_FOUND) SET(APPINDICATOR_LEGACY 1) - ENDIF() -ENDIF() + endif() +endif() mark_as_advanced(APPINDICATOR_INCLUDE_DIR APPINDICATOR_LIBRARY) diff --git a/cmake/FindLIBNOTIFY.cmake b/cmake/FindLibNotify.cmake similarity index 100% rename from cmake/FindLIBNOTIFY.cmake rename to cmake/FindLibNotify.cmake diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..e9c9c87 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,19 @@ +--- +codecov: + branch: master + +coverage: + status: + project: + default: + target: auto + threshold: 10% + +comment: + layout: "diff, flags, files" + behavior: default + require_changes: false # if true: only post the comment if coverage changes + +ignore: + - "tests" + - "third-party" diff --git a/screenshot_linux.png b/docs/images/screenshot_linux.png similarity index 100% rename from screenshot_linux.png rename to docs/images/screenshot_linux.png diff --git a/screenshot_macosx.png b/docs/images/screenshot_macosx.png similarity index 100% rename from screenshot_macosx.png rename to docs/images/screenshot_macosx.png diff --git a/screenshot_windows.png b/docs/images/screenshot_windows.png similarity index 100% rename from screenshot_windows.png rename to docs/images/screenshot_windows.png diff --git a/icon.ico b/icons/icon.ico similarity index 100% rename from icon.ico rename to icons/icon.ico diff --git a/icon.png b/icons/icon.png similarity index 100% rename from icon.png rename to icons/icon.png diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000..2d2abbf --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1 @@ +clang-format diff --git a/scripts/update_clang_format.py b/scripts/update_clang_format.py new file mode 100644 index 0000000..d5a90ec --- /dev/null +++ b/scripts/update_clang_format.py @@ -0,0 +1,37 @@ +# standard imports +import os +import subprocess + +# variables +directories = [ + 'src', + 'tests', +] +file_types = [ + 'cpp', + 'h', + 'm', + 'mm' +] + + +def clang_format(file: str): + print(f'Formatting {file} ...') + subprocess.run(['clang-format', '-i', file]) + + +def main(): + """ + Main entry point. + """ + # walk the directories + for directory in directories: + for root, dirs, files in os.walk(directory): + for file in files: + file_path = os.path.join(root, file) + if os.path.isfile(file_path) and file.rsplit('.')[-1] in file_types: + clang_format(file=file_path) + + +if __name__ == '__main__': + main() diff --git a/example.c b/src/example.c similarity index 100% rename from example.c rename to src/example.c diff --git a/tray.h b/src/tray.h similarity index 100% rename from tray.h rename to src/tray.h diff --git a/tray_darwin.m b/src/tray_darwin.m similarity index 99% rename from tray_darwin.m rename to src/tray_darwin.m index 21e9a6d..838dba9 100644 --- a/tray_darwin.m +++ b/src/tray_darwin.m @@ -76,4 +76,3 @@ void tray_update(struct tray *tray) { void tray_exit(void) { [app terminate:app]; } - diff --git a/tray_linux.c b/src/tray_linux.c similarity index 100% rename from tray_linux.c rename to src/tray_linux.c diff --git a/tray_windows.c b/src/tray_windows.c similarity index 99% rename from tray_windows.c rename to src/tray_windows.c index 7176291..cf92da7 100644 --- a/tray_windows.c +++ b/src/tray_windows.c @@ -275,4 +275,3 @@ void tray_exit(void) { PostQuitMessage(0); UnregisterClass(WC_TRAY_CLASS_NAME, GetModuleHandle(NULL)); } - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..453a2fe --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.13) +# https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md#foundational-c-support + +project(test_tray) + +include_directories("${CMAKE_SOURCE_DIR}") + +enable_testing() + +# Add GoogleTest directory to the project +set(GTEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/googletest") +set(INSTALL_GTEST OFF) +set(INSTALL_GMOCK OFF) +add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest") +include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}") + +# coverage +# https://gcovr.com/en/stable/guide/compiling.html#compiler-options +set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage -O1") +set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage -O1") + +# if windows +if (WIN32) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # cmake-lint: disable=C0103 +endif () + +file(GLOB_RECURSE TEST_SOURCES + ${CMAKE_SOURCE_DIR}/tests/conftest.cpp + ${CMAKE_SOURCE_DIR}/tests/utils.cpp + ${CMAKE_SOURCE_DIR}/tests/test_*.cpp) + +add_executable(${PROJECT_NAME} + ${TEST_SOURCES} + ${TRAY_SOURCES}) +set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17) +target_link_directories(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_DIRECTORIES}) +target_link_libraries(${PROJECT_NAME} + ${TRAY_EXTERNAL_LIBRARIES} + gtest + gtest_main) # if we use this we don't need our own main function +target_compile_definitions(${PROJECT_NAME} PUBLIC ${TRAY_DEFINITIONS} ${TEST_DEFINITIONS}) +target_compile_options(${PROJECT_NAME} PRIVATE $<$:${TRAY_COMPILE_OPTIONS}>) +target_link_options(${PROJECT_NAME} PRIVATE) + +add_test(NAME ${PROJECT_NAME} COMMAND tray_test) diff --git a/tests/conftest.cpp b/tests/conftest.cpp new file mode 100644 index 0000000..297edb2 --- /dev/null +++ b/tests/conftest.cpp @@ -0,0 +1,174 @@ +#include +#include +#include + +#include + +// Undefine the original TEST macro +#undef TEST + +// Redefine TEST to use our BaseTest class, to automatically use our BaseTest fixture +#define TEST(test_case_name, test_name) \ + GTEST_TEST_(test_case_name, test_name, ::BaseTest, \ + ::testing::internal::GetTypeId<::BaseTest>()) + +/** + * @brief Base class for tests. + * + * This class provides a base test fixture for all tests. + * + * ``cout``, ``stderr``, and ``stdout`` are redirected to a buffer, and the buffer is printed if the test fails. + * + * @todo Retain the color of the original output. + */ +class BaseTest: public ::testing::Test { +protected: + // https://stackoverflow.com/a/58369622/11214013 + + // we can possibly use some internal googletest functions to capture stdout and stderr, but I have not tested this + // https://stackoverflow.com/a/33186201/11214013 + + BaseTest(): + sbuf { nullptr }, pipe_stdout { nullptr }, pipe_stderr { nullptr } { + // intentionally empty + } + + ~BaseTest() override = default; + + void + SetUp() override { + // todo: only run this one time, instead of every time a test is run + // see: https://stackoverflow.com/questions/2435277/googletest-accessing-the-environment-from-a-test + // get command line args from the test executable + testArgs = ::testing::internal::GetArgvs(); + + // then get the directory of the test executable + // std::string path = ::testing::internal::GetArgvs()[0]; + testBinary = testArgs[0]; + + // get the directory of the test executable + testBinaryDir = std::filesystem::path(testBinary).parent_path(); + + // If testBinaryDir is empty or `.` then set it to the current directory + // maybe some better options here: https://stackoverflow.com/questions/875249/how-to-get-current-directory + if (testBinaryDir.empty() || testBinaryDir.string() == ".") { + testBinaryDir = std::filesystem::current_path(); + } + + sbuf = std::cout.rdbuf(); // save cout buffer (std::cout) + std::cout.rdbuf(cout_buffer.rdbuf()); // redirect cout to buffer (std::cout) + } + + void + TearDown() override { + std::cout.rdbuf(sbuf); // restore cout buffer + + // get test info + const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + + if (test_info->result()->Failed()) { + std::cout << std::endl + << "Test failed: " << test_info->name() << std::endl + << std::endl + << "Captured boost log:" << std::endl + << boost_log_buffer.str() << std::endl + << "Captured cout:" << std::endl + << cout_buffer.str() << std::endl + << "Captured stdout:" << std::endl + << stdout_buffer.str() << std::endl + << "Captured stderr:" << std::endl + << stderr_buffer.str() << std::endl; + } + + sbuf = nullptr; // clear sbuf + if (pipe_stdout) { + pclose(pipe_stdout); + pipe_stdout = nullptr; + } + if (pipe_stderr) { + pclose(pipe_stderr); + pipe_stderr = nullptr; + } + } + + // functions and variables + std::vector testArgs; // CLI arguments used + std::filesystem::path testBinary; // full path of this binary + std::filesystem::path testBinaryDir; // full directory of this binary + std::stringstream boost_log_buffer; // declare boost_log_buffer + std::stringstream cout_buffer; // declare cout_buffer + std::stringstream stdout_buffer; // declare stdout_buffer + std::stringstream stderr_buffer; // declare stderr_buffer + std::streambuf *sbuf; + FILE *pipe_stdout; + FILE *pipe_stderr; + + int + exec(const char *cmd) { + std::array buffer {}; + pipe_stdout = popen((std::string(cmd) + " 2>&1").c_str(), "r"); + pipe_stderr = popen((std::string(cmd) + " 2>&1").c_str(), "r"); + if (!pipe_stdout || !pipe_stderr) { + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe_stdout) != nullptr) { + stdout_buffer << buffer.data(); + } + while (fgets(buffer.data(), buffer.size(), pipe_stderr) != nullptr) { + stderr_buffer << buffer.data(); + } + int returnCode = pclose(pipe_stdout); + pipe_stdout = nullptr; + if (returnCode != 0) { + std::cout << "Error: " << stderr_buffer.str() << std::endl + << "Return code: " << returnCode << std::endl; + } + return returnCode; + } +}; + +class LinuxTest: public BaseTest { +protected: + void + SetUp() override { +#ifndef __linux__ + GTEST_SKIP_("Skipping, this test is for Linux only."); +#endif + } + + void + TearDown() override { + BaseTest::TearDown(); + } +}; + +class MacOSTest: public BaseTest { +protected: + void + SetUp() override { +#if !defined(__APPLE__) || !defined(__MACH__) + GTEST_SKIP_("Skipping, this test is for macOS only."); +#endif + } + + void + TearDown() override { + BaseTest::TearDown(); + } +}; + +class WindowsTest: public BaseTest { +protected: + void + SetUp() override { +#ifndef _WIN32 + GTEST_SKIP_("Skipping, this test is for Windows only."); +#endif + BaseTest::SetUp(); + } + + void + TearDown() override { + BaseTest::TearDown(); + } +}; diff --git a/tests/unit/test_tray.cpp b/tests/unit/test_tray.cpp new file mode 100644 index 0000000..7d55b5c --- /dev/null +++ b/tests/unit/test_tray.cpp @@ -0,0 +1,133 @@ +#include + +#if defined (_WIN32) || defined (_WIN64) +#define TRAY_WINAPI 1 +#elif defined (__linux__) || defined (linux) || defined (__linux) +#define TRAY_APPINDICATOR 1 +#elif defined (__APPLE__) || defined (__MACH__) +#define TRAY_APPKIT 1 +#endif + +#include "src/tray.h" + +#if TRAY_APPINDICATOR +#define TRAY_ICON1 "mail-message-new" +#define TRAY_ICON2 "mail-message-new" +#elif TRAY_APPKIT +#define TRAY_ICON1 "icon.png" +#define TRAY_ICON2 "icon.png" +#elif TRAY_WINAPI +#define TRAY_ICON1 "icon.ico" +#define TRAY_ICON2 "icon.ico" +#endif + +class TrayTest : public BaseTest { +protected: + static struct tray testTray; + + // Static arrays for submenus + static struct tray_menu submenu7_8[]; + static struct tray_menu submenu5_6[]; + static struct tray_menu submenu_second[]; + static struct tray_menu submenu[]; + + // Non-static member functions + static void hello_cb(struct tray_menu *item) { + // Mock implementation + } + static void toggle_cb(struct tray_menu *item) { + item->checked = !item->checked; + tray_update(&testTray); + } + static void quit_cb(struct tray_menu *item) { + tray_exit(); + } + static void submenu_cb(struct tray_menu *item) { + // Mock implementation + tray_update(&testTray); + } + + void SetUp() override { + testTray.icon = TRAY_ICON1; + testTray.tooltip = "TestTray"; + testTray.menu = submenu; + } + + void TearDown() override { + // Clean up any resources if needed + } +}; + +// Define the static arrays +struct tray_menu TrayTest::submenu7_8[] = { + {.text = "7", .cb = submenu_cb}, + {.text = "-"}, + {.text = "8", .cb = submenu_cb}, + {.text = nullptr} +}; +struct tray_menu TrayTest::submenu5_6[] = { + {.text = "5", .cb = submenu_cb}, + {.text = "6", .cb = submenu_cb}, + {.text = nullptr} +}; +struct tray_menu TrayTest::submenu_second[] = { + {.text = "THIRD", .submenu = submenu7_8}, + {.text = "FOUR", .submenu = submenu5_6}, + {.text = nullptr} +}; +struct tray_menu TrayTest::submenu[] = { + {.text = "Hello", .cb = hello_cb}, + {.text = "Checked", .checked = 1, .checkbox = 1, .cb = toggle_cb}, + {.text = "Disabled", .disabled = 1}, + {.text = "-"}, + {.text = "SubMenu", .submenu = submenu_second}, + {.text = "-"}, + {.text = "Quit", .cb = quit_cb}, + {.text = nullptr} +}; +struct tray TrayTest::testTray = { + .icon = TRAY_ICON1, + .tooltip = "TestTray", + .menu = submenu +}; + +TEST_F(TrayTest, TestTrayInit) { + int result = tray_init(&testTray); + EXPECT_EQ(result, 0); // make sure return value is 0 +} + +TEST_F(TrayTest, TestTrayLoop) { + int result = tray_loop(1); + EXPECT_EQ(result, 0); // make sure return value is 0 +} + +TEST_F(TrayTest, TestTrayUpdate) { + // check the initial values + EXPECT_EQ(testTray.icon, TRAY_ICON1); + EXPECT_EQ(testTray.tooltip, "TestTray"); + + // update the values + testTray.icon = TRAY_ICON2; + testTray.tooltip = "TestTray2"; + tray_update(&testTray); + EXPECT_EQ(testTray.icon, TRAY_ICON2); + EXPECT_EQ(testTray.tooltip, "TestTray2"); + + // put back the original values + testTray.icon = TRAY_ICON1; + testTray.tooltip = "TestTray"; + tray_update(&testTray); + EXPECT_EQ(testTray.icon, TRAY_ICON1); + EXPECT_EQ(testTray.tooltip, "TestTray"); +} + +TEST_F(TrayTest, TestToggleCallback) { + bool initialCheckedState = testTray.menu[1].checked; + toggle_cb(&testTray.menu[1]); + EXPECT_EQ(testTray.menu[1].checked, !initialCheckedState); +} + +TEST_F(TrayTest, TestTrayExit) { + tray_exit(); + // TODO: Check the state after tray_exit +} diff --git a/tests/utils.cpp b/tests/utils.cpp new file mode 100644 index 0000000..2ff1bfd --- /dev/null +++ b/tests/utils.cpp @@ -0,0 +1,21 @@ +/** + * @file utils.cpp + * @brief Utility functions + */ + +#include "utils.h" + +/** + * @brief Set an environment variable. + * @param name Name of the environment variable + * @param value Value of the environment variable + * @return 0 on success, non-zero error code on failure + */ +int +setEnv(const std::string &name, const std::string &value) { +#ifdef _WIN32 + return _putenv_s(name.c_str(), value.c_str()); +#else + return setenv(name.c_str(), value.c_str(), 1); +#endif +} diff --git a/tests/utils.h b/tests/utils.h new file mode 100644 index 0000000..f82de91 --- /dev/null +++ b/tests/utils.h @@ -0,0 +1,11 @@ +/** + * @file utils.h + * @brief Reusable functions for tests. + */ + +#pragma once + +#include + +int +setEnv(const std::string &name, const std::string &value); diff --git a/third-party/googletest b/third-party/googletest new file mode 160000 index 0000000..f8d7d77 --- /dev/null +++ b/third-party/googletest @@ -0,0 +1 @@ +Subproject commit f8d7d77c06936315286eb55f8de22cd23c188571 From cd49bebafb2cc88b2633f279acc8c87bc096c373 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:39:03 -0400 Subject: [PATCH 2/7] style: apply clang-format rules --- src/tray.h | 17 ++-- src/tray_darwin.m | 125 ++++++++++++++------------ tests/unit/test_tray.cpp | 190 ++++++++++++++++++++------------------- 3 files changed, 174 insertions(+), 158 deletions(-) diff --git a/src/tray.h b/src/tray.h index 68b5324..c94854e 100644 --- a/src/tray.h +++ b/src/tray.h @@ -2,8 +2,7 @@ #define TRAY_H #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif struct tray_menu; @@ -32,16 +31,20 @@ struct tray_menu { struct tray_menu *submenu; }; -int tray_init(struct tray *tray); +int +tray_init(struct tray *tray); -int tray_loop(int blocking); +int +tray_loop(int blocking); -void tray_update(struct tray *tray); +void +tray_update(struct tray *tray); -void tray_exit(void); +void +tray_exit(void); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif /* TRAY_H */ diff --git a/src/tray_darwin.m b/src/tray_darwin.m index 838dba9..9865bd6 100644 --- a/src/tray_darwin.m +++ b/src/tray_darwin.m @@ -1,78 +1,85 @@ +#include "tray.h" #include #include -#include "tray.h" - @interface AppDelegate: NSObject - - (IBAction)menuCallback:(id)sender; +- (IBAction)menuCallback:(id)sender; @end -@implementation AppDelegate{} - - (IBAction)menuCallback:(id)sender - { - struct tray_menu *m = [[sender representedObject] pointerValue]; - if (m != NULL && m->cb != NULL) { - m->cb(m); - } - } +@implementation AppDelegate { +} +- (IBAction)menuCallback:(id)sender { + struct tray_menu *m = [[sender representedObject] pointerValue]; + if (m != NULL && m->cb != NULL) { + m->cb(m); + } +} @end -static NSApplication* app; -static NSStatusBar* statusBar; -static NSStatusItem* statusItem; +static NSApplication *app; +static NSStatusBar *statusBar; +static NSStatusItem *statusItem; -static NSMenu* _tray_menu(struct tray_menu *m) { - NSMenu* menu = [[NSMenu alloc] init]; - [menu setAutoenablesItems:FALSE]; +static NSMenu * +_tray_menu(struct tray_menu *m) { + NSMenu *menu = [[NSMenu alloc] init]; + [menu setAutoenablesItems:FALSE]; - for (; m != NULL && m->text != NULL; m++) { - if (strcmp(m->text, "-") == 0) { - [menu addItem:[NSMenuItem separatorItem]]; - } else { - NSMenuItem* menuItem = [[NSMenuItem alloc] - initWithTitle:[NSString stringWithUTF8String:m->text] - action:@selector(menuCallback:) - keyEquivalent:@""]; - [menuItem setEnabled:(m->disabled ? FALSE : TRUE)]; - [menuItem setState:(m->checked ? 1 : 0)]; - [menuItem setRepresentedObject:[NSValue valueWithPointer:m]]; - [menu addItem:menuItem]; - if (m->submenu != NULL) { - [menu setSubmenu:_tray_menu(m->submenu) forItem:menuItem]; - } - } + for (; m != NULL && m->text != NULL; m++) { + if (strcmp(m->text, "-") == 0) { + [menu addItem:[NSMenuItem separatorItem]]; } - return menu; + else { + NSMenuItem *menuItem = [[NSMenuItem alloc] + initWithTitle:[NSString stringWithUTF8String:m->text] + action:@selector(menuCallback:) + keyEquivalent:@""]; + [menuItem setEnabled:(m->disabled ? FALSE : TRUE)]; + [menuItem setState:(m->checked ? 1 : 0)]; + [menuItem setRepresentedObject:[NSValue valueWithPointer:m]]; + [menu addItem:menuItem]; + if (m->submenu != NULL) { + [menu setSubmenu:_tray_menu(m->submenu) forItem:menuItem]; + } + } + } + return menu; } -int tray_init(struct tray *tray) { - AppDelegate *delegate = [[AppDelegate alloc] init]; - app = [NSApplication sharedApplication]; - [app setDelegate:delegate]; - statusBar = [NSStatusBar systemStatusBar]; - statusItem = [statusBar statusItemWithLength:NSVariableStatusItemLength]; - tray_update(tray); - [app activateIgnoringOtherApps:TRUE]; - return 0; +int +tray_init(struct tray *tray) { + AppDelegate *delegate = [[AppDelegate alloc] init]; + app = [NSApplication sharedApplication]; + [app setDelegate:delegate]; + statusBar = [NSStatusBar systemStatusBar]; + statusItem = [statusBar statusItemWithLength:NSVariableStatusItemLength]; + tray_update(tray); + [app activateIgnoringOtherApps:TRUE]; + return 0; } -int tray_loop(int blocking) { - NSDate* until = (blocking ? [NSDate distantFuture] : [NSDate distantPast]); - NSEvent* event = [app nextEventMatchingMask:ULONG_MAX untilDate:until - inMode:[NSString stringWithUTF8String:"kCFRunLoopDefaultMode"] dequeue:TRUE]; - if (event) { - [app sendEvent:event]; - } - return 0; +int +tray_loop(int blocking) { + NSDate *until = (blocking ? [NSDate distantFuture] : [NSDate distantPast]); + NSEvent *event = [app nextEventMatchingMask:ULONG_MAX + untilDate:until + inMode:[NSString stringWithUTF8String:"kCFRunLoopDefaultMode"] + dequeue:TRUE]; + if (event) { + [app sendEvent:event]; + } + return 0; } -void tray_update(struct tray *tray) { - NSImage *image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:tray->icon]]; - NSSize size = NSMakeSize(16, 16); - [image setSize:NSMakeSize(16, 16)]; - statusItem.button.image = image; - [statusItem setMenu:_tray_menu(tray->menu)]; +void +tray_update(struct tray *tray) { + NSImage *image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:tray->icon]]; + NSSize size = NSMakeSize(16, 16); + [image setSize:NSMakeSize(16, 16)]; + statusItem.button.image = image; + [statusItem setMenu:_tray_menu(tray->menu)]; } -void tray_exit(void) { - [app terminate:app]; +void +tray_exit(void) { + [app terminate:app]; } diff --git a/tests/unit/test_tray.cpp b/tests/unit/test_tray.cpp index 7d55b5c..e31bca5 100644 --- a/tests/unit/test_tray.cpp +++ b/tests/unit/test_tray.cpp @@ -1,133 +1,139 @@ #include -#if defined (_WIN32) || defined (_WIN64) -#define TRAY_WINAPI 1 -#elif defined (__linux__) || defined (linux) || defined (__linux) -#define TRAY_APPINDICATOR 1 -#elif defined (__APPLE__) || defined (__MACH__) -#define TRAY_APPKIT 1 +#if defined(_WIN32) || defined(_WIN64) + #define TRAY_WINAPI 1 +#elif defined(__linux__) || defined(linux) || defined(__linux) + #define TRAY_APPINDICATOR 1 +#elif defined(__APPLE__) || defined(__MACH__) + #define TRAY_APPKIT 1 #endif #include "src/tray.h" #if TRAY_APPINDICATOR -#define TRAY_ICON1 "mail-message-new" -#define TRAY_ICON2 "mail-message-new" + #define TRAY_ICON1 "mail-message-new" + #define TRAY_ICON2 "mail-message-new" #elif TRAY_APPKIT -#define TRAY_ICON1 "icon.png" -#define TRAY_ICON2 "icon.png" + #define TRAY_ICON1 "icon.png" + #define TRAY_ICON2 "icon.png" #elif TRAY_WINAPI -#define TRAY_ICON1 "icon.ico" -#define TRAY_ICON2 "icon.ico" + #define TRAY_ICON1 "icon.ico" + #define TRAY_ICON2 "icon.ico" #endif -class TrayTest : public BaseTest { +class TrayTest: public BaseTest { protected: - static struct tray testTray; - - // Static arrays for submenus - static struct tray_menu submenu7_8[]; - static struct tray_menu submenu5_6[]; - static struct tray_menu submenu_second[]; - static struct tray_menu submenu[]; - - // Non-static member functions - static void hello_cb(struct tray_menu *item) { - // Mock implementation - } - static void toggle_cb(struct tray_menu *item) { - item->checked = !item->checked; - tray_update(&testTray); - } - static void quit_cb(struct tray_menu *item) { - tray_exit(); - } - static void submenu_cb(struct tray_menu *item) { - // Mock implementation - tray_update(&testTray); - } - - void SetUp() override { - testTray.icon = TRAY_ICON1; - testTray.tooltip = "TestTray"; - testTray.menu = submenu; - } - - void TearDown() override { + static struct tray testTray; + + // Static arrays for submenus + static struct tray_menu submenu7_8[]; + static struct tray_menu submenu5_6[]; + static struct tray_menu submenu_second[]; + static struct tray_menu submenu[]; + + // Non-static member functions + static void + hello_cb(struct tray_menu *item) { + // Mock implementation + } + static void + toggle_cb(struct tray_menu *item) { + item->checked = !item->checked; + tray_update(&testTray); + } + static void + quit_cb(struct tray_menu *item) { + tray_exit(); + } + static void + submenu_cb(struct tray_menu *item) { + // Mock implementation + tray_update(&testTray); + } + + void + SetUp() override { + testTray.icon = TRAY_ICON1; + testTray.tooltip = "TestTray"; + testTray.menu = submenu; + } + + void + TearDown() override { // Clean up any resources if needed - } + } }; // Define the static arrays struct tray_menu TrayTest::submenu7_8[] = { - {.text = "7", .cb = submenu_cb}, - {.text = "-"}, - {.text = "8", .cb = submenu_cb}, - {.text = nullptr} + { .text = "7", .cb = submenu_cb }, + { .text = "-" }, + { .text = "8", .cb = submenu_cb }, + { .text = nullptr } }; struct tray_menu TrayTest::submenu5_6[] = { - {.text = "5", .cb = submenu_cb}, - {.text = "6", .cb = submenu_cb}, - {.text = nullptr} + { .text = "5", .cb = submenu_cb }, + { .text = "6", .cb = submenu_cb }, + { .text = nullptr } }; struct tray_menu TrayTest::submenu_second[] = { - {.text = "THIRD", .submenu = submenu7_8}, - {.text = "FOUR", .submenu = submenu5_6}, - {.text = nullptr} + { .text = "THIRD", .submenu = submenu7_8 }, + { .text = "FOUR", .submenu = submenu5_6 }, + { .text = nullptr } }; struct tray_menu TrayTest::submenu[] = { - {.text = "Hello", .cb = hello_cb}, - {.text = "Checked", .checked = 1, .checkbox = 1, .cb = toggle_cb}, - {.text = "Disabled", .disabled = 1}, - {.text = "-"}, - {.text = "SubMenu", .submenu = submenu_second}, - {.text = "-"}, - {.text = "Quit", .cb = quit_cb}, - {.text = nullptr} + { .text = "Hello", .cb = hello_cb }, + { .text = "Checked", .checked = 1, .checkbox = 1, .cb = toggle_cb }, + { .text = "Disabled", .disabled = 1 }, + { .text = "-" }, + { .text = "SubMenu", .submenu = submenu_second }, + { .text = "-" }, + { .text = "Quit", .cb = quit_cb }, + { .text = nullptr } }; struct tray TrayTest::testTray = { - .icon = TRAY_ICON1, - .tooltip = "TestTray", - .menu = submenu + .icon = TRAY_ICON1, + .tooltip = "TestTray", + .menu = submenu }; TEST_F(TrayTest, TestTrayInit) { - int result = tray_init(&testTray); - EXPECT_EQ(result, 0); // make sure return value is 0 + int result = tray_init(&testTray); + EXPECT_EQ(result, 0); // make sure return value is 0 } TEST_F(TrayTest, TestTrayLoop) { - int result = tray_loop(1); - EXPECT_EQ(result, 0); // make sure return value is 0 + int result = tray_loop(1); + EXPECT_EQ(result, 0); // make sure return value is 0 } TEST_F(TrayTest, TestTrayUpdate) { - // check the initial values - EXPECT_EQ(testTray.icon, TRAY_ICON1); - EXPECT_EQ(testTray.tooltip, "TestTray"); - - // update the values - testTray.icon = TRAY_ICON2; - testTray.tooltip = "TestTray2"; - tray_update(&testTray); - EXPECT_EQ(testTray.icon, TRAY_ICON2); - EXPECT_EQ(testTray.tooltip, "TestTray2"); - - // put back the original values - testTray.icon = TRAY_ICON1; - testTray.tooltip = "TestTray"; - tray_update(&testTray); - EXPECT_EQ(testTray.icon, TRAY_ICON1); - EXPECT_EQ(testTray.tooltip, "TestTray"); + // check the initial values + EXPECT_EQ(testTray.icon, TRAY_ICON1); + EXPECT_EQ(testTray.tooltip, "TestTray"); + + // update the values + testTray.icon = TRAY_ICON2; + testTray.tooltip = "TestTray2"; + tray_update(&testTray); + EXPECT_EQ(testTray.icon, TRAY_ICON2); + EXPECT_EQ(testTray.tooltip, "TestTray2"); + + // put back the original values + testTray.icon = TRAY_ICON1; + testTray.tooltip = "TestTray"; + tray_update(&testTray); + EXPECT_EQ(testTray.icon, TRAY_ICON1); + EXPECT_EQ(testTray.tooltip, "TestTray"); } TEST_F(TrayTest, TestToggleCallback) { - bool initialCheckedState = testTray.menu[1].checked; - toggle_cb(&testTray.menu[1]); - EXPECT_EQ(testTray.menu[1].checked, !initialCheckedState); + bool initialCheckedState = testTray.menu[1].checked; + toggle_cb(&testTray.menu[1]); + EXPECT_EQ(testTray.menu[1].checked, !initialCheckedState); } TEST_F(TrayTest, TestTrayExit) { - tray_exit(); - // TODO: Check the state after tray_exit + tray_exit(); + // TODO: Check the state after tray_exit } From b7678a716425ceabb78cb9835111d28c66359bd2 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:45:56 -0400 Subject: [PATCH 3/7] Add vscode to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index cab683a..5073c82 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ # JetBrains IDE .idea/ +# VSCode IDE +.vscode/ + # build directories build/ cmake-*/ From 03181d7967412fd77cc18ba17b4cf2aa8e20b937 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:52:10 -0400 Subject: [PATCH 4/7] fully remove boost capture from conftest.cpp --- tests/conftest.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/conftest.cpp b/tests/conftest.cpp index 297edb2..33fea0a 100644 --- a/tests/conftest.cpp +++ b/tests/conftest.cpp @@ -70,8 +70,6 @@ class BaseTest: public ::testing::Test { std::cout << std::endl << "Test failed: " << test_info->name() << std::endl << std::endl - << "Captured boost log:" << std::endl - << boost_log_buffer.str() << std::endl << "Captured cout:" << std::endl << cout_buffer.str() << std::endl << "Captured stdout:" << std::endl @@ -95,7 +93,6 @@ class BaseTest: public ::testing::Test { std::vector testArgs; // CLI arguments used std::filesystem::path testBinary; // full path of this binary std::filesystem::path testBinaryDir; // full directory of this binary - std::stringstream boost_log_buffer; // declare boost_log_buffer std::stringstream cout_buffer; // declare cout_buffer std::stringstream stdout_buffer; // declare stdout_buffer std::stringstream stderr_buffer; // declare stderr_buffer From ea4b5400469b2868297d1880603dfb834a3157ef Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 15 Apr 2024 18:30:56 -0400 Subject: [PATCH 5/7] use quotes for local includes --- tests/conftest.cpp | 2 +- tests/unit/test_tray.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.cpp b/tests/conftest.cpp index 33fea0a..a425223 100644 --- a/tests/conftest.cpp +++ b/tests/conftest.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include "tests/utils.h" // Undefine the original TEST macro #undef TEST diff --git a/tests/unit/test_tray.cpp b/tests/unit/test_tray.cpp index e31bca5..d634a26 100644 --- a/tests/unit/test_tray.cpp +++ b/tests/unit/test_tray.cpp @@ -1,4 +1,4 @@ -#include +#include "tests/conftest.cpp" #if defined(_WIN32) || defined(_WIN64) #define TRAY_WINAPI 1 From 1d4902caf381de7cf18e031d4d2fb7689c7671b1 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:57:44 -0400 Subject: [PATCH 6/7] disable search for codecov action --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a70b346..3b48b94 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,6 +143,7 @@ jobs: if: always() && (steps.test_report.outcome == 'success') uses: codecov/codecov-action@v4 with: + disable_search: true fail_ci_if_error: true files: ./build/coverage.xml flags: "${{ steps.codecov_flags.outputs.flags }}" From 1358e75a8ca169001cc0f08e54ba91e1940db21e Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:57:36 -0400 Subject: [PATCH 7/7] ci: skip coverage upload if not in LizardByte org --- .github/workflows/ci.yml | 10 ++++++++-- tests/CMakeLists.txt | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b48b94..bfed178 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,8 +124,11 @@ jobs: run: | ${{ steps.python-path.outputs.python-path }} -m pip install gcovr ${{ steps.python-path.outputs.python-path }} -m gcovr -r .. \ + --exclude-noncode-lines \ + --exclude-throw-branches \ + --exclude-unreachable-branches \ --exclude '.*tests/.*' \ - --exclude '.*tests/.*' \ + --exclude '.*third-party/.*' \ --xml-pretty \ -o coverage.xml @@ -140,7 +143,9 @@ jobs: - name: Upload coverage # any except canceled or skipped - if: always() && (steps.test_report.outcome == 'success') + if: >- + always() && (steps.test_report.outcome == 'success') && + startsWith(github.repository, 'LizardByte/') uses: codecov/codecov-action@v4 with: disable_search: true @@ -148,3 +153,4 @@ jobs: files: ./build/coverage.xml flags: "${{ steps.codecov_flags.outputs.flags }}" token: ${{ secrets.CODECOV_TOKEN }} + verbose: true diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 453a2fe..e8f8bd3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,8 +16,8 @@ include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR # coverage # https://gcovr.com/en/stable/guide/compiling.html#compiler-options -set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage -O1") -set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage -O1") +set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage -ggdb -O0") +set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage -ggdb -O0") # if windows if (WIN32)