diff --git a/.astylerc b/.astylerc deleted file mode 100644 index 8a8b9ef82c..0000000000 --- a/.astylerc +++ /dev/null @@ -1,87 +0,0 @@ -# -# MetaCall Artistic Style Configuration by Parra Studios -# A configuration for Artistic Style linter. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -# Bracket Style Options -# - ---style=allman # Use Allman style with broken brackets - -# -# Tabs Options -# - ---indent=force-tab=4 # Use tab for indentation with size equal to 4 - -# -# Bracket Modify Options -# - ---attach-namespaces # Attach brackets to bracketed namespace statement ---attach-extern-c # Attach brackets to bracketed extern "C" statement - -# -# Indentation Options -# - ---indent-classes # Indent class and struct blocks ---indent-modifiers # Indent class and struct access modifiers ---indent-switches # Indent switch blocks so that the case statements are indented in the block ---indent-labels # Add extra indentation to labels ---indent-preproc-define # Indent multi-line preprocessor definitions ---indent-preproc-cond # Indent preprocessor conditional statements to the same level as source code ---indent-col1-comments # Indent C++ comments beginning in column one ---min-conditional-indent=0 # Minimal indent when a header is built of multiple lines ---max-instatement-indent=80 # Maximum number of spaces to indent a continuation line - -# -# Padding options -# - ---break-blocks # Pad empty lines around header blocks ---pad-oper # Insert space padding around operators ---pad-header # Insert space padding between a header and the following paren ---unpad-paren # Remove extra space padding around parenthesis on the inside and outside ---align-pointer=middle # Align pointer to the middle ---align-reference=middle # Align reference to the middle - -# -# Formatting options -# - ---add-brackets # Add brackets to unbracketed one line conditional statements ---max-code-length=120 # Define max code lenght limit ---break-after-logical # Add line break after logical operators in a conditional statement - -# -# Indentation mode -# - ---mode=c # Use C/C++ indentation mode - -# -# Other options -# - ---dry-run # Do not create any file nor overwrite -#--sufix=none # Overwrite original file ---recursive # Process all directories recursively ---preserve-date # Preserve original file date ---verbose # Display optional information and staticial data ---lineend=windows # Use windows line endings diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..bffce04a3e --- /dev/null +++ b/.clang-format @@ -0,0 +1,110 @@ +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +# AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: true +# AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: true +AlignEscapedNewlines: DontAlign +# AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +# AllowShortBlocksOnASingleLine: false +# AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +# AllowShortIfStatementsOnASingleLine: false +# AllowShortLoopsOnASingleLine: false +# AlwaysBreakAfterDefinitionReturnType: None +# AlwaysBreakAfterReturnType: None +# AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: No +# BinPackArguments: true +# BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +# BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +# BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +# BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +# BreakStringLiterals: true +ColumnLimit: 0 +# CommentPragmas: '^ IWYU pragma:' +# CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +# DerivePointerAlignment: false +# DisableFormat: false +# ExperimentalAutoDetectBinPacking: false +# FixNamespaceComments: true +# ForEachMacros: +# - foreach +# - Q_FOREACH +# - BOOST_FOREACH +# IncludeBlocks: Preserve +IncludeCategories: + - Regex: '".*"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 +# IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentExternBlock: NoIndent +IndentPPDirectives: BeforeHash +IndentWidth: 4 +# IndentWrappedFunctionNames: false +# JavaScriptQuotes: Leave +# JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +# MacroBlockBegin: '' +# MacroBlockEnd: '' +# MaxEmptyLinesToKeep: 1 +# NamespaceIndentation: None +# PenaltyBreakAssignment: 2 +# PenaltyBreakBeforeFirstCallParameter: 19 +# PenaltyBreakComment: 300 +# PenaltyBreakFirstLessLess: 120 +# PenaltyBreakString: 1000 +# PenaltyExcessCharacter: 1000000 +# PenaltyReturnTypeOnItsOwnLine: 60 +# PointerAlignment: Right +# RawStringFormats: +# - Delimiter: pb +# Language: TextProto +# BasedOnStyle: google +# ReflowComments: true +# SortIncludes: true +# SortUsingDeclarations: true +# SpaceAfterCStyleCast: false +# SpaceAfterTemplateKeyword: true +# SpaceBeforeAssignmentOperators: true +# SpaceBeforeParens: ControlStatements +# SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +# SpacesInAngles: false +# SpacesInContainerLiterals: true +# SpacesInCStyleCastParentheses: false +# SpacesInParentheses: false +# SpacesInSquareBrackets: false +TabWidth: 4 +UseCRLF: true +UseTab: Always diff --git a/.env b/.env index e99eafc2fb..cf3ae699c0 100644 --- a/.env +++ b/.env @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,5 +22,5 @@ COMPOSE_PROJECT_NAME='metacall' # Configure default variables METACALL_PATH=/usr/local/metacall -METACALL_BUILD_TYPE=release -METACALL_BASE_IMAGE=debian:buster-slim +METACALL_BUILD_TYPE=relwithdebinfo +METACALL_BASE_IMAGE=debian:trixie-slim # debian:bookworm-slim # ubuntu:noble # ubuntu:jammy # alpine:3.17 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e13dab85d9..ea802c726b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,9 +1,19 @@ # Contributing When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. +email, or any other method with the owners of this repository before making a change. Here is an examplary process you can follow to create an issue on MetaCall: -Please note we have a [Code of Conduct](/.github/CODE_OF_CONDUCT), please follow it in all your interactions with the project. +1. Identify a problem or a possible addition to Metacall codebase/operation
+for instance: ```pre-commit-clang-format not working on windows...``` +2. Then go to the necessary MetaCall gitHub repository and click on the "Issues" tab at the top of the page. Indicate your issue's interest by tagging it as a bug report, custom issue, documentation, feature, or discussion.
+in this case: ```metacall/core``` +3. Type in a title and description for your issue. Be as detailed as possible, including any error messages or steps to reproduce the issue. Image example:
+![issue image](https://user-images.githubusercontent.com/93955843/220493308-0ce3f101-6957-43fb-96d1-7a730ccf304c.PNG) + +4. You can assign the issue to yourself, add labels or a milestone to it, and attach files if necessary. +5. Submit the newly created issue and start working on a solution by creating a fork of the repository.
(Here to issue used for explanation) + +Please note we have a [Code of Conduct](/.github/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. ## Pull Request Process diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..1be1f7c86b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: 🐛 Bug Report +about: Submit a bug report to help us improve +labels: "bug" +assignees: '' +--- + +## 🐛 Bug Report + + + +### Expected Behavior + + +### Current Behavior + + +### Possible Solution + + +### Steps to Reproduce + + +1. +2. +3. +4. + +### Context (Environment) + + + + + +### Detailed Description + + +### Possible Implementation + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..41f18c3c10 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +blank_issues_enabled: false +contact_links: + - name: Discussions + url: https://github.com/metacall/core/discussions + about: This is where long discussions could take place outside of the chat platforms. + + diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000..841a03f189 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,23 @@ +--- +name: ➕ Custom Issue +about: Create a custom issue +labels: "custom" +assignees: '' +--- + +Hey there and thank you for using Github Issues! + +Our project, as you've probably heard, is getting really popular and truth is we're getting a bit overwhelmed by the activity surrounding it. There are just too many issues for us to manage properly. + +Do the checklist before filing an issue: + +- [ ] Is this something you can **debug and fix**? Create an apt issue and send us a pull request! Bug fixes and documentation fixes are welcome. +- [ ] Have an usage question? Ask it on [Discussions](https://github.com/metacall/core/discussions/new) or community chats. We use Github Discussions for usage questions and GitHub Issues for bugs. +- [ ] Have an idea for a feature? Use the feature issue and propose your ideas. + +None of the above, create a custom issue + +Make sure to add **all the information needed to help us understand what you wish to convey** so that someone can help. + +------------------------------------------------------------------ + diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000000..f96efa63f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,10 @@ +--- +name: 📚 Documentation +about: Report an issue related to documentation +labels: "documentation" +assignees: '' +--- + +## 📚 Documentation + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..13fcc6e8f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: 🚀 Feature +about: Submit a proposal for a new feature +labels: "feature" +assignees: '' +--- + +## 🚀 Feature + + + +### Is your feature request related to a problem? + + +### Describe the solution you'd like + + +### Describe alternatives you've considered + + +### Additional context + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e69de29bb2..7b3e570695 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ +# Description + +Please include a summary of the change and which issue is fixed. List any dependencies that are required for this change. + +Fixes #(issue_no) + + + +## Type of change + + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update +- [ ] Documentation update + +# Checklist: + +- [ ] I have performed a self-review of my own code. +- [ ] I have commented my code, particularly in hard-to-understand areas. +- [ ] I have made corresponding changes to the documentation. +- [ ] My changes generate no new warnings. +- [ ] I have added tests/screenshots (if any) that prove my fix is effective or that my feature works. +- [ ] I have tested the tests implicated (if any) by my own code and they pass (`make test` or `ctest -VV -R `). +- [ ] If my change is significant or breaking, I have passed all tests with `./docker-compose.sh test &> output` and attached the output. +- [ ] I have tested my code with `OPTION_BUILD_ADDRESS_SANITIZER` or `./docker-compose.sh test-address-sanitizer &> output` and `OPTION_TEST_MEMORYCHECK`. +- [ ] I have tested my code with `OPTION_BUILD_THREAD_SANITIZER` or `./docker-compose.sh test-thread-sanitizer &> output`. +- [ ] I have tested with `Helgrind` in case my code works with threading. +- [ ] I have run `make clang-format` in order to format my code and my code follows the style guidelines. + +If you are unclear about any of the above checks, have a look at our documentation [here](https://github.com/metacall/core/blob/develop/docs/README.md#63-debugging). diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000000..67a684cd07 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,202 @@ +name: Benchmarks + +on: + pull_request: + push: + tags: + - "v*.*.*" + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + benchmark-unix: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest] + + env: + LTTNG_UST_REGISTER_TIMEOUT: 0 + NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Prepare MacOS Installation + if: matrix.os == 'macos-latest' + run: | + echo "Uninstall CMake" + brew uninstall --force cmake + + echo "Uninstall NodeJS and NPM" + npm uninstall npm -g + rm -rf /usr/local/lib/node_modules/npm + + echo "Uninstall Ruby" + brew uninstall --force --ignore-dependencies ruby + brew cleanup -s ruby + brew cleanup --prune-prefix + RUBY_FRAMEWORK_DIR=$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/Ruby.framework + sudo rm -rf $RUBY_FRAMEWORK_DIR + + echo "Uninstall Go" + brew uninstall --force go + brew autoremove + sudo rm -rf /usr/local/Cellar/go + sudo rm -rf /usr/local/go + sudo rm -rf /usr/local/opt/go + sudo rm -rf /etc/paths.d/go + sudo rm -rf /usr/local/bin/go + sudo rm -rf /usr/local/bin/gofmt + + echo "Uninstall Java" + sudo rm -rf /Library/Java/JavaVirtualMachines/* + sudo rm -rf /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin + sudo rm -rf /Library/PreferencePanes/JavaControlPanel.prefPane + unset JAVA_HOME + + echo "Export XCode SDK Root" + echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV + + # TODO: Add support for NetCore Bench + - name: Set up the environment + run: | + sh ./tools/metacall-environment.sh $METACALL_INSTALL_OPTIONS + env: + METACALL_INSTALL_OPTIONS: base python nodejs ruby # TODO: Implement NetCore once it works with MacOS + + - name: Configure + run: | + mkdir -p build + cd build + if [ "$(uname)" == "Darwin" ]; then + . .env + fi + bash ../tools/metacall-configure.sh $METACALL_CONFIGURE_OPTIONS + env: + METACALL_CONFIGURE_OPTIONS: release scripts python nodejs ruby benchmarks # TODO: Implement NetCore once it works with MacOS + + - name: Build + working-directory: ./build + run: | + if [ "$(uname)" == "Darwin" ]; then + . .env + fi + bash ../tools/metacall-build.sh $METACALL_BUILD_OPTIONS + env: + METACALL_BUILD_OPTIONS: release benchmarks + + - name: Merge benchmarks + if: ${{ github.event_name != 'pull_request' }} + run: python3 ./tools/metacall-benchmarks-merge.py ./build/benchmarks + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + if: ${{ github.event_name != 'pull_request' }} + with: + name: MetaCall Benchmark (${{ matrix.os }}) + tool: 'googlecpp' + output-file-path: ./build/benchmarks/metacall-benchmarks.json + # Access token to deploy GitHub Pages branch + github-token: ${{ secrets.BENCHMARKS_PUSH_TOKEN }} + # Disable push and deploy GitHub pages branch automatically + auto-push: false + # Github Pages repository name + gh-repository: github.com/metacall/core-benchmarks + # Github Pages branch name + gh-pages-branch: main + # Output directory + benchmark-data-dir-path: ./${{ matrix.os }} + + - name: Push benchmark result + if: ${{ github.event_name != 'pull_request' }} + run: | + cd benchmark-data-repository + git push https://$REPO_KEY@github.com/metacall/core-benchmarks.git + env: + REPO_KEY: ${{secrets.BENCHMARKS_PUSH_TOKEN}} + + benchmark-windows: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-2022, windows-2025] + + env: + LTTNG_UST_REGISTER_TIMEOUT: 0 + NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Activate the Visual Studio Developer Prompt + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64 + + - name: Set up the environment + run: cmd.exe /c "powershell .\tools\metacall-environment.ps1 $Env:METACALL_INSTALL_OPTIONS" + env: + METACALL_INSTALL_OPTIONS: python nodejs ruby # TODO: Implement NetCore once it works with Windows + + # TODO: Use release when this bug is solved: https://github.com/metacall/core/issues/461 + - name: Configure + run: | + $METACALL_PATH = $PWD + md -Force "$METACALL_PATH\build" + cd "$METACALL_PATH\build" + cmd.exe /c "powershell ..\tools\metacall-configure.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: debug scripts python nodejs ruby benchmarks # TODO: Implement NetCore once it works with Windows + + # TODO: Use release when this bug is solved: https://github.com/metacall/core/issues/461 + - name: Build + working-directory: ./build + run: cmd.exe /c "powershell ..\tools\metacall-build.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: debug benchmarks + + - name: Merge benchmarks + if: ${{ github.event_name != 'pull_request' }} + run: python3 ./tools/metacall-benchmarks-merge.py ./build/benchmarks + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + if: ${{ github.event_name != 'pull_request' }} + with: + name: MetaCall Benchmark (${{ matrix.os }}) + tool: 'googlecpp' + output-file-path: ./build/benchmarks/metacall-benchmarks.json + # Access token to deploy GitHub Pages branch + github-token: ${{ secrets.BENCHMARKS_PUSH_TOKEN }} + # Disable push and deploy GitHub pages branch automatically + auto-push: false + # Github Pages repository name + gh-repository: github.com/metacall/core-benchmarks + # Github Pages branch name + gh-pages-branch: main + # Output directory + benchmark-data-dir-path: ./${{ matrix.os }} + + - name: Push benchmark result + if: ${{ github.event_name != 'pull_request' }} + run: | + cd benchmark-data-repository + git push https://${REPO_KEY}@github.com/metacall/core-benchmarks.git + env: + REPO_KEY: ${{secrets.BENCHMARKS_PUSH_TOKEN}} diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000000..243d5b2355 --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,19 @@ +name: Code Formatting Check + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + formatting-check: + name: Formatting Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run clang-format style check for C/C++. + uses: jidicula/clang-format-action@v4.9.0 + with: + clang-format-version: '11' + check-path: 'source' diff --git a/.github/workflows/docker-hub.yml b/.github/workflows/docker-hub.yml new file mode 100644 index 0000000000..34267338aa --- /dev/null +++ b/.github/workflows/docker-hub.yml @@ -0,0 +1,214 @@ +name: Build and Push Docker Image for Multiple Architectures + +on: + pull_request: + push: + branches: + - master + - develop + tags: + - 'v*.*.*' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + DOCKER_REGISTRY: index.docker.io + DOCKER_USERNAME: metacall + IMAGE_NAME: core + BUILDKIT_VERSION: 0.13.0 + + # TODO: Tests failing + # - linux/s390x + # TODO: Detour not supported, needs to patch GOT instead of PLT + # - linux/mips64le + # - linux/mips64 + PLATFORM_LIST: > + [ + "linux/amd64", + "linux/amd64/v2", + "linux/amd64/v3", + "linux/386", + "linux/arm64", + "linux/riscv64", + "linux/ppc64le", + "linux/arm/v7", + "linux/arm/v6", + "linux/loong64" + ] + +jobs: + matrix: + name: Generate Platform List + runs-on: ubuntu-latest + outputs: + platform_list: ${{ steps.generate_platform_list.outputs.platform_list }} + steps: + - name: Generate platform list + id: generate_platform_list + run: | + set -exuo pipefail + PLATFORM_STRING=$(cat <> $GITHUB_ENV + echo "::set-output name=platform_list::$PLATFORM_LIST" + + build: + name: Build + runs-on: ubuntu-latest + needs: matrix + strategy: + fail-fast: false + matrix: + platform: ${{ fromJSON(needs.matrix.outputs.platform_list) }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker BuildX + uses: docker/setup-buildx-action@v3 + with: + version: v${{ env.BUILDKIT_VERSION }} + + - name: Login to Docker Hub + # Only run when master or when tagging a version + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Build MetaCall Docker Images + env: + METACALL_PLATFORM: ${{ matrix.platform }} + DOCKER_BUILDKIT: 1 + run: | + ./docker-compose.sh platform + + - name: Run Tests + env: + DOCKER_BUILDKIT: 1 + run: | + set -exuo pipefail + docker image inspect ${DOCKER_USERNAME}/${IMAGE_NAME}:cli --format='{{.Os}}/{{.Architecture}}' + cat < Dockerfile.test + FROM ${DOCKER_USERNAME}/${IMAGE_NAME}:cli + RUN apt-get update && apt-get install -y file + RUN file /usr/local/bin/metacallcli && ldd /usr/local/bin/metacallcli + RUN echo "console.log('0123456789abcdef')" > script.js + RUN metacallcli script.js | tee output.txt + RUN grep 0123456789abcdef output.txt + EOF + + docker buildx build --progress=plain --platform ${{ matrix.platform }} -f Dockerfile.test -t test-image . + + - name: Tag & Push Platform Images + # Only run when master or when tagging a version + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.event_name != 'pull_request' + run: | + platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-') + for tag in "deps" "dev" "runtime" "cli"; do + docker tag \ + ${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} \ + ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag} + + echo "Pushing image for tag: ${tag} with platform: ${platform_tag}" + docker push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag} + done + + manifest: + name: Create and Push Manifest Lists + needs: [matrix, build] + # Only run when master or when tagging a version + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.event_name != 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Create and Push Manifest Lists + run: | + set -exuo pipefail + + tags=("deps" "dev" "runtime" "cli") + platforms=($(echo '${{ needs.matrix.outputs.platform_list }}' | jq -r '.[]')) + + echo "Create all the tags by platform" + + for tag in "${tags[@]}"; do + echo "Creating manifest for tag: $tag" + platform_tags="" + for platform in "${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + platform_tags="${platform_tags} ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag}" + done + echo "Creating manifest with tags: ${platform_tags}" + docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} ${platform_tags} --amend + docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} + done + + echo "Create the latest tag" + + cli_platform_tags="" + for platform in ${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + cli_platform_tags="${cli_platform_tags} ${DOCKER_USERNAME}/${IMAGE_NAME}:cli-${platform_tag}" + done + docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:latest ${cli_platform_tags} --amend + docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:latest + + if [[ "${{ startsWith(github.ref, 'refs/tags/') }}" = true ]]; then + VERSION=${GITHUB_REF#refs/tags/v} + + echo "Create the version ${VERSION} tag" + + for tag in "${tags[@]}"; do + platform_tags="" + for platform in "${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + platform_tags="${platform_tags} ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag}" + done + docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${VERSION}-${tag} ${platform_tags} --amend + docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${VERSION}-${tag} + done + fi + + cleanup: + name: Cleanup Platform Specific Tags + needs: [matrix, build, manifest] + runs-on: ubuntu-latest + if: always() + steps: + - name: Remove Platform-Specific Tags + run: | + set -exuo pipefail + + tags=("deps" "dev" "runtime" "cli") + platforms=($(echo '${{ needs.matrix.outputs.platform_list }}' | jq -r '.[]')) + + for tag in "${tags[@]}"; do + for platform in "${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + tag_to_delete="${tag}-${platform_tag}" + + echo "Deleting tag: ${tag_to_delete}" + echo "https://hub.docker.com/v2/repositories/$%7BDOCKER_USERNAME%7D/$%7BIMAGE_NAME%7D/tags/$%7Btag_to_delete%7D/" + + curl -X DELETE \ + -H "Authorization: Bearer ${{ secrets.DOCKER_HUB_ACCESS_TOKEN_DELETE }}" \ + "https://hub.docker.com/v2/repositories/$%7BDOCKER_USERNAME%7D/$%7BIMAGE_NAME%7D/tags/$%7Btag_to_delete%7D/" + done + done diff --git a/.github/workflows/linux-test.yml b/.github/workflows/linux-test.yml new file mode 100644 index 0000000000..dd9af86734 --- /dev/null +++ b/.github/workflows/linux-test.yml @@ -0,0 +1,85 @@ +name: Linux Test + +on: + workflow_dispatch: + pull_request: + push: + tags: + - 'v*.*.*' + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + linux-test: + name: Linux GCC Test + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + build: [debug, release] + image: ["debian:trixie-slim", "debian:bookworm-slim", "ubuntu:noble", "ubuntu:jammy"] # TODO: "alpine:3.17" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install, build and run tests (build) + run: ./docker-compose.sh build + env: + METACALL_BUILD_TYPE: ${{ matrix.build }} + METACALL_BASE_IMAGE: ${{ matrix.image }} + + - name: Install, build and run tests (test) + run: ./docker-compose.sh test + env: + METACALL_BUILD_TYPE: ${{ matrix.build }} + METACALL_BASE_IMAGE: ${{ matrix.image }} + + linux-sanitizer-test: + name: Linux GCC Sanitizer Test + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + image: ["debian:trixie-slim", "debian:bookworm-slim", "ubuntu:noble", "ubuntu:jammy"] + sanitizer: [address-sanitizer, thread-sanitizer] # TODO: memory-sanitizer not supported by GCC + + env: + SANITIZER_SKIP_SUMMARY: 1 + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install, build and run thread sanitizer tests + run: ./docker-compose.sh test-${{ matrix.sanitizer }} + env: + METACALL_BUILD_TYPE: debug + METACALL_BASE_IMAGE: ${{ matrix.image }} + + linux-distributable: + name: Linux Distributable Dispatch + needs: [linux-test, linux-sanitizer-test] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' + steps: + - name: Linux Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: distributable-linux + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: ci.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: master diff --git a/.github/workflows/macos-test.yml b/.github/workflows/macos-test.yml new file mode 100644 index 0000000000..520de15171 --- /dev/null +++ b/.github/workflows/macos-test.yml @@ -0,0 +1,127 @@ +name: MacOS Test + +on: + workflow_dispatch: + pull_request: + push: + tags: + - "v*.*.*" + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + macos-test: + name: MacOS Clang Test + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [macos-13, macos-14, macos-15] + options: [ + {build: debug, sanitizer: without-sanitizer}, + {build: debug, sanitizer: address-sanitizer}, + {build: debug, sanitizer: thread-sanitizer}, + {build: release, sanitizer: without-sanitizer} + ] + + env: + NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Uninstall CMake + run: | + brew uninstall --force cmake + + - name: Uninstall NodeJS and NPM + run: | + npm uninstall npm -g + rm -rf /usr/local/lib/node_modules/npm + + - name: Uninstall Ruby + run: | + brew uninstall --force --ignore-dependencies ruby + brew cleanup -s ruby + brew cleanup --prune-prefix + RUBY_FRAMEWORK_DIR=$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/Ruby.framework + sudo rm -rf $RUBY_FRAMEWORK_DIR + + - name: Uninstall Go + run: | + brew uninstall --force go + brew autoremove + sudo rm -rf /usr/local/Cellar/go + sudo rm -rf /usr/local/go + sudo rm -rf /usr/local/opt/go + sudo rm -rf /etc/paths.d/go + sudo rm -rf /usr/local/bin/go + sudo rm -rf /usr/local/bin/gofmt + + - name: Uninstall Java + run: | + sudo rm -rf /Library/Java/JavaVirtualMachines/* + sudo rm -rf /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin + sudo rm -rf /Library/PreferencePanes/JavaControlPanel.prefPane + unset JAVA_HOME + + - name: Export XCode SDK Root + run: echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV + + - name: Set up the environment + run: sh ./tools/metacall-environment.sh $METACALL_INSTALL_OPTIONS + env: + METACALL_INSTALL_OPTIONS: base python nodejs typescript java ruby wasm rpc file cobol go backtrace #netcore5 c rust rapidjson pack # clangformat v8rep51 coverage + + - name: Configure + run: | + cd build + . .env + bash ../tools/metacall-configure.sh $METACALL_CONFIGURE_OPTIONS + env: + METACALL_CONFIGURE_OPTIONS: ${{ matrix.options.build }} ${{ matrix.options.sanitizer }} scripts ports tests python nodejs typescript java ruby wasm rpc file cobol go benchmarks install # netcore5 c rust examples pack # v8 coverage + + - name: Build + working-directory: ./build + run: | + . .env + bash ../tools/metacall-build.sh $METACALL_BUILD_OPTIONS + env: + METACALL_BUILD_OPTIONS: ${{ matrix.options.build }} tests install + + macos-distributable: + name: MacOS Distributable Dispatch + needs: macos-test + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' + steps: + - name: Homebrew Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: homebrew + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: test.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: main + - name: MacOS Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: distributable-macos + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: ci.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: master diff --git a/.github/workflows/release-nodejs.yml b/.github/workflows/release-nodejs.yml new file mode 100644 index 0000000000..da626217cc --- /dev/null +++ b/.github/workflows/release-nodejs.yml @@ -0,0 +1,26 @@ +name: Release NodeJS Package + +on: + push: + branches: [ master, develop ] + paths: + - 'source/ports/node_port/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + release: + name: Release NodeJS Port + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Release the port + working-directory: source/ports/node_port + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: ./upload.sh diff --git a/.github/workflows/release-python.yml b/.github/workflows/release-python.yml new file mode 100644 index 0000000000..a5f0c7de4f --- /dev/null +++ b/.github/workflows/release-python.yml @@ -0,0 +1,28 @@ +name: Release Python Package + +on: + push: + branches: [ master, develop ] + paths: + - 'source/ports/py_port/**' + +permissions: + id-token: write + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + release: + name: Release Python Port + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Release the port + working-directory: source/ports/py_port + run: ./upload.sh diff --git a/.github/workflows/release-rust.yml b/.github/workflows/release-rust.yml new file mode 100644 index 0000000000..d2a0c31a43 --- /dev/null +++ b/.github/workflows/release-rust.yml @@ -0,0 +1,66 @@ +name: Release Rust Crates + +on: + workflow_dispatch: + pull_request: + push: + branches: [ master, develop ] + paths: + - '.github/workflows/release-rust.yml' + - 'source/ports/rs_port/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + +jobs: + test: + name: Rust Port Tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] # TODO: , windows-latest] + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install MetaCall Unix + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' + run: curl -sL https://raw.githubusercontent.com/metacall/install/master/install.sh | sh + - name: Install MetaCall Windows + if: matrix.os == 'windows-latest' + run: powershell -NoProfile -ExecutionPolicy Unrestricted -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://raw.githubusercontent.com/metacall/install/master/install.ps1')))" + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build the Rust Port + working-directory: source/ports/rs_port + run: cargo build --verbose + + - name: Test the Rust Port + working-directory: source/ports/rs_port + run: cargo test --verbose + + release: + name: Release Rust Port + runs-on: ubuntu-latest + needs: test + if: ${{ github.event_name != 'pull_request' }} + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Release the port + run: | + cd source/ports/rs_port + bash ./upload.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..24f963c9e9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,75 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + GHR_VERSION: 0.17.0 + IMAGE_NAME: index.docker.io/metacall/core + IMAGE_REGISTRY: index.docker.io + ARTIFACTS_PATH: ./build-artifacts + # GITHUB_TOKEN - From default secrets + # GITHUB_REPOSITORY - Default variable + +jobs: + build: + name: Build the core + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Pull the Docker images + run: bash ./docker-compose.sh pull + - name: Build the Docker images + run: bash ./docker-compose.sh build + - name: Extract built artifacts + run: bash ./docker-compose.sh pack + - name: Upload built artifacts + uses: actions/upload-artifact@v4 + with: + name: built-artifacts + path: ${{ env.ARTIFACTS_PATH }}/ + + release: + name: Release on GitHub + runs-on: ubuntu-latest + needs: build + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 # To fetch all tags + - name: Download built artifacts + uses: actions/download-artifact@v4.1.8 + with: + name: built-artifacts + path: ${{ env.ARTIFACTS_PATH }}/ + - name: Load GHR binary + run: | + curl -sL https://github.com/tcnksm/ghr/releases/download/v${GHR_VERSION}/ghr_v${GHR_VERSION}_linux_amd64.tar.gz | tar zx + chmod +x ghr_v${GHR_VERSION}_linux_amd64/ghr + mv ghr_v${GHR_VERSION}_linux_amd64/ghr /usr/local/bin + - name: Export variables + run: | + echo "GH_REPO_OWNER=${GITHUB_REPOSITORY_OWNER}" >> $GITHUB_ENV + echo "GH_REPO_NAME=${GITHUB_REPOSITORY#*/}" >> $GITHUB_ENV + export PREVIOUS_TAG=$(git describe HEAD^1 --abbrev=0 --tags) + echo "PREVIOUS_TAG=${PREVIOUS_TAG}" >> $GITHUB_ENV + echo "GIT_HISTORY<> $GITHUB_ENV + echo "$(git log --no-merges --format="- %s" ${PREVIOUS_TAG}..HEAD)" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + - name: Release on GitHub + run: | + if [[ "${PREVIOUS_TAG}" == "" ]]; then export GIT_HISTORY=$(git log --no-merges --format="- %s"); fi + export CI_COMMIT_TAG="${{ github.ref_name }}" + export RELEASE_DATE=$(date '+%Y-%m-%d') + echo "MetaCall ${CI_COMMIT_TAG} [${RELEASE_DATE}]" + ghr -t "${{ secrets.GITHUB_TOKEN }}" -u "${GH_REPO_OWNER}" -r "${GH_REPO_NAME}" -c "${GITHUB_SHA}" -n "MetaCall ${CI_COMMIT_TAG} [${RELEASE_DATE}]" -b "${GIT_HISTORY}" -replace "${CI_COMMIT_TAG}" "${PWD}/${{ env.ARTIFACTS_PATH }}/packages/" diff --git a/.github/workflows/windows-test.yml b/.github/workflows/windows-test.yml new file mode 100644 index 0000000000..3518e6d4db --- /dev/null +++ b/.github/workflows/windows-test.yml @@ -0,0 +1,84 @@ +name: Windows Test + +on: + workflow_dispatch: + pull_request: + push: + tags: + - 'v*.*.*' + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + windows-test: + name: Windows MSVC Test + runs-on: windows-${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [2022, 2025] + options: [ + {build: debug, sanitizer: without-sanitizer}, + {build: debug, sanitizer: address-sanitizer}, + + # TODO: Not supported yet by MSVC + # {build: debug, sanitizer: thread-sanitizer}, + # {build: debug, sanitizer: memory-sanitizer}, + + # TODO: https://github.com/metacall/core/issues/461 + # {build: release, sanitizer: without-sanitizer} + ] + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Activate the Visual Studio Developer Prompt + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64 + + - name: Set up the environment + run: cmd.exe /c "powershell .\tools\metacall-environment.ps1 $Env:METACALL_INSTALL_OPTIONS" + env: + METACALL_INSTALL_OPTIONS: python nodejs java ruby typescript wasm rpc file # netcore5 java c cobol rust rapidjson pack # clangformat v8rep51 coverage + + - name: Configure + run: | + $METACALL_PATH = $PWD + md -Force "$METACALL_PATH\build" + cd "$METACALL_PATH\build" + cmd.exe /c "powershell ..\tools\metacall-configure.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: ${{ matrix.options.build }} ${{ matrix.options.sanitizer }} scripts ports tests python nodejs java ruby typescript wasm rpc file # netcore5 java c cobol rust examples install pack benchmarks # v8 coverage + + - name: Build + working-directory: ./build + run: cmd.exe /c "powershell ..\tools\metacall-build.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: ${{ matrix.options.build }} tests install + + windows-distributable: + name: Windows Distributable Dispatch + needs: windows-test + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' + steps: + - name: Windows Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: distributable-windows + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: ci.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: master diff --git a/.gitignore b/.gitignore index 7008ad39cd..f462998c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ release_build /bin /lib /install +/dependencies # CMake CMakeCache.txt @@ -32,13 +33,6 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake -# Avoid ignoring build hooks -!hooks/build -!tools/deps/hooks/build -!tools/dev/hooks/build -!tools/runtime/hooks/build -!tools/cli/hooks/build - # Qt cache CMakeLists.txt.user CMakeLists.txt.user.* @@ -53,18 +47,21 @@ _open-project.bat _start-cmake-gui.bat _start-cmd.bat +# macOS files +.DS_Store + # Local config unix .localconfig # Visual Studio Code .vscode +# Clang files +.cache + # Linked dockerignore file in main context .dockerignore !tools/deps/.dockerignore !tools/dev/.dockerignore !tools/runtime/.dockerignore !tools/cli/.dockerignore - -# macOS files -.DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index dea3b4a3a8..0000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,65 +0,0 @@ -# -# MetaCall Library by Parra Studios -# GitLab CD/CI infrastructure for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -services: - - docker:dind - -before_script: - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - -stages: - - build - - package - -variables: - GIT_SUBMODULE_STRATEGY: recursive - IMAGE_REGISTRY: registry.gitlab.com - IMAGE_NAME: registry.gitlab.com/$CI_PROJECT_PATH - DOCKER_HOST: tcp://docker:2375 - DOCKER_DRIVER: overlay2 - ARTIFACTS_PATH: $CI_PROJECT_DIR/artifacts - -build: - stage: build - image: - name: docker/compose:1.23.2 - entrypoint: [""] - script: - - $CI_PROJECT_DIR/docker-compose.sh pull - - $CI_PROJECT_DIR/docker-compose.sh cache - - $CI_PROJECT_DIR/docker-compose.sh push - only: - - master - -# TODO: Implement tags correctly for Docker -package: - stage: package - image: - name: docker/compose:1.23.2 - entrypoint: [""] - script: - - $CI_PROJECT_DIR/docker-compose.sh pull - - $CI_PROJECT_DIR/docker-compose.sh cache - - $CI_PROJECT_DIR/docker-compose.sh push - - $CI_PROJECT_DIR/docker-compose.sh pack - artifacts: - paths: - - $CI_PROJECT_DIR/artifacts/packages - only: - - tags diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000000..9d72770244 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,23 @@ +tasks: + +- name: Build Metacall + init: | + mkdir build && cd ./build + cmake .. + cd .. + command: + sudo env PATH=$PATH cmake --build . --target install + +- name: Pulling metacall Docker Images + init: | + docker pull metacall/core + docker pull metacall/core:dev + mkdir -p $HOME/metacall + command: + docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:dev /bin/bash + +- name: Pulling metacall-cli Docker Image + init: | + docker pull metacall/core:cli + command: + docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:cli diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index da880f926a..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,101 +0,0 @@ -# -# MetaCall Library by Parra Studios -# Travis CD/CI infrastructure for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -language: cpp - -branches: - only: - - master # TODO: Change to master or tags - -sudo: required - -services: - - docker - -dist: xenial - -# Global environment variables -env: - global: - - DOCKER_VERSION: 18.06.1 - - DOCKER_COMPOSE_VERSION: 1.22.0 - - GHR_VERSION: 0.12.0 - - GIT_SUBMODULE_STRATEGY: recursive - - IMAGE_REGISTRY: registry.gitlab.com - - IMAGE_NAME: registry.gitlab.com/$TRAVIS_REPO_SLUG - - ARTIFACTS_PATH: $TRAVIS_BUILD_DIR/artifacts - -# Update Docker & Docker Compose -before_script: - - sudo rm /usr/local/bin/docker-compose || true - - curl -sL https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose - - chmod +x docker-compose - - sudo mv docker-compose /usr/local/bin - - sudo sh -c 'echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list' - - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - - sudo apt-key fingerprint 0EBFCD88 - - sudo apt-get update - - sudo apt-get -y install docker-ce=${DOCKER_VERSION}~ce~3-0~ubuntu # -$(lsb_release -cs) - -# Run the build and packaging steps with cache -script: - - $TRAVIS_BUILD_DIR/docker-compose.sh pull - # - $TRAVIS_BUILD_DIR/docker-compose.sh cache - - $TRAVIS_BUILD_DIR/docker-compose.sh pack - -# Custom script for deploying to GitHub the artifacts -after_script: - - curl -sL https://github.com/tcnksm/ghr/releases/download/v${GHR_VERSION}/ghr_v${GHR_VERSION}_linux_amd64.tar.gz | tar zx - - chmod +x ghr_v${GHR_VERSION}_linux_amd64/ghr - - sudo mv ghr_v${GHR_VERSION}_linux_amd64/ghr /usr/local/bin - - TRAVIS_REPO_OWNER=${TRAVIS_REPO_SLUG%/*} - - TRAVIS_REPO_NAME=${TRAVIS_REPO_SLUG#*/} - - PREVIOUS_TAG=`git describe HEAD^1 --abbrev=0 --tags` - - GIT_HISTORY=`git log --no-merges --format="- %s" ${PREVIOUS_TAG}..HEAD` - - if [[ $PREVIOUS_TAG == "" ]]; then GIT_HISTORY=`git log --no-merges --format="- %s"`; fi - - if [[ $TRAVIS_TAG == "" ]]; then TRAVIS_TAG=${PREVIOUS_TAG}; fi - - RELEASE_DATE=`date '+%Y-%m-%d'` - - echo "MetaCall ${TRAVIS_TAG} [${RELEASE_DATE}]" - - | - ghr -t $GITHUB_TOKEN -u $TRAVIS_REPO_OWNER -r $TRAVIS_REPO_NAME -c $TRAVIS_COMMIT \ - -n "MetaCall ${TRAVIS_TAG} [${RELEASE_DATE}]" -b "${GIT_HISTORY}" -replace \ - $TRAVIS_TAG $ARTIFACTS_PATH/packages/ - -# Install NPM for deploy -before_deploy: - - sudo apt-get -y install curl - - curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - - - sudo apt-get -y install nodejs - - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > $HOME/.npmrc - -# Deploy artifacts to bintray and npm -deploy: - - provider: bintray - file: "$TRAVIS_BUILD_DIR/deploy/packages/descriptor-metacall.json" - user: "${BINTRAY_USER}" - key: "${BINTRAY_KEY}" - skip_cleanup: true - on: - tags: true - - provider: script - script: - - npm publish $TRAVIS_BUILD_DIR/source/ports/node_port --access public # TODO: --tag - skip_cleanup: true - on: - tags: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 730d4c9b6b..f543559e6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # A library for providing a foreing function interface calls. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,19 +22,15 @@ # # CMake version -cmake_minimum_required(VERSION 3.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) # Include cmake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(WriterCompilerDetectionHeaderFound NOTFOUND) -include(GenerateExportHeader) include(ExternalProject) -# This module is only available with CMake >=3.1, so check whether it could be found -include(WriteCompilerDetectionHeader OPTIONAL RESULT_VARIABLE WriterCompilerDetectionHeaderFound) - include(GetGitRevisionDescription) include(Custom) @@ -43,6 +39,7 @@ set_policy(CMP0028 NEW) # ENABLE CMP0028: Double colon in target name means ALIA set_policy(CMP0054 NEW) # ENABLE CMP0054: Only interpret if() arguments as variables or keywords when unquoted. set_policy(CMP0042 NEW) # ENABLE CMP0042: MACOSX_RPATH is enabled by default. set_policy(CMP0063 NEW) # ENABLE CMP0063: Honor visibility properties for all target types. +set_policy(CMP0135 NEW) # ENABLE CMP0135: Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24. # # Project description and (meta) information @@ -58,11 +55,15 @@ set(META_PROJECT_DESCRIPTION "A library for providing inter-language foreign fun set(META_AUTHOR_ORGANIZATION "MetaCall Inc.") set(META_AUTHOR_DOMAIN "https://metacall.io/") set(META_AUTHOR_MAINTAINER "vic798@gmail.com") -set(META_VERSION_MAJOR "0") -set(META_VERSION_MINOR "1") -set(META_VERSION_PATCH "0") + +# Parse version +file(READ VERSION META_VERSION) +string(REPLACE "\n" "" META_VERSION ${META_VERSION}) +string(REPLACE "." ";" META_VERSION_LIST ${META_VERSION}) +list(GET META_VERSION_LIST 0 META_VERSION_MAJOR) +list(GET META_VERSION_LIST 1 META_VERSION_MINOR) +list(GET META_VERSION_LIST 2 META_VERSION_PATCH) set(META_VERSION_REVISION "${GIT_REV}") -set(META_VERSION "${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}") set(META_NAME_VERSION "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_REVISION})") # @@ -70,15 +71,15 @@ set(META_NAME_VERSION "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_R # # Project options -option(BUILD_SHARED_LIBS "Build shared instead of static libraries." ON) -option(OPTION_BUILD_DIST_LIBS "Build all libraries into a single compilation unit." ON) -option(OPTION_SELF_CONTAINED "Create a self-contained install with all dependencies." OFF) +option(BUILD_SHARED_LIBS "Build shared instead of static libraries." ON) # TODO: Static libraries build is not implemented yet +option(OPTION_SELF_CONTAINED "Create a self-contained install with all dependencies." OFF) # TODO: Not implemented option(OPTION_BUILD_TESTS "Build tests." ON) option(OPTION_BUILD_BENCHMARKS "Build benchmarks." OFF) option(OPTION_BUILD_DOCS "Build documentation." OFF) option(OPTION_BUILD_EXAMPLES "Build examples." ON) option(OPTION_BUILD_CLI "Build CLIs." ON) option(OPTION_BUILD_LOADERS "Build loaders." ON) +option(OPTION_BUILD_EXTENSIONS "Build extensions." ON) option(OPTION_BUILD_SCRIPTS "Build scripts." ON) option(OPTION_BUILD_SERIALS "Build serials." ON) option(OPTION_BUILD_DETOURS "Build detours." ON) @@ -87,9 +88,11 @@ option(OPTION_BUILD_LOG_PRETTY "Build logs in a human readable format." ON) option(OPTION_BUILD_PIC "Build with position independent code." ON) option(OPTION_BUILD_SECURITY "Build with stack-smashing protection and source fortify." ON) option(OPTION_BUILD_GUIX "Disable all build system unreproductible operations." OFF) +option(OPTION_GIT_HOOKS "Disable git hooks when running in CI/CD." ON) option(OPTION_FORK_SAFE "Enable fork safety." ON) option(OPTION_THREAD_SAFE "Enable thread safety." OFF) option(OPTION_COVERAGE "Enable coverage." OFF) +option(OPTION_MEMORY_TRACKER "Enable memory tracking for reflect data." ON) # Build type if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) @@ -97,6 +100,13 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") endif() +# Binary directory +if ("${CMAKE_GENERATOR}" MATCHES "^(Visual Studio)") + set(PROJECT_OUTPUT_DIR "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") +else() + set(PROJECT_OUTPUT_DIR "${CMAKE_BINARY_DIR}") +endif() + # # Declare project # @@ -116,12 +126,42 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) # Create version file file(WRITE "${PROJECT_BINARY_DIR}/VERSION" "${META_NAME_VERSION}") +# +# Define Export Headers +# + +include(GenerateExportHeader) + +# Patch export headers in order to produce "${target_upper}_EXPORTS" in API definition +function(GENERATE_EXPORT_HEADER) + # This function is patched again in Portability, due to Haiku workaround, review it if this func gets removed + set(target ${ARGV0}) + string(TOUPPER ${target} target_upper) + set_target_properties(${target} + PROPERTIES DEFINE_SYMBOL "${target_upper}_EXPORTS" + ) + # When the function is redefined, the old function can be accessed through underscore + _GENERATE_EXPORT_HEADER(${ARGN}) +endfunction() + # # Portability # include(Portability) +# +# clang-format and tidy +# + +find_package(ClangFormat) + +if(ClangFormat_FOUND) + include(ClangDevTools) +else() + message(WARNING "Linting disabled: clang-format executable not found") +endif() + # # Compiler settings and options # @@ -133,12 +173,12 @@ include(CompileOptions) # if(OPTION_COVERAGE) - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/coverage") - - set(ENABLE_COVERAGE ON CACHE BOOL "Enable coverage build." FORCE) - set(ENABLE_COVERAGE_ALL ON CACHE BOOL "Enable coverage build for all targets." FORCE) - - find_package(codecov) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "OPTION_COVERAGE requires to be build with -DCMAKE_BUILD_TYPE=Debug, skipping coverage") + set(OPTION_COVERAGE OFF) + else() + include(Coverage) + endif() endif() # @@ -155,7 +195,7 @@ if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr" OR "${CMAKE_INSTALL_PREFIX}" STREQU endif() # Installation paths -if(UNIX AND SYSTEM_DIR_INSTALL) +if(UNIX AND SYSTEM_DIR_INSTALL OR OPTION_BUILD_GUIX) # Install into the system (/usr/bin or /usr/local/bin) set(INSTALL_ROOT "share/${project}") # /usr/[local]/share/ set(INSTALL_CMAKE "share/${project}/cmake") # /usr/[local]/share//cmake @@ -195,33 +235,46 @@ if(SYSTEM_DIR_INSTALL) else() # Find libraries relative to binary if(APPLE) - set(CMAKE_INSTALL_RPATH "@loader_path/../../../${INSTALL_LIB}") + set(CMAKE_INSTALL_RPATH "@loader_path/${INSTALL_LIB}") else() set(CMAKE_INSTALL_RPATH "$ORIGIN/${INSTALL_LIB}") endif() endif() +# Export compile commands +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # # CTest configuration # -if(OPTION_BUILD_TESTS) +if(OPTION_BUILD_TESTS OR OPTION_BUILD_BENCHMARKS) enable_testing() endif() # -# Project module names +# Project module includes +# + +add_subdirectory(source) +add_subdirectory(docs) +add_subdirectory(deploy) + +# +# Project configuration generation # -set(MODULE_NAMES +set(EXPORT_MODULE_NAMES version preprocessor environment format + threading log memory portability adt + filesystem reflect dynlink detour @@ -231,48 +284,14 @@ set(MODULE_NAMES metacall ) -# -# Project export module names -# - -if(NOT OPTION_BUILD_DIST_LIBS) - set(EXPORT_MODULE_NAMES - ${MODULE_NAMES} - ) -else() - set(EXPORT_MODULE_NAMES - metacall - ) -endif() - -# -# Project module includes -# - -add_subdirectory(source) -add_subdirectory(docs) -add_subdirectory(deploy) - -# -# Coverage evaluation (must run after all targets) -# - -if(OPTION_COVERAGE) - coverage_evaluate() -endif() - -# -# Project configuration generation -# - set(PROJECT_CONFIGURATION "metacall-config.cmake") set(PROJECT_CONFIGURATION_VERSION "metacall-config-version.cmake") set(PROJECT_CONFIGURATION_PATH "${PROJECT_BINARY_DIR}/${PROJECT_CONFIGURATION}") set(PROJECT_CONFIGURATION_VERSION_PATH "${PROJECT_BINARY_DIR}/${PROJECT_CONFIGURATION_VERSION}") -configure_file("${PROJECT_CONFIGURATION}.in" "${PROJECT_CONFIGURATION_PATH}" @ONLY) -configure_file("${PROJECT_CONFIGURATION_VERSION}.in" "${PROJECT_CONFIGURATION_VERSION_PATH}" @ONLY) +configure_file("${CMAKE_SOURCE_DIR}/${PROJECT_CONFIGURATION}.in" "${PROJECT_CONFIGURATION_PATH}" @ONLY) +configure_file("${CMAKE_SOURCE_DIR}/${PROJECT_CONFIGURATION_VERSION}.in" "${PROJECT_CONFIGURATION_VERSION_PATH}" @ONLY) # # Deployment (global project files) @@ -292,3 +311,15 @@ install(FILES README.md DESTINATION ${INSTALL_ROOT} COMPONENT runtime) # # Install runtime data # install(DIRECTORY ${PROJECT_SOURCE_DIR}/data DESTINATION ${INSTALL_DATA} COMPONENT runtime) + +# +# Install githooks directory +# + +if(OPTION_GIT_HOOKS) + message(STATUS "Installing git hooks at ${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND git config --local core.hooksPath githooks/${PROJECT_OS_FAMILY} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif() diff --git a/CONTRIBUTORS b/CONTRIBUTORS index ea4451c8cf..014aa2a803 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -10,3 +10,10 @@ Declan Nnadozie Duxz Swarnim Arun Ben Schattinger +Muhammad Tabaza +Anas Al Barghouthy +ketangupta34 +onkardahale +Akshit Garg +burnerlee +Praveen Kumar \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 8f642ff810..a81cfd453b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/LICENSE b/LICENSE index 953b6f9b2f..d56ad81fad 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2016-2020 Vicente Eduardo Ferrer Garcia + Copyright 2016-2025 Vicente Eduardo Ferrer Garcia Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/NOTICE.md b/NOTICE similarity index 73% rename from NOTICE.md rename to NOTICE index 64d3172e01..687413ccf2 100644 --- a/NOTICE.md +++ b/NOTICE @@ -19,9 +19,8 @@ All external code and licenses used by **METACALL** are always wrapped into plug - [2. Serials](#2-serials) - [2.1 RapidJSON](#21-rapidjson) - [3. Detours](#3-detours) - - [3.1 FuncHook](#31-funchook) + - [3.1 PLTHook](#31-plthook) - [4. Ports](#4-ports) - - [4.1 Swig](#41-swig) @@ -80,24 +79,10 @@ All external code and licenses used by **METACALL** are always wrapped into plug ## 3. Detours -### 3.1 FuncHook +### 3.1 PLTHook -| Software | License | -| :----------: | :-------------------------------------------------------------------------------------------------: | -| **FuncHook** | [GPLv2 or later with a GPL linking exception](https://github.com/kubo/funchook/blob/master/LICENSE) | +| Software | License | +| :----------: | :------------------------------------------------------------------------------------------: | +| **PLTHook** | [2-clause BSD-style license](https://github.com/metacall/plthook?tab=readme-ov-file#license) | ## 4. Ports - -### 4.1 Swig - -| Software | License | -| :------: | :-----: | -| **SWIG** | **∅** | - ->When SWIG is used as it is distributed by the SWIG developers, its output is not governed by SWIG's license (including the GPL). SWIG's output contains code from three sources: -> -> - code generated by SWIG, which is not governed by copyright; -> - code copied from the SWIG library which is permissively licensed to be redistributed without restriction; -> - code derived from the user's input, which may be governed by the license of the code supplied by the user. -> ->So, while the input supplied to SWIG may affect the license of SWIG's output (e.g. if the input code is licensed under a copyleft or proprietary license), SWIG's license does not affect the license of the output. This is consistent with the FSF's FAQ entries on this subject ([GPLOutput](http://www.gnu.org/licenses/gpl-faq.html#GPLOutput) and [WhatCaseIsOutputGPL](http://www.gnu.org/licenses/gpl-faq.html#WhatCaseIsOutputGPL)), because the SWIG code copied into the output by SWIG is not GPL-licensed. diff --git a/README.md b/README.md index d172df9081..8248f30942 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,27 @@
- M E T A C A L L -

M E T A C A L L

-

A library for providing inter-language foreign function interface calls

+ METACALL +

MetaCall Polyglot Runtime

+ MetaCall.io | + Install | + Docs
+
+ + -# Abstract + + -**METACALL** is a library that allows calling functions, methods or procedures between programming languages. With **METACALL** you can transparently execute code from / to any programming language, for example, call a Python function from NodeJS. + + + + + + + M E T A C A L L +
+ +**MetaCall** allows calling functions, methods or procedures between multiple programming languages. `sum.py` ``` python @@ -16,725 +31,28 @@ def sum(a, b): `main.js` ``` javascript -const { sum } = require('sum.py'); +const { sum } = require('./sum.py'); sum(3, 4); // 7 ``` -Use the [installer](https://github.com/metacall/install) and try [some examples](https://github.com/metacall/beautifulsoup-express-example). - -
- M E T A C A L L -
- -# Table Of Contents - - - -- [Abstract](#abstract) -- [Table Of Contents](#table-of-contents) - - [1. Motivation](#1-motivation) - - [2. Language Support](#2-language-support) - - [2.1 Loaders (Backends)](#21-loaders-backends) - - [2.2 Ports (Frontends)](#22-ports-frontends) - - [3. Use Cases](#3-use-cases) - - [3.1 Known Projects Using MetaCall](#31-known-projects-using-metacall) - - [4. Usage](#4-usage) - - [4.1 Installation](#41-installation) - - [4.2 Environment Variables](#42-environment-variables) - - [4.3 Examples](#43-examples) - - [5. Architecture](#5-architecture) - - [5.1 Overview](#51-overview) - - [5.1.1 Design Decisions](#511-design-decisions) - - [5.1.2 Modules](#512-modules) - - [5.2 Reflect](#52-reflect) - - [5.2.1 Type System](#521-type-system) - - [5.2.2 Values](#522-values) - - [5.2.3 Functions](#523-functions) - - [5.3 Plugins](#53-plugins) - - [5.3.1 Loaders](#531-loaders) - - [5.3.1.1 Python](#5311-python) - - [5.3.1.2 NodeJS](#5312-nodejs) - - [5.3.1.3 JavaScript](#5313-javascript) - - [5.3.1.4 C#](#5314-c) - - [5.3.1.5 Ruby](#5315-ruby) - - [5.3.1.6 Mock](#5316-mock) - - [5.3.1.7 File](#5317-file) - - [5.3.2 Serials](#532-serials) - - [5.3.2.1 MetaCall](#5321-metacall) - - [5.3.2.2 RapidJSON](#5322-rapidjson) - - [5.3.3 Detours](#533-detours) - - [5.3.3.1 FuncHook](#5331-funchook) - - [5.4 Ports](#54-ports) - - [5.5 Serialization](#55-serialization) - - [5.6 Memory Layout](#56-memory-layout) - - [5.7 Fork Model](#57-fork-model) - - [5.8 Threading Model](#58-threading-model) - - [5. Application Programming Interface (API)](#5-application-programming-interface-api) - - [6. Build System](#6-build-system) - - [6.1 Build Options](#61-build-options) - - [6.2 Coverage](#62-coverage) - - [7. Platform Support](#7-platform-support) - - [7.1 Docker Support](#71-docker-support) - - [7.1.1 Docker Development](#711-docker-development) - - [7.1.2 Docker Testing](#712-docker-testing) - - [8. License](#8-license) - - - -## 1. Motivation - -The **METACALL** project started time ago when I was coding a [Game Engine for an MMORPG](https://bitbucket.org/parrastudios/argentum-online-c). My idea was to provide an interface to allow other programmers to extend the Game Engine easily. By that time, I was finishing the university so I decided to do my [Final Thesis](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-paper.pdf) and [Presentation](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-presentation.pdf) based on the plug-in system for my Game Engine. The Plugin Architecture designed for the Game Engine has similarities with **METACALL** although the architecture has been redefined and the code has been rewritten from scratch. After some refination of the system, I came up with **METACALL** and other use cases for the tool. Currently we are using **METACALL** to build a cutting edge FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on this technique to provide high scalability of the functions among multiple cores and **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern, a new technique I have developed to interconnect transparently functions in a distributed system based on this library. - -## 2. Language Support - -This section describes all programming languages that **METACALL** supports. **METACALL** is offered through a C API. This means you can use it as a library to embed different runtimes into C. The **[Loaders](#21-loaders-backends)** are the ones that allow to call different functions from C. They are plugins (libraries) which **METACALL** loads and they have a common interface. They usually implement JITs, VMs or Interpreters. On the other hand we have the **[Ports](#22-ports-frontends)** which are wrappers to the **METACALL** C API that expose the API to other languages. With the Python Loader we can execute calls to Python from C. With the Python Port we can install **METACALL** via pip and use it to call other languages from Python. The combination of both virtually provides full support to call from / to any language. - -### 2.1 Loaders (Backends) - -This section describes all programming languages that **METACALL** allows to load and invoke from C language, in other words all languages that **METACALL** can embed. If you are interested in design and implementation details of the loaders, please go to [loaders section](#531-loaders). - -- Currently supported languages and run-times: - -| Language | Runtime | Version | Tag | -|--------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|:------------------------------:|:----:| -| [Python](https://www.python.org/) | [Python C API](https://docs.python.org/3/c-api/intro.html) | **>= 3.2 <= 3.7** | py | -| [NodeJS](https://nodejs.org/) | [N API](https://nodejs.org/api/n-api.html) | **10.22.0** | node | -| [TypeScript](https://www.typescriptlang.org/) | [TypeScript Language Service API](https://github.com/microsoft/TypeScript/wiki/Using-the-Language-Service-API) | **3.9.7** | ts | -| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [V8](https://v8.dev/) | **5.1.117** | js | -| [C#](https://dotnet.microsoft.com/) | [NetCore](https://github.com/dotnet/docs/blob/master/docs/core/tutorials/netcore-hosting.md) | **>= 1.0.0-preview2 <= 2.2.8** | cs | -| [Ruby](https://ruby-lang.org/) | [Ruby C API](https://silverhammermba.github.io/emberb/c/) | **>= 2.1 <= 2.5** | rb | -| [Cobol](https://sourceforge.net/projects/open-cobol/) | [GNU/Cobol](https://open-cobol.sourceforge.io/doxygen/gnucobol-2/libcob_8h.html) | **>= 1.1.0** | cob | -| [File](/source/loaders/file_loader) | **∅** | **0.1.0** | file | -| [Mock](/source/loaders/mock_loader) | **∅** | **0.1.0** | mock | - -- Languages and run-times under construction: - -| Language | Runtime | Tag | -|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|:----:| -| [WebAssembly](https://webassembly.org/) | [WebAssembly Virtual Machine](https://github.com/WAVM/WAVM) | wasm | -| [C/C++](http://www.cplusplus.com/) | [Clang](https://clang.llvm.org/) - [LLVM](https://llvm.org/) - [libffi](http://sourceware.org/libffi/) | c | -| [Java](https://www.java.com/) | [JNI](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/) | java | -| [PHP](https://php.net/) | [Zend](https://www.php.net/manual/en/internals2.ze1.zendapi.php) | php | -| [Go](https://golang.org/) | Go Runtime | go | -| [Haskell](https://www.haskell.org/) | [Haskell FFI](https://wiki.haskell.org/GHC/Using_the_FFI) | hs | -| [Crystal](https://crystal-lang.org/) | [Crystal Compiler Internals](https://github.com/crystal-lang/crystal/wiki/Compiler-internals) | cr | -| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [SpiderMonkey](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference) | jsm | -| [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) | [cURL](https://curl.haxx.se/) | rpc | -| [Dart](https://dart.dev/) | [Dart VM](https://dart.dev/tools/dart-vm) | dart | - -### 2.2 Ports (Frontends) - -Ports are the frontends to the **METACALL C API** from other languages. They allow to use **METACALL** from different languages. If you are interested in design and implementation details of the ports, please go to [ports section](#54-ports). - -- Currently supported languages and run-times: - -| Language | Runtime | Version | -|--------------------------------------------------------------------|------------------------------------------------------------|:---------------------:| -| [Python](https://www.python.org/) | [Python C API](https://docs.python.org/3/c-api/intro.html) | **3.x** | -| [NodeJS](https://nodejs.org/) | [N API](https://nodejs.org/api/n-api.html) | **>= 8.11.1** | -| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [D8 (V8)](https://v8.dev/docs/d8) | **5.1.117** | -| [C#](https://dotnet.microsoft.com/) | [NetCore](https://github.com/dotnet/core) | **>= 1.0.0-preview2** | -| [Ruby](https://ruby-lang.org/) | [Ruby C API](https://silverhammermba.github.io/emberb/c/) | **2.x** | -| [Go](https://golang.org/) | [CGO](https://golang.org/cmd/cgo/) | **1.x** | -| [D](https://dlang.org/) | [DMD](https://wiki.dlang.org/DMD) | **2.x** | -| [Rust](https://www.rust-lang.org/) | **∅** | **>= 1.47.0** | - -## 3. Use Cases - -**METACALL** can be used in the following cases: - -- Interconnect different technologies in the same project. It allows to have heterogeneous teams of developers working over same project in an isolated way and using different programming languages at the same time. - -- Embedding programming languages to existing softwares. Game Engines, 3D Editors like [Blender](https://www.blender.org/), among others can take benefit of **METACALL** and extend the core functionality with higher level programming languages (aka scripting). - -- Function as a Service. **METACALL** can be used to implement efficient FaaS architectures. We are using it to implement our own FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern and high performance function scalability thanks to this library. - -- Source code migrations. **METACALL** can wrap large and legacy code-bases, and provide an agnostic way to work with the codebase into a new programming language. Eventually the code can be migrated by parts, without need of creating a new project or stopping the production environment. Incremental changes can be done, solving the migration easily and with less time and effort. - -- Porting low level libraries to high level languages transparently. With **METACALL** you can get rid of extension APIs like Python C API or NodeJS N-API. You can call directly low level libraries from your high level languages without making a wrapper in C or C++ for it. - -As you can see, there are plenty of uses. **METACALL** introduces a new model of programming which allows a high interoperability between technologies. If you find any other use case just let us know about it with a Pull Request and we will add it to the list. - -## 3.1 Known Projects Using MetaCall - -- **[Acid Cam](https://www.facebook.com/AcidCam/)**: A software for video manipulation that distorts videos for generating art by means of OpenCV. [Acid Cam CLI](https://github.com/lostjared/acidcam-cli) uses **METACALL** to allow custom filters written in Python and easily embed Python programming language into its plugin system. - -## 4. Usage - -## 4.1 Installation - -Prior to try any example, you must have **METACALL** installed in your system. To install **METACALL** you have the following options. - -- [Install precompiled tarball via shell script](https://github.com/metacall/install). -- [Download a release through a precompiled tarball](https://github.com/metacall/distributable/releases). -- [Build and install it manually](#6-build-system). -- [Pull it from DockerHub](https://hub.docker.com/r/metacall/core). -- [Install via Guix package manager](https://github.com/metacall/distributable/blob/master/source/metacall.scm). - -### 4.2 Environment Variables - -This environment variables are optional, in case that you want to modify default paths of **METACALL**. - -| Name | Description | Default Value | -|:-------------------------:|------------------------------------------------------------------|:--------------------------------:| -| **`DETOUR_LIBRARY_PATH`** | Directory where detour plugins to be loaded are located | **`detours`** | -| **`SERIAL_LIBRARY_PATH`** | Directory where serial plugins to be loaded are located | **`serials`** | -| **`CONFIGURATION_PATH`** | File path where the **METACALL** global configuration is located | **`configurations/global.json`** | -| **`LOADER_LIBRARY_PATH`** | Directory where loader plugins to be loaded are located | **`loaders`** | -| **`LOADER_SCRIPT_PATH`** | Directory where scripts to be loaded are located | **`${execution_path}`** ¹ | - -¹ **`${execution_path}`** defines the path where the program is executed, **`.`** in Linux. - -### 4.3 Examples - -- [Embedding NodeJS](https://github.com/metacall/embedding-nodejs-example): Example application for embedding NodeJS code into C/C++ using CMake as a build system. - -- [Embedding Python](https://github.com/metacall/embedding-python-example): Example application for embedding Python code into C/C++ using CMake as a build system. - -- [Using `matplotlib` from C/C++](https://github.com/metacall/embedding-matplotlib-example): Example application for using Python `matplotlib` library into C/C++ using `gcc` for compiling it and installing MetaCall by compining it by hand. - -- [MetaCall CLI](/source/examples/metacallcli): Example of a Command Language Interpreter based on MetaCall where you can load, unload scripts and call their functions. - -- [Rotulin](https://github.com/metacall/rotulin): Example of a multi-language application built with **METACALL**. This application embeds a Django server with a Ruby DataBase and C# business layer based on ImageMagick. - -- [BeautifulSoup from Express](https://github.com/metacall/beautifulsoup-express-example): This example shows how to use [MetaCall CLI](/source/examples/metacallcli) for building a **Polyglot Scraping API** that mixes NodeJS with Python. - -## 5. Architecture - -### 5.1 Overview - -#### 5.1.1 Design Decisions - -- To provide an high level API with a simple UX and to be easy to understand. - -- To work in high performance environments. - -- To be as cross-platform as possible. - -- To avoid to modify run-times directly or use the code inside **METACALL** in order to avoid maintaining them, or propagating security flaws or licenses into **METACALL**. - -- To provide support for any embeddable programming language and to provide support for **METACALL** to be used form any programming language. - -- All external code used into **METACALL** must be introduced by inversion of control in the plugin system, so that the core must not remain aware from what software is using. - -- All code developed in **METACALL** must be implemented in standalone libraries that can work by itself in an isolated way (aka modules). - -#### 5.1.2 Modules - -- [`adt`](/source/adt) provides a base for Abstract Data Types and algorithms used in **METACALL**. Implementation must be done in an efficient and generic way. Some of the data structures implemented are vector, set, hash, comparable or trie. - -- [`detour`](/source/detour) provides an interface to hook into functions. Detours are used by the [fork model](#57-fork-model) to intercept fork calls. - -- [`detours`](/source/detours) implement the [`detour`](/source/detour) interface by using a plugin architecture. The current list of available detour plugins is the following one. - - [`funchook_detour`](/source/detours/funchook_detour) implemented by means of FuncHook library. - -- [`distributable`](/source/distributable) defines the compilation of **METACALL** that generates an unique library with all core libraries bundled into it. As the **METACALL** architecture is divided by modules, in order to distribute **METACALL** is needed to build all of them into a single library. This module implements this compilation by means of CMake. - -- [`dynlink`](/source/dynlink) implements a cross-platform method to dynamically load libraries. It is used to dynamically load plugins into **METACALL**. - -- [`environment`](/source/environment) implements an standard way to deal with environment variables. **METACALL** uses environment variables to define custom paths for plugins and scripts. - -- [`examples`](/source/examples) ... - -- [`filesystem`](/source/filesystem) provides an abstraction for operative system file system. - -- [`format`](/source/format) provides an standard way for printing to standard input output for old C versions that does not support newest constructions. - -- [`loader`](/source/loader) ... - -- [`loaders`](/source/loaders) - -- [`log`](/source/log) - -- [`memory`](/source/memory) - -- [`metacall`](/source/metacall) - -- [`ports`](/source/ports) - -- [`preprocessor`](/source/preprocessor) - -- [`reflect`](/source/reflect) - -- [`scripts`](/source/scripts) - -- [`serial`](/source/serial) - -- [`serials`](/source/serials) - -- [`tests`](/source/tests) - -- [`version`](/source/version) - -### 5.2 Reflect - -The module that holds the representation of types, values and functions is called [`reflect`](/source/reflect) and it handles the abstraction of code loaded into **METACALL**. - -**METACALL** uses reflection and introspection techniques to inspect the code loaded by the [`loaders`](/source/loaders) in order to interpret it and provide an higher abstraction of it. With this higher abstraction **METACALL** can easily inter-operate between languages transparently. - -#### 5.2.1 Type System - -**METACALL** implements an abstract type system which is a binary representation of the types supported by it. This means that **METACALL** can convert any type of a language to its own type system and back. Each loader is responsible of doing this conversions. - -**METACALL** maintains most of the types of the languages but not all are supported. If new types are added they have to be implemented in the [`reflect`](/source/reflect) module and also in the [`loaders`](/source/loaders) and [`serials`](/source/serials) to fully support it. - -| Type | Value | -|:-------:|--------------------------------------------------------------------| -| Boolean | `true` or `false` | -| Char | `-128` to `127` | -| Short | `-32,768` to `32,767` | -| Int | `-2,147,483,648` to `2,147,483,647` | -| Long | `–9,223,372,036,854,775,808` to `9,223,372,036,854,775,807` | -| Float | `1.2E-38` to `3.4E+38` | -| Double | `2.3E-308` to `1.7E+308` | -| String | NULL terminated list of characters | -| Buffer | Blob of memory representing a binary data | -| Array | Arrangement of values of any type | -| Map | List of elements formed by a key (String) value (Any) pair (Array) | -| Pointer | Low level representation of a memory reference | -| Null | Representation of NULL value type | - -- Boolean is mostly represented by an integer value. There are languages that does not support it so it gets converted to a integer value in the memory layout. - -- Integer and Floating Point values provide a complete abstraction to numerical types. Type sizes are preserved and the correct type is used when using any number. This depends on the internal implementation of the value by the run-time. Although there can be problems related to this. A `bignum` type from Ruby may overflow if it is too big when trying to convert it to a `float` type in C#. - -- String is represented by ASCII encoding currently. Future versions will implement multiple encodings to be interoperable between other language encodings. - -- Buffer represents a blob of raw memory (i.e. an array of bytes). This can be used to represent files as images or any other resources into memory. - -- Array is implemented by means of array of values, which you can think it should be called _list_ instead. But as the memory layout is stored into a contiguous memory block of references to values, it is considered an array. - -- Map implements an associative key value pair container. A map is implemented with an array of two sized elements array. Each element of the map is an array of size two, where the first element of it is always an String and the second element is a value of any type. - -- Pointer is an opaque value representing a raw reference to a memory block. Some languages allow to use references to memory and some others not. This type is opaque because **METACALL** does not know what kind of concrete value represents it. The representation may be a complex type handled by the developer source code inside the run-time. - -- Null type implements a null value. This type has only been implemented in order to support null value from multiple run-times. It represents a null value and it does not have data size on the value allocated. - -#### 5.2.2 Values - -Values represent the instances of the **METACALL** type system. - -The memory layout guarantees to fit at least the same size of the types into memory. This means if a boolean type can be represented with one bit inside a value of one byte size, maybe this value is stored in a bigger memory block and this fact is architecture and platform dependant. - -When converting values between different types, if any potential number overflow or invalid conversion between types is done, **METACALL** will warn about it. If any conversion of types can be handled by **METACALL**, it will automatically cast or transform the values into the target type automatically in order to avoid errors in the call. - -The value model is implemented by means of object pool. Each value is a reference to a memory block allocated from a memory pool (which can be injected into **METACALL**). The references can be passed by value, this means **METACALL** copies the reference value instead of the data which this reference is pointing to, like most run-times do when managing their own values. - -Each created value must be destroyed manually. Otherwise it will lead to a memory leak. This fact only occurs when dealing with **METACALL** at C level. If **METACALL** is being used in an higher language through [`ports`](/source/ports), the developer does not have to care about memory management. - -The value memory layout is described in the following form. - -| Memory Offset | `0` to `sizeof(data) - 1` | `sizeof(data)` to `sizeof(data) + sizeof(type_id) - 1` | -|:-------------:|:-------------------------:|:------------------------------------------------------:| -| **Content** | **DATA** | **TYPE ID** | - -This layout is used by the following reasons. - -- Data is located at the first position of the memory block, so it can be used as a normal low level value. This allows to threat **METACALL** values as a normal C values. Therefore you can use **METACALL** with normal pointers to existing variables, literal values as shown in the previous examples or **METACALL** values. - -- Data can be accessed faster as it is located at first position of the memory block. There is not extra calculation of an offset when trying to access the pointer. - -- Data and type id are contiguously allocated in order to threat it as the same memory block so it can be freed with one operation. - -#### 5.2.3 Functions - -Functions are an abstract callable representation of functions, methods or procedures loaded by [`loaders`](/source/loaders). The functions are like a template who is linked to a loader run-time and allows to do a foreign function call. - -A function is composed by a name and a signature. The signature defines the arguments name, type, and return type if any. When a function is loaded, **METACALL** tries to inspect the signature and records the types if any. It stores the arguments name and size and also a concrete type that will be used later by the loader to implement the call to the run-time. - -The function interface must be implemented by the [`loaders`](/source/loaders) and it has the following form. - -``` c -typedef struct function_interface_type -{ - function_impl_interface_create create; - function_impl_interface_invoke invoke; - function_impl_interface_await await; - function_impl_interface_destroy destroy; - -} * function_interface; -``` - -- `create` instantiates the function concrete data related to the run-time. -- `invoke` transforms arguments from [`reflect`](/source/reflect) abstract types to run-time concrete types, executes the call in the run-time, and converts the result of the call from run-time concrete type to [`reflect`](/source/reflect) abstract type. -- `await` idem to invoke but awaiting the promise that is expected to be returned by the function. -- `destroy` clears all data previously instantiated in `create`. - -The type deduction can be done at different levels. For example, it is possible to guess function types from the loaded code. - -``` python -def multiply_type(a: int, b: int) -> int: - return a * b -``` - -If this code is loaded, **METACALL** will be able to inspect the types and define the signature. Signature includes the names of the arguments, the types of those arguments if any, and the return type if any. - -It may be possible that the function loaded into **METACALL** is duck typed. This means it does not have information about what types it supports and therefore they cannot be inspected statically. - -``` python -def multiply_duck(a, b): - return a * b -``` - -At low level **METACALL** must always know the types to do the call. This types can be inferred statically or dynamically and this has implications over the call model. - -In the first example, we can simply call the function without specifying the types. - -``` c -metacall("multiply_type", 3, 4); // 12 -``` - -As the signature is already know the literal values `3` and `4` can be converted into **METACALL** values automatically. Note that in this case, as literal values are provided, if we pass a double floating point, the memory representation of the value will be corrupted as there is no possible way to detect input values and cast them to the correct target values. - -In the second example, the values are not know. If we use the same API to call the function, **METACALL** will not be able to call correctly the function as its types are not know. To allow calls to duck typed functions the developer must specify the value types he is passing to the function. - -``` c -const enum metacall_value_id multiply_types[] = -{ - METACALL_INT, METACALL_INT -}; - -metacallt("multiply_duck", multiply_types, 3, 4); // 12 -``` - -This method allows to pass different value types to the same function. The following call would be valid too. - -``` c -const enum metacall_value_id multiply_types[] = -{ - METACALL_DOUBLE, METACALL_DOUBLE -}; - -metacallt("multiply_duck", multiply_types, 3.0, 4.0); // 12.0 -``` - -### 5.3 Plugins - -**METACALL** has a plugin architecture implemented at multiple levels. - -- Loaders implement a layer of plugins related to the run-times. - -- Serials implement a layer of (de)serializers in order to transform input (arguments) or output (return value) of the calls into a generic format. - -- Detours is another layer of plugins focused on low level function interception (hooks). - -Each plugin is a piece of software that can be dynamically loaded into the **METACALL** core, used and unloaded when it is not needed anymore. - -#### 5.3.1 Loaders - -Loaders are responsible for embedding run-times into **METACALL**. Each loader has the following interface. - -``` c -typedef struct loader_impl_interface_type -{ - loader_impl_interface_initialize initialize; - loader_impl_interface_execution_path execution_path; - loader_impl_interface_load_from_file load_from_file; - loader_impl_interface_load_from_memory load_from_memory; - loader_impl_interface_load_from_package load_from_package; - loader_impl_interface_clear clear; - loader_impl_interface_discover discover; - loader_impl_interface_destroy destroy; - -} * loader_impl_interface; -``` - -A loader must implement it to be considered a valid loader. - -- `initialize` starts up the run-time. -- `execution_path` defines a new import path to the run-time. -- `load_from_file` loads a code from file into the run-time and returns a handle which represents it. -- `load_from_memory` loads a code from memory into the run-time and returns a handle which represents it. -- `load_from_package` loads a code from a compiled library or package into the run-time and returns a handle which represents it. -- `clear` unloads a handle from the run-time. -- `discover` inspects a handle previously loaded. -- `destroy` shutdowns the run-time. - -##### 5.3.1.1 Python - -##### 5.3.1.2 NodeJS - -##### 5.3.1.3 JavaScript - -##### 5.3.1.4 C# # - -##### 5.3.1.5 Ruby - -##### 5.3.1.6 Mock - -##### 5.3.1.7 File - -#### 5.3.2 Serials - -##### 5.3.2.1 MetaCall - -##### 5.3.2.2 RapidJSON - -#### 5.3.3 Detours - -##### 5.3.3.1 FuncHook - -### 5.4 Ports - -### 5.5 Serialization - -### 5.6 Memory Layout - -### 5.7 Fork Model - -**METACALL** implements a fork safe model. This means if **METACALL** is running in any program instance, the process where is running can be forked safely at any moment of the execution. This fact has many implications at design, implementation and use level. But the whole **METACALL** architecture tries to remove all responsibility from the developer and make this transparent. - -To understand the **METACALL** fork model, first of all we have to understand the implications of the forking model in operative systems and the difference between [fork-one and fork-all models](https://docs.oracle.com/cd/E37838_01/html/E61057/gen-1.html). - -The main difference between fork-one and fork-all is that in fork-one only the thread which called the fork is preserved after the fork (i.e. gets cloned). In fork-all model, all threads are preserved after cloning. POSIX uses fork-one model, meanwhile Oracle Solaris use the fork-all model. - -Because of fork-one model, forking a running run-time like NodeJS (which has a thread pool) implies that in the child process the thread pool will be almost dead except the thread which did the fork call. So NodeJS run-time cannot continue the execution anymore and the event-loop enters into a deadlock state. - -When a fork is done, the status of the execution is lost by the moment. **METACALL** is not able to preserve the state when a fork is done. Some run-times do not allow to preserve the internal state. For example, the bad design[[0]](https://github.com/nodejs/node/issues/23265)[[1]](https://github.com/nodejs/node/issues/23265#issuecomment-452690239)[[2]](https://github.com/nodejs/node/issues/23265#issuecomment-496873739)[[3]](https://github.com/nodejs/node/issues/23265#issuecomment-496878712)[[4]](https://github.com/nodejs/node/issues/23265#issuecomment-496910654)[[5]](https://github.com/nodejs/node/issues/23265#issuecomment-496918901) of NodeJS does not allow to manage the thread pool from outside, so it cannot be preserved after a fork. - -Because of these restrictions, **METACALL** cannot preserve the status of the run-times. In the future this model will be improved to maintain consistency and preserve the execution state of the run-times making **METACALL** more robust. - -Although the state is not preserved, fork safety is. The mechanism **METACALL** uses to allow fork safety is described in the following enumeration. - -1) Intercept fork call done by the program where **METACALL** is running. - -2) Shutdown all run-times by means of unloading all loaders. - -3) Execute the real fork function. - -4) Restore all run-times by means of reloading all loaders. - -5) Execute user defined fork callback if any. - -To achieve this, **METACALL** hooks fork primitives depending on the platform. - -- `fork` on POSIX systems. -- `RtlCloneUserProcess` on Windows systems. - -If you use `clone` instead of `fork` to spawn a new process in a POSIX system, **METACALL** won't catch it. - -Whenever you call a to a cloning primitive **METACALL** intercepts it by means of [**`detour`**](/source/detour). Detours is a way to intercept functions at low level by editing the memory and introducing a jump over your own function preserving the address of the old one. **METACALL** uses this method instead of POSIX `pthread_atfork` for three main reasons. - -- The first one is that `pthread_atfork` is only supported by POSIX systems. So it is not a good solution because of the philosophy of **METACALL** is to be as cross-platform as possible. - -- The second is that `pthread_atfork` has a [bug in the design of the standard](https://stackoverflow.com/a/6605487). It was designed to solve a problem which cannot be solved with `pthread_atfork` itself. This means that even having the control of NodeJS thread pool, it will not be possible to restore the [mutexes](https://github.com/nodejs/node/blob/v8.x/src/node_platform.cc) in the child process. The only possibility is to re-implement the thread pool of NodeJS with async safe primitives like a semaphore. Async safe primitives will be able to work in the child process handler. But this is not possible as it enters in conflict with the design decision of to not modify the run-times. - -- The third one is that the mechanism of `pthread_atfork` also [will be deprecated](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html) because of second reason. - > The `pthread_atfork()` function may be formally deprecated (for example, by shading it OB) in a future version of this standard. - -Detours model is not safe. It is platform dependant and implies that the program modifies the memory of itself during the execution which is not safe at all and can induce bugs or security flaws if it is not done correctly. But because of limitations of run-times, there is not another alternative to solve the problem of fork safety. - -Usually the developer is the same who does the fork, but it may be possible that **METACALL** is embedded into a larger application and the developer is in the middle between the application code and **METACALL** so it is impossible to control when a fork is done. Because of this the developer can register a callback by means of [**`metacall_fork`**](/source/metacall/include/metacall/metacall_fork.h) to know when a fork is executed to do the actions needed after the fork, for example, re-loading all previous code and restore the state of the run-times. This gives a partial solution to the problem of losing the state when doing a fork. - -### 5.8 Threading Model - -## 5. Application Programming Interface (API) - -## 6. Build System - -Follow these steps to build and install **METACALL** manually. - +`shell` ``` sh -git clone --recursive https://github.com/metacall/core.git -mkdir core/build && cd core/build -cmake .. -cmake --build . --target install +metacall main.js ``` -### 6.1 Build Options - -These options can be set using **`-D`** prefix when configuring CMake. For example, the following configuration enables the build of Python and Ruby loaders. +**MetaCall** is an extensible, embeddable, and interoperable cross-platform polyglot runtime. It supports NodeJS, Vanilla JavaScript, TypeScript, Python, Ruby, C#, Java, WASM, Go, C, C++, Rust, D, Cobol [and more](https://github.com/metacall/core/blob/develop/docs/README.md#2-language-support). -``` sh -cmake -DOPTION_BUILD_LOADERS_PY=On -DOPTION_BUILD_LOADERS_RB=On .. -``` +## Install -Available build options are the following ones. - -| Build Option | Description | Default Value | -|:---------------------------:|--------------------------------------------------------|:-------------:| -| **BUILD_SHARED_LIBS** | Build shared instead of static libraries. | ON | -| **OPTION_BUILD_DIST_LIBS** | Build all libraries into a single compilation unit. | ON | -| **OPTION_SELF_CONTAINED** | Create a self-contained install with all dependencies. | OFF | -| **OPTION_BUILD_TESTS** | Build tests. | ON | -| **OPTION_BUILD_BENCHMARKS** | Build benchmarks. | OFF | -| **OPTION_BUILD_DOCS** | Build documentation. | OFF | -| **OPTION_BUILD_EXAMPLES** | Build examples. | ON | -| **OPTION_BUILD_LOADERS** | Build loaders. | ON | -| **OPTION_BUILD_SCRIPTS** | Build scripts. | ON | -| **OPTION_BUILD_SERIALS** | Build serials. | ON | -| **OPTION_BUILD_DETOURS** | Build detours. | ON | -| **OPTION_BUILD_PORTS** | Build ports. | OFF | -| **OPTION_FORK_SAFE** | Enable fork safety. | OFF | -| **OPTION_THREAD_SAFE** | Enable thread safety. | OFF | -| **OPTION_COVERAGE** | Enable coverage. | OFF | -| **CMAKE_BUILD_TYPE** | Define the type of build. | Release | - -It is possible to enable or disable concrete loaders, script, ports, serials or detours. For building use the following options. - -| Build Option Prefix | Build Option Suffix | -|:-------------------------:|-----------------------------------------------------------------------| -| **OPTION_BUILD_LOADERS_** | `C` `JS` `CS` `MOCK` `PY` `JSM` `NODE` `RB` `JSM` `FILE` | -| **OPTION_BUILD_SCRIPTS_** | `C` `CS` `JS` `NODE` `PY` `RB` `JAVA` | -| **OPTION_BUILD_SERIALS_** | `METACALL` `RAPID_JSON` | -| **OPTION_BUILD_DETOURS_** | `FUNCHOOK` | -| **OPTION_BUILD_PORTS_** | `CS` `CXX` `D` `GO` `JAVA` `JS` `LUA` `NODE` `PHP` `PL` `PY` `R` `RB` | - -### 6.2 Coverage - -In order to run code coverage and obtain html reports use the following commands. Note, test must be run before executing code coverage. +The easiest way to install **MetaCall** is the following: ``` sh -make -make test -make -k gcov -make -k lcov -make -k lcov-genhtml +curl -sL https://raw.githubusercontent.com/metacall/install/master/install.sh | sh ``` -The output reports will be generated in `${CMAKE_BINARY_DIR}/lcov/html/selected_targets` in html format. - -To obtain a report of a single `target` do: - -``` sh -make -make test -make -gcov -make -geninfo -make -genhtml -``` - -## 7. Platform Support - -The following platforms and architectures have been tested an work correctly with all plugins of **METACALL**. - -| Operative System | Architecture | Compiler | Build Status | -|:------------------------:|:-------------------:|:---------------:|:------------------------------------------------------------------------------------------------------:| -| **`ubuntu:xenial`** | **`amd64`** | **`gcc`** | | -| **`debian:buster-slim`** | **`amd64`** | **`gcc:6.3.0`** | [![build](https://gitlab.com/metacall/core/badges/master/build.svg)](https://gitlab.com/metacall/core) | -| **`debian:buster-slim`** | **`amd64`** | **`gcc:8.2.0`** | | -| **`windows`** | **`x86`** **`x64`** | **`msvc`** | | - -### 7.1 Docker Support - -To provide a reproducible environment **METACALL** is also distributed under Docker on [DockerHub](https://hub.docker.com/r/metacall/core). Current images are based on `debian:buster-slim` for `amd64` architecture. - -For pulling the **METACALL** `latest` image containing the runtime, use: - -``` sh -docker pull metacall/core -``` - -For pulling a specific image depending on the tag, use: - -- **METACALL** `deps` image. Includes all dependencies for development: - -``` sh -docker pull metacall/core:deps -``` - -- **METACALL** `dev` image. Includes all dependencies, headers and libraries for development: - -``` sh -docker pull metacall/core:dev -``` - -- **METACALL** `runtime` image. Includes all dependencies and libraries for runtime: - -``` sh -docker pull metacall/core:runtime -``` - -- **METACALL** `cli` image. Includes all dependencies and libraries for runtime and the CLI as entry point (equivalent to `latest`): - -``` sh -docker pull metacall/core:cli -``` - -### 7.1.1 Docker Development - -It is possible to develop **METACALL** itself or applications using **METACALL** as standalone library with Docker. The `dev` image can be used for development. It contains all dependencies with all run-times installed with the code, allowing debugging too. - -Use the following commands to start developing with **METACALL**: - -``` sh -mkdir -p $HOME/metacall -code $HOME/metacall -``` - -We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. - -``` sh -docker pull metacall/core:dev -docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:dev /bin/bash -``` - -Inside docker terminal you can run `python` or `ruby` command to test what you are developing. You can also run `metacallcli` to test (load, clear, inspect and call). - -### 7.1.2 Docker Testing - -An alternative for testing is to use a reduced image that includes the runtime and also the CLI. This alternative allows fast prototyping and CLI management in order to test and inspect your own scripts. - -Use the following commands to start testing with **METACALL**: - -``` sh -mkdir -p $HOME/metacall -code $HOME/metacall -``` - -We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. - -``` sh -docker pull metacall/core:cli -docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:cli -``` - -After the container is up, it is possible to load any script contained in host folder `$HOME/metacall`. If we have a `script.js` inside the folder, we can just load it (each line beginning with `>` is the input command): - -`script.js` -``` js -function sum(left, right) { - return left + right; -} - -module.exports = { - sum -}; -``` - -`Command Line Interface` -``` sh -> load node script.js -Script (script.js) loaded correctly -> inspect -runtime node { - module script { - function sum(left, right) - } -} -runtime __metacall_host__ -> call sum(3, 5) -8.0 -> exit -``` - -Where `script.js` is a script contained in host folder `$HOME/metacall` that will be loaded on the CLI after starting up the container. Type `help` to see all available CLI commands. - -## 8. License +For more information about other install methodologies and platforms or Docker, check the [install documentation](https://github.com/metacall/core/blob/develop/docs/README.md#41-installation). -**METACALL** is licensed under **[Apache License Version 2.0](/LICENSE)**. +## Examples ->Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia <> -> ->Licensed under the Apache License, Version 2.0 (the "License"); ->you may not use this file except in compliance with the License. ->You may obtain a copy of the License at -> -> http://www.apache.org/licenses/LICENSE-2.0 -> ->Unless required by applicable law or agreed to in writing, software ->distributed under the License is distributed on an "AS IS" BASIS, ->WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ->See the License for the specific language governing permissions and ->limitations under the License. +You can find a complete [list of examples in the documentation](https://github.com/metacall/core/blob/develop/docs/README.md#43-examples). If you are interested in submitting new examples, please [contact us in our chats](#badges). diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000..69010fa5be --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.9.14 \ No newline at end of file diff --git a/cmake/CMakeDebug.cmake b/cmake/CMakeDebug.cmake index 796ced0303..dd80a221b9 100644 --- a/cmake/CMakeDebug.cmake +++ b/cmake/CMakeDebug.cmake @@ -2,7 +2,7 @@ # CMake Debug Utilities by Parra Studios # CMake debugging utilities and inspection facilities. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/CheckCCompilerFlagStackSmashing.c b/cmake/CheckCCompilerFlagStackSmashing.c index 4e303640d7..2a018d92e6 100644 --- a/cmake/CheckCCompilerFlagStackSmashing.c +++ b/cmake/CheckCCompilerFlagStackSmashing.c @@ -1,4 +1,4 @@ -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { char buffer[2]; diff --git a/cmake/CheckCCompilerFlagStackSmashing.cmake b/cmake/CheckCCompilerFlagStackSmashing.cmake index 80796f97fc..ea31ed5d77 100644 --- a/cmake/CheckCCompilerFlagStackSmashing.cmake +++ b/cmake/CheckCCompilerFlagStackSmashing.cmake @@ -2,7 +2,7 @@ # Compiler checker for stack smashing flags by Parra Studios # Tests if a defined stack smashing security flag is available. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/CheckCXXCompilerFlagStackSmashing.cmake b/cmake/CheckCXXCompilerFlagStackSmashing.cmake index 6ba4ba5e23..9fa7f95891 100644 --- a/cmake/CheckCXXCompilerFlagStackSmashing.cmake +++ b/cmake/CheckCXXCompilerFlagStackSmashing.cmake @@ -2,7 +2,7 @@ # Compiler checker for stack smashing flags by Parra Studios # Tests if a defined stack smashing security flag is available. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/CheckCXXCompilerFlagStackSmashing.cpp b/cmake/CheckCXXCompilerFlagStackSmashing.cpp index 4e303640d7..2a018d92e6 100644 --- a/cmake/CheckCXXCompilerFlagStackSmashing.cpp +++ b/cmake/CheckCXXCompilerFlagStackSmashing.cpp @@ -1,4 +1,4 @@ -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { char buffer[2]; diff --git a/cmake/ClangDevTools.cmake b/cmake/ClangDevTools.cmake new file mode 100644 index 0000000000..3013037744 --- /dev/null +++ b/cmake/ClangDevTools.cmake @@ -0,0 +1,37 @@ +# See FindClangFormat.cmake +# Variables of interest on this file: ${ClangFormat_VERSION} and ${ClangFormat_EXECUTABLE} + +# Get only C/C++ files for now +file(GLOB_RECURSE + ALL_SOURCE_FILES + LIST_DIRECTORIES OFF + FOLLOW_SYMLINKS + ${CMAKE_SOURCE_DIR}/source/**/*.cpp + ${CMAKE_SOURCE_DIR}/source/**/*.hpp + ${CMAKE_SOURCE_DIR}/source/**/*.h + ${CMAKE_SOURCE_DIR}/source/**/*.c + ${CMAKE_SOURCE_DIR}/source/**/*.cc + ${CMAKE_SOURCE_DIR}/source/**/*.hh + ${CMAKE_SOURCE_DIR}/source/**/*.cxx + ${CMAKE_SOURCE_DIR}/source/**/*.inl +) + +# clang-tidy not implemented yet +#add_custom_target( +# clang-tidy +# COMMAND /usr/bin/clang-tidy +# ${ALL_SOURCE_FILES} +# -config='' +# -- +# -std=c++11 +# ${INCLUDE_DIRECTORIES} +#) + +add_custom_target( + clang-format + COMMAND ${ClangFormat_EXECUTABLE} + --verbose + -style=file + -i + ${ALL_SOURCE_FILES} +) diff --git a/cmake/CompileOptions.cmake b/cmake/CompileOptions.cmake index d4b9eb7211..b3cd23a466 100644 --- a/cmake/CompileOptions.cmake +++ b/cmake/CompileOptions.cmake @@ -2,7 +2,13 @@ # Compile options configuration # -option(OPTION_BUILD_SANITIZER "Build with sanitizer compiler options." OFF) +option(OPTION_BUILD_ADDRESS_SANITIZER "Build with sanitizer compiler options." OFF) +option(OPTION_BUILD_THREAD_SANITIZER "Build with thread sanitizer compiler options." OFF) +option(OPTION_BUILD_MEMORY_SANITIZER "Build with memory sanitizer compiler options." OFF) + +if((OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_MEMORY_SANITIZER) OR (OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_THREAD_SANITIZER) OR (OPTION_BUILD_MEMORY_SANITIZER AND OPTION_BUILD_THREAD_SANITIZER)) + message(FATAL_ERROR "OPTION_BUILD_ADDRESS_SANITIZER and OPTION_BUILD_MEMORY_SANITIZER and OPTION_BUILD_THREAD_SANITIZER are mutually exclusive, choose one of them") +endif() # # Platform and architecture setup @@ -13,6 +19,7 @@ string(TOUPPER ${CMAKE_SYSTEM_NAME} SYSTEM_NAME_UPPER) # Determine architecture (32/64 bit) set(X64 OFF) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(X64 ON) endif() @@ -27,21 +34,30 @@ include(Portability) # Project options # +# Test for GNU 4.9+, Clang 3.6+ or ((Visual Studio C++ or Clang with MSVC backend) and Visual Studio 2022 or superior) +if(("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" AND ${CMAKE_C_COMPILER_VERSION} VERSION_GREATER 4.9) OR + ((("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") OR + ("${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")) AND ${CMAKE_C_COMPILER_VERSION} VERSION_GREATER 3.6) OR + ( + (("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") OR + (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") AND ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC"))) AND + (MSVC_VERSION GREATER_EQUAL 1930) + ) +) + set(C_STANDARD 11) +else() + set(C_STANDARD 99) # TODO: Implement support for older standards +endif() + set(DEFAULT_PROJECT_OPTIONS DEBUG_POSTFIX "d" - CXX_STANDARD 11 # Not available before CMake 3.1; see below for manual command line argument addition + CXX_STANDARD 11 LINKER_LANGUAGE "CXX" POSITION_INDEPENDENT_CODE ON CXX_VISIBILITY_PRESET "hidden" + C_STANDARD ${C_STANDARD} ) -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - set(DEFAULT_PROJECT_OPTIONS - ${DEFAULT_PROJECT_OPTIONS} - C_STANDARD 99 # TODO: Provide preprocessor support for older standards (GCC) - ) -endif() - # # Include directories # @@ -52,12 +68,163 @@ set(DEFAULT_INCLUDE_DIRECTORIES) # Libraries # -if(OPTION_BUILD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - set(DEFAULT_LIBRARIES -lasan -lubsan) +# Valgrind +if(OPTION_TEST_MEMORYCHECK) + set(MEMORYCHECK_COMPILE_DEFINITIONS + "__MEMORYCHECK__=1" + ) + + set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full") + # set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --show-leak-kinds=all") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --trace-children=yes") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --show-reachable=yes") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --track-origins=yes") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --num-callers=100") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --smc-check=all-non-file") # for JITs + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-dl.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-python.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-node.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-wasm.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-wasm.supp") + + # TODO: Implement automatic detection for valgrind suppressions and create a proper test suite for the CI + set(MEMORYCHECK_ADDITIONAL_SUPPRESSIONS + "/usr/lib/valgrind/python3.supp" + "/usr/lib/valgrind/debian.supp" + ) + + foreach(SUPPRESSION ${MEMORYCHECK_ADDITIONAL_SUPPRESSIONS}) + if(EXISTS "${SUPPRESSION}") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${SUPPRESSION}") + endif() + endforeach() + + # This is needed in order to allow valgrind to properly track malloc in Python + set(TESTS_MEMCHECK_ENVIRONMENT_VARIABLES + "PYTHONMALLOC=malloc" + ) +else() + set(MEMORYCHECK_COMPILE_DEFINITIONS) + set(MEMORYCHECK_COMMAND_OPTIONS) + set(TESTS_MEMCHECK_ENVIRONMENT_VARIABLES) +endif() + +# ThreadSanitizer is incompatible with AddressSanitizer and LeakSanitizer +if(OPTION_BUILD_THREAD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + set(SANITIZER_LIBRARIES -ltsan) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES + "TSAN_OPTIONS=suppressions=${CMAKE_SOURCE_DIR}/source/tests/sanitizer/tsan.supp" + ) + set(SANITIZER_COMPILE_DEFINITIONS + "__THREAD_SANITIZER__=1" + ) +elseif(OPTION_BUILD_MEMORY_SANITIZER AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + # TODO: This requires much more effort than expected: https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo + set(SANITIZER_LIBRARIES) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES) + set(SANITIZER_COMPILE_DEFINITIONS + "__MEMORY_SANITIZER__=1" + ) +elseif(OPTION_BUILD_ADDRESS_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + set(SANITIZER_LIBRARIES -lasan -lubsan) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES + "LSAN_OPTIONS=verbosity=1:log_threads=1:print_suppressions=false:suppressions=${CMAKE_SOURCE_DIR}/source/tests/sanitizer/lsan.supp" + + # Specify handle_segv=0 and detect_leaks=0 for the JVM (https://blog.gypsyengineer.com/en/security/running-java-with-addresssanitizer.html) + # "ASAN_OPTIONS=handle_segv=0:symbolize=1:alloc_dealloc_mismatch=0:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:fast_unwind_on_malloc=0" + + # TODO: We should document each flag why is it used, because now we do not know what runtime has each requirement and why. + # Another option should be to separate by runtimes and only set up them on the ASAN tests that require them, + # because we do not need to disable all features on all tests, this may hide bugs in the core library for example. + + # Specify use_sigaltstack=0 as CoreCLR uses own alternate stack for signal handlers (https://github.com/swgillespie/coreclr/commit/bec020aa466d08e49e007d0011b0e79f8f7c7a62) + "ASAN_OPTIONS=use_sigaltstack=0:symbolize=1:alloc_dealloc_mismatch=0:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:fast_unwind_on_malloc=0" + ) + set(SANITIZER_COMPILE_DEFINITIONS + "__ADDRESS_SANITIZER__=1" + ) else() - set(DEFAULT_LIBRARIES) + set(SANITIZER_LIBRARIES) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES) + set(SANITIZER_COMPILE_DEFINITIONS) endif() +function(find_sanitizer NAME LINK_OPTION) + string(TOUPPER "${NAME}" NAME_UPPER) + set(SANITIZER_PROGRAM_CODE "int main() {return 0;}") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate.cpp" "${SANITIZER_PROGRAM_CODE}") + + try_compile( + STATUS + ${PROJECT_OUTPUT_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate.cpp + OUTPUT_VARIABLE SANITIZER_COMPILER_OUTPUT + LINK_OPTIONS ${LINK_OPTION} + COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate + ) + + if(NOT STATUS) + message(FATAL_ERROR "Could not find location for lib${NAME}: ${SANITIZER_COMPILER_OUTPUT}") + return() + endif() + + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES ${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate + RESOLVED_DEPENDENCIES_VAR SANITIZER_PROGRAM_LIBRARIES + ) + + foreach(DEPENDENCY IN LISTS SANITIZER_PROGRAM_LIBRARIES) + string(FIND "${DEPENDENCY}" "${NAME}" POSITION) + if(POSITION GREATER -1) + set(LIB${NAME_UPPER}_PATH "${DEPENDENCY}" PARENT_SCOPE) + return() + endif() + endforeach() +endfunction() + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + if(OPTION_BUILD_THREAD_SANITIZER) + find_sanitizer(tsan -fsanitize=thread) + set(SANITIZER_LIBRARIES_PATH + "${LIBTSAN_PATH}" + ) + elseif(OPTION_BUILD_MEMORY_SANITIZER) + set(SANITIZER_LIBRARIES_PATH) # TODO + elseif(OPTION_BUILD_ADDRESS_SANITIZER) + find_sanitizer(asan -fsanitize=address) + find_sanitizer(ubsan -fsanitize=undefined) + set(SANITIZER_LIBRARIES_PATH + "${LIBASAN_PATH}" + "${LIBUBSAN_PATH}" + ) + endif() +endif() + +if(SANITIZER_LIBRARIES_PATH) + if(PROJECT_OS_LINUX) + list(JOIN SANITIZER_LIBRARIES_PATH " " SANITIZER_LIBRARIES_PATH_JOINED) + set(TESTS_SANITIZER_PRELOAD_ENVIRONMENT_VARIABLES + "LD_PRELOAD=${SANITIZER_LIBRARIES_PATH_JOINED}" + ) + elseif(PROJECT_OS_FAMILY MATCHES "macos") + list(JOIN SANITIZER_LIBRARIES_PATH ":" SANITIZER_LIBRARIES_PATH_JOINED) + set(TESTS_SANITIZER_PRELOAD_ENVIRONMENT_VARIABLES + "DYLD_INSERT_LIBRARIES=${SANITIZER_LIBRARIES_PATH_JOINED}" + "DYLD_FORCE_FLAT_NAMESPACE=1" + ) + endif() +endif() + +if((PROJECT_OS_WIN AND MSVC) OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + # MSVC and Clang do not require to link manually the sanitizer libraries + set(SANITIZER_LIBRARIES) +endif() + +# Set default libraries +set(DEFAULT_LIBRARIES + ${SANITIZER_LIBRARIES} +) + # # Compile definitions # @@ -68,20 +235,29 @@ else() set(LOG_POLICY_FORMAT_PRETTY_VALUE 0) endif() +if(OPTION_MEMORY_TRACKER) + set(REFLECT_MEMORY_TRACKER_VALUE 1) +else() + set(REFLECT_MEMORY_TRACKER_VALUE 0) +endif() + set(DEFAULT_COMPILE_DEFINITIONS LOG_POLICY_FORMAT_PRETTY=${LOG_POLICY_FORMAT_PRETTY_VALUE} + REFLECT_MEMORY_TRACKER=${REFLECT_MEMORY_TRACKER_VALUE} SYSTEM_${SYSTEM_NAME_UPPER} + ${MEMORYCHECK_COMPILE_DEFINITIONS} + ${SANITIZER_COMPILE_DEFINITIONS} ) # MSVC compiler options -if(WIN32) +if(PROJECT_OS_WIN AND MSVC) set(DEFAULT_COMPILE_DEFINITIONS ${DEFAULT_COMPILE_DEFINITIONS} _SCL_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the Standard C++ Library _CRT_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the CRT Library ) endif() -if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR MAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(DEFAULT_COMPILE_DEFINITIONS ${DEFAULT_COMPILE_DEFINITIONS} DEBUG @@ -100,17 +276,31 @@ endif() # They are empty by default set(DEFAULT_COMPILE_OPTIONS) -if(WIN32) +if(WIN32 AND MSVC) + # Build runtime as multithreaded shared library + # if(${CMAKE_VERSION} VERSION_LESS "3.15") + # set(COMPILER_FLAGS_ID + # CMAKE_CXX_FLAGS + # CMAKE_CXX_FLAGS_DEBUG + # CMAKE_CXX_FLAGS_RELEASE + # CMAKE_C_FLAGS + # CMAKE_C_FLAGS_DEBUG + # CMAKE_C_FLAGS_RELEASE + # ) + # foreach(FLAG_ID ${COMPILER_FLAGS_ID}) + # string(REPLACE "/MD" "/MT" ${FLAG_ID} "${${FLAG_ID}}") + # endforeach() + # else() + # set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + # endif() + add_compile_options(/nologo) # Suppress Startup Banner - add_compile_options(/W4) # Set warning level to 4 - add_compile_options(/WX-) # Do not treat warnings as errors add_compile_options(/Gm-) # Disable minimal rebuild add_compile_options(/MP) # Build with Multiple Processes (number of processes equal to the number of processors) #add_compile_options(/wd4251 /wd4592) #add_compile_options(/ZH:SHA_256) # use SHA256 for generating hashes of compiler processed source files. - # Release - if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + if(CMAKE_BUILD_TYPE STREQUAL "Debug") # Disable optimizations add_compile_options(/Od) else() @@ -124,15 +314,45 @@ if(WIN32) add_compile_options(/GR-) # Enable optimizations - add_compile_options(/O2) + # add_compile_options(/O2) # TODO: Enable when runtime checks can be disabled properly add_compile_options(/Oi) add_compile_options(/Oy) + + # TODO: Disable runtime checks (not compatible with O2) + # foreach(COMPILER_FLAGS + # CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE + # CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO + # CMAKE_C_FLAGS CMAKE_C_FLAGS_RELEASE + # CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + # ) + # string(REGEX REPLACE "/RTC[^ ]*" "" ${COMPILER_FLAGS} "${${COMPILER_FLAGS}}") + # endforeach(COMPILER_FLAGS) + + if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + # Enable debug symbols + add_compile_options(/Z7) + endif() + endif() + + # Sanitizers + if(OPTION_BUILD_THREAD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(/fsanitize=thread) + add_link_options(/INCREMENTAL:NO) + elseif(OPTION_BUILD_ADDRESS_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(/fsanitize=address) + # add_compile_options(/fsanitize=undefined) + add_link_options(/INCREMENTAL:NO) + elseif(OPTION_BUILD_MEMORY_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(/fsanitize=memory) + add_compile_options(/fsanitize=leak) + add_link_options(/INCREMENTAL:NO) endif() + endif() -if (PROJECT_OS_FAMILY MATCHES "unix") +if (PROJECT_OS_FAMILY MATCHES "unix" OR PROJECT_OS_FAMILY MATCHES "macos") - if(APPLE) + if(PROJECT_OS_FAMILY MATCHES "macos") # We cannot enable "stack-protector-strong" On OS X due to a bug in clang compiler (current version 7.0.2) # Enable threads in OS X @@ -142,9 +362,8 @@ if (PROJECT_OS_FAMILY MATCHES "unix") add_compile_options(-Wreturn-stack-address) endif() - if(PROJECT_OS_LINUX) - # Enable threads in linux - add_compile_options(-pthread) + if(PROJECT_OS_HAIKU) + add_compile_options(-fPIC) endif() # All warnings that are not explicitly disabled are reported as errors @@ -152,38 +371,124 @@ if (PROJECT_OS_FAMILY MATCHES "unix") add_compile_options(-Wall) add_compile_options(-Wextra) - if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - add_compile_options(-g) - else() - add_compile_options(-O3) - endif() - # Sanitizers - if(OPTION_BUILD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - add_compile_options(-fuse-ld=gold) + if(OPTION_BUILD_THREAD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(-fno-omit-frame-pointer) + add_compile_options(-fno-optimize-sibling-calls) + add_compile_options(-fsanitize=thread) + if(PROJECT_OS_FAMILY MATCHES "macos" OR (PROJECT_OS_FAMILY MATCHES "unix" AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + add_link_options(-fsanitize=thread) + endif() + elseif(OPTION_BUILD_ADDRESS_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) add_compile_options(-fno-omit-frame-pointer) add_compile_options(-fno-optimize-sibling-calls) add_compile_options(-fsanitize=undefined) add_compile_options(-fsanitize=address) - add_compile_options(-fsanitize=leak) - #add_compile_options(-fsanitize=thread) - - if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - add_compile_options(-fsanitize=memory) - add_compile_options(-fsanitize=memory-track-origins) + add_compile_options(-fsanitize-address-use-after-scope) + add_compile_options(-fsanitize=float-divide-by-zero) + add_compile_options(-fsanitize=float-cast-overflow) + if(PROJECT_OS_FAMILY MATCHES "unix") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_compile_options(-fsanitize=pointer-compare) + add_compile_options(-fsanitize=pointer-subtract) + add_compile_options(-fuse-ld=gold) + endif() + add_compile_options(-fsanitize=leak) + endif() + if(PROJECT_OS_FAMILY MATCHES "macos" OR (PROJECT_OS_FAMILY MATCHES "unix" AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + add_link_options(-fsanitize=undefined) + add_link_options(-fsanitize=address) + add_link_options(-fsanitize-address-use-after-scope) + endif() + elseif(OPTION_BUILD_MEMORY_SANITIZER AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(-fno-omit-frame-pointer) + add_compile_options(-fno-optimize-sibling-calls) + add_compile_options(-fsanitize=undefined) + add_compile_options(-fsanitize=memory) + add_compile_options(-fsanitize-memory-track-origins) + add_compile_options(-fsanitize-memory-use-after-dtor) + if(PROJECT_OS_FAMILY MATCHES "macos") + add_link_options(-fsanitize=undefined) + add_link_options(-fsanitize=memory) + add_link_options(-fsanitize-memory-track-origins) + add_link_options(-fsanitize-memory-use-after-dtor) endif() endif() + + # Debug symbols + if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + add_compile_options(-g) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic") + endif() + + # Optimizations + if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + add_compile_options(-O3) + endif() endif() +macro(check_symbol_executable symbol binary_path result_var) + if(WIN32) + find_program(DUMPBIN_EXECUTABLE dumpbin) + if(NOT DUMPBIN_EXECUTABLE) + message(FATAL_ERROR "Trying to find symbol ${symbol} in ${binary_path} but dumpbin was not found") + endif() + execute_process( + COMMAND ${DUMPBIN_EXECUTABLE} /symbols ${binary_path} + OUTPUT_VARIABLE dumpbin_output + RESULT_VARIABLE dumpbin_result + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(FIND "${dumpbin_output}" symbol SYMBOL_FOUND) + if(NOT SYMBOL_FOUND EQUAL -1) + set(${result_var} TRUE PARENT_SCOPE) + else() + set(${result_var} FALSE PARENT_SCOPE) + endif() + else() + find_program(NM_EXECUTABLE nm) + if(NM_EXECUTABLE) + execute_process( + COMMAND ${NM_EXECUTABLE} -D ${binary_path} + OUTPUT_VARIABLE nm_output + RESULT_VARIABLE nm_result + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(FIND "${nm_output}" symbol SYMBOL_FOUND) + if(NOT SYMBOL_FOUND EQUAL -1) + set(${result_var} TRUE PARENT_SCOPE) + else() + set(${result_var} FALSE PARENT_SCOPE) + endif() + else() + message(FATAL_ERROR "Trying to find symbol ${symbol} in ${binary_path} but nm was not found") + endif() + endif() +endmacro() + +function(check_asan_executable binary_path result_var) + check_symbol_executable("__asan_init" "${binary_path}" ${result_var}) +endfunction() + +function(check_tsan_executable binary_path result_var) + check_symbol_executable("__tsan_init" "${binary_path}" ${result_var}) +endfunction() + # # Linker options # set(DEFAULT_LINKER_OPTIONS) -# Use pthreads on mingw and linux -if(("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR "${CMAKE_SYSTEM_NAME}" MATCHES "Linux") +if(PROJECT_OS_FAMILY MATCHES "macos" OR PROJECT_OS_LINUX OR PROJECT_OS_MINGW) + # Enable threads in linux, macos and mingw set(DEFAULT_LINKER_OPTIONS -pthread ) +elseif(PROJECT_OS_HAIKU) + set(DEFAULT_LINKER_OPTIONS + -lpthread + ) endif() diff --git a/cmake/Coverage.cmake b/cmake/Coverage.cmake new file mode 100644 index 0000000000..4a15b0efcb --- /dev/null +++ b/cmake/Coverage.cmake @@ -0,0 +1,48 @@ +# +# Coverage CMake support by Parra Studios +# Cross-compiler code coverage utility. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if(NOT OPTION_COVERAGE) + return() +endif() + +include(CTest) + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") +endif() + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") +endif() + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") +endif() + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage -fprofile-instr-generate -fcoverage-mapping") +endif() + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -fno-elide-constructors -fprofile-instr-generate -fcoverage-mapping") +endif() diff --git a/cmake/Distributable.cmake b/cmake/Distributable.cmake deleted file mode 100644 index e28c5fe4e7..0000000000 --- a/cmake/Distributable.cmake +++ /dev/null @@ -1,156 +0,0 @@ -# -# CMake Distributable (Unity Build) library by Parra Studios -# CMake script to generate distributable (unity build) libraries. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -function(distributable_generate target_root unity_build_file) - - set(target_list "${ARGN}") - - set(unity_build_source) - - foreach(target ${target_list}) - - # Get target source files - get_target_property(target_sources - ${META_PROJECT_NAME}::${target} - SOURCES - ) - - # Add private linkage for all targets except metacall - if(NOT "${target}" STREQUAL "${target_root}") - string(TOUPPER ${target} target_upper) - set(unity_build_source "${unity_build_source}\n\#ifndef ${target_upper}_API") - - if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") - set(unity_build_source "${unity_build_source}\n\# define ${target_upper}_API __attribute__ ((visibility(\"hidden\")))") - else() - set(unity_build_source "${unity_build_source}\n\# define ${target_upper}_API ${target_upper}_NO_EXPORT") - add_definitions("-D${target_upper}_API=${target_upper}_NO_EXPORT") - endif() - - set(unity_build_source "${unity_build_source}\n\#endif /* ${target_upper}_API */") - else() - set(unity_build_source "${unity_build_source}\n\#ifndef ${target}_EXPORTS") - set(unity_build_source "${unity_build_source}\n\# define ${target}_EXPORTS") - set(unity_build_source "${unity_build_source}\n\#endif /* ${target}_EXPORTS */") - - add_definitions("-D${target}_EXPORTS") - endif() - - # Add include paths - include_directories("${CMAKE_BINARY_DIR}/source/${target}/include") - include_directories("${CMAKE_SOURCE_DIR}/source/${target}/include") - - # Write auto-generated includes into unity build - set(unity_build_source "${unity_build_source}\n\#include <${CMAKE_BINARY_DIR}/source/${target}/include/${target}/${target}_api.h>") - set(unity_build_source "${unity_build_source}\n\#include <${CMAKE_BINARY_DIR}/source/${target}/include/${target}/${target}_features.h>") - - # Write all includes into unity build - foreach(source ${target_sources}) - set(unity_build_source "${unity_build_source}\n\#include <${source}>") - endforeach() - - endforeach() - - set(unity_build_source "${unity_build_source}\n") - - if(EXISTS "${unity_build_file}") - file(READ "${unity_build_file}" unity_build_source_old) - - if("${unity_build_source_old}" STREQUAL "${unity_build_source}") - return() - endif() - endif() - - file(WRITE ${unity_build_file} "${unity_build_source}") - - message(STATUS "Unity build written in ${unity_build_file}") - -endfunction() - -function(distributable_export_generate unity_build_file) - - set(target_list "${ARGN}") - - set(unity_build_source) - - foreach(target ${target_list}) - - # Get target source files - get_target_property(target_sources - ${META_PROJECT_NAME}::${target} - SOURCES - ) - - # Add public linkage for all targets - set(unity_build_source "${unity_build_source}\n\#ifndef ${target}_EXPORTS") - set(unity_build_source "${unity_build_source}\n\# define ${target}_EXPORTS") - set(unity_build_source "${unity_build_source}\n\#endif /* ${target}_EXPORTS */") - - add_definitions("-D${target}_EXPORTS") - - # Add include paths - include_directories("${CMAKE_BINARY_DIR}/source/${target}/include") - include_directories("${CMAKE_SOURCE_DIR}/source/${target}/include") - - # Write auto-generated includes into unity build - set(unity_build_source "${unity_build_source}\n\#include <${CMAKE_BINARY_DIR}/source/${target}/include/${target}/${target}_api.h>") - set(unity_build_source "${unity_build_source}\n\#include <${CMAKE_BINARY_DIR}/source/${target}/include/${target}/${target}_features.h>") - - # Write all includes into unity build - foreach(source ${target_sources}) - set(unity_build_source "${unity_build_source}\n\#include <${source}>") - endforeach() - - endforeach() - - set(unity_build_source "${unity_build_source}\n") - - if(EXISTS "${unity_build_file}") - file(READ "${unity_build_file}" unity_build_source_old) - - if("${unity_build_source_old}" STREQUAL "${unity_build_source}") - return() - endif() - endif() - - file(WRITE ${unity_build_file} "${unity_build_source}") - - message(STATUS "Unity build written in ${unity_build_file}") - -endfunction() - -function(distributable_include) - - set(target_list "${ARGN}") - - foreach(target ${target_list}) - - # Get target source files - get_target_property(target_sources - ${META_PROJECT_NAME}::${target} - SOURCES - ) - - # Add include paths - include_directories("${CMAKE_BINARY_DIR}/source/${target}/include") - include_directories("${CMAKE_SOURCE_DIR}/source/${target}/include") - - endforeach() - -endfunction() diff --git a/cmake/FindClang.cmake b/cmake/FindClang.cmake deleted file mode 100644 index 33d55b626f..0000000000 --- a/cmake/FindClang.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# Find Clang -# -# It defines the following variables -# CLANG_FOUND - True if Clang found. -# CLANG_INCLUDE_DIRS - where to find Clang include files -# CLANG_LIBS - list of clang libs - -if(NOT LLVM_INCLUDE_DIRS OR NOT LLVM_LIBRARY_DIRS) - message(FATAL_ERROR "No LLVM and Clang support requires LLVM") - return() -endif(NOT LLVM_INCLUDE_DIRS OR NOT LLVM_LIBRARY_DIRS) - -macro(FIND_AND_ADD_CLANG_LIB _libname_) - find_library(CLANG_${_libname_}_LIB ${_libname_} ${LLVM_LIBRARY_DIRS} ${CLANG_LIBRARY_DIRS}) - - if(CLANG_${_libname_}_LIB) - set(CLANG_LIBS ${CLANG_LIBS} ${CLANG_${_libname_}_LIB}) - endif (CLANG_${_libname_}_LIB) -macro(FIND_AND_ADD_CLANG_LIB) - -# Clang shared library provides just the limited C interface, so it -# can not be used. We look for the static libraries. -FIND_AND_ADD_CLANG_LIB(clang) -FIND_AND_ADD_CLANG_LIB(clangBasic) -FIND_AND_ADD_CLANG_LIB(clangFrontend) -FIND_AND_ADD_CLANG_LIB(clangFrontendTool) -FIND_AND_ADD_CLANG_LIB(clangToolingCore) -FIND_AND_ADD_CLANG_LIB(clangTooling) -FIND_AND_ADD_CLANG_LIB(clangDriver) -FIND_AND_ADD_CLANG_LIB(clangCodeGen) -FIND_AND_ADD_CLANG_LIB(clangEdit) -FIND_AND_ADD_CLANG_LIB(clangSema) -FIND_AND_ADD_CLANG_LIB(clangChecker) -FIND_AND_ADD_CLANG_LIB(clangAnalysis) -FIND_AND_ADD_CLANG_LIB(clangAST) -FIND_AND_ADD_CLANG_LIB(clangParse) -FIND_AND_ADD_CLANG_LIB(clangLex) -FIND_AND_ADD_CLANG_LIB(clangSerialization) -FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend) -FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers) -FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore) -FIND_AND_ADD_CLANG_LIB(clangAnalysis) -FIND_AND_ADD_CLANG_LIB(clangARCMigrate) -FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend) -FIND_AND_ADD_CLANG_LIB(clangRewriteCore) -FIND_AND_ADD_CLANG_LIB(clangQuery) -FIND_AND_ADD_CLANG_LIB(clangIndex) -FIND_AND_ADD_CLANG_LIB(clangRename) -FIND_AND_ADD_CLANG_LIB(clangTidy) - -FIND_AND_ADD_CLANG_LIB(LLVMCore) -FIND_AND_ADD_CLANG_LIB(LLVMSupport) - -find_path(CLANG_INCLUDE_DIRS clang/Basic/Version.h HINTS ${LLVM_INCLUDE_DIRS}) - -if(CLANG_LIBS AND CLANG_INCLUDE_DIRS) - message(STATUS "Clang libs: " ${CLANG_LIBS}) - set(CLANG_FOUND TRUE) -endif(CLANG_LIBS AND CLANG_INCLUDE_DIRS) - -if(CLANG_FOUND) - message(STATUS "Found Clang: ${CLANG_INCLUDE_DIRS}") -else(CLANG_FOUND) - if (CLANG_FIND_REQUIRED) - message(FATAL_ERROR "Could NOT find Clang") - endif (CLANG_FIND_REQUIRED) -endif(CLANG_FOUND) diff --git a/cmake/FindClangFormat.cmake b/cmake/FindClangFormat.cmake new file mode 100644 index 0000000000..3e7e79d433 --- /dev/null +++ b/cmake/FindClangFormat.cmake @@ -0,0 +1,98 @@ +# +# Taken from https://raw.githubusercontent.com/BlueBrain/git-cmake-format/master/FindClangFormat.cmake +# --------------- +# +# The module defines the following variables +# +# ``ClangFormat_EXECUTABLE`` Path to clang-format executable +# ``ClangFormat_FOUND`` True if the clang-format executable was found. +# ``ClangFormat_VERSION`` The version of clang-format found +# ``ClangFormat_VERSION_MAJOR`` The clang-format major version if specified, 0 +# otherwise ``ClangFormat_VERSION_MINOR`` The clang-format minor version if +# specified, 0 otherwise ``ClangFormat_VERSION_PATCH`` The clang-format patch +# version if specified, 0 otherwise ``ClangFormat_VERSION_COUNT`` Number of +# version components reported by clang-format +# +# Example usage: +# +# .. code-block:: cmake +# +# find_package(ClangFormat) if(ClangFormat_FOUND) message("clang-format +# executable found: ${ClangFormat_EXECUTABLE}\n" "version: +# ${ClangFormat_VERSION}") endif() + +if(ClangFormat_FOUND) + set(ClangFormat_FIND_QUIETLY TRUE) +endif() + +set(ClangFormat_NAMES + clang-format + clang-format-11 + clang-format-12 +) + +set(ClangFormat_PATHS + /usr/bin + /usr/lib/llvm-11/bin + /usr/lib/llvm-12/bin +) + +find_program(ClangFormat_EXECUTABLE + NAMES ${ClangFormat_NAMES} + DOC "clang-format executable" + PATHS ${ClangFormat_PATHS} +) + +# Extract version from command "clang-format -version" +if(ClangFormat_EXECUTABLE) + execute_process(COMMAND ${ClangFormat_EXECUTABLE} -version + OUTPUT_VARIABLE clang_format_version + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(clang_format_version MATCHES "clang-format version .*") + # clang_format_version sample: "clang-format version 3.9.1-4ubuntu3~16.04.1 + # (tags/RELEASE_391/rc2)" + string(REGEX + REPLACE ".*clang-format version ([.0-9]+).*" + "\\1" + ClangFormat_VERSION + "${clang_format_version}") + # ClangFormat_VERSION sample: "3.9.1" + + # Extract version components + string(REPLACE "." ";" clang_format_version "${ClangFormat_VERSION}") + list(LENGTH clang_format_version ClangFormat_VERSION_COUNT) + if(ClangFormat_VERSION_COUNT GREATER 0) + list(GET clang_format_version 0 ClangFormat_VERSION_MAJOR) + else() + set(ClangFormat_VERSION_MAJOR 0) + endif() + if(ClangFormat_VERSION_COUNT GREATER 1) + list(GET clang_format_version 1 ClangFormat_VERSION_MINOR) + else() + set(ClangFormat_VERSION_MINOR 0) + endif() + if(ClangFormat_VERSION_COUNT GREATER 2) + list(GET clang_format_version 2 ClangFormat_VERSION_PATCH) + else() + set(ClangFormat_VERSION_PATCH 0) + endif() + endif() + unset(clang_format_version) +endif() + +if(ClangFormat_EXECUTABLE AND ClangFormat_VERSION) + set(ClangFormat_FOUND TRUE) + + include(FindPackageHandleStandardArgs) + + # Set standard args + find_package_handle_standard_args(ClangFormat + REQUIRED_VARS ClangFormat_EXECUTABLE + VERSION_VAR ClangFormat_VERSION + ) + + mark_as_advanced(ClangFormat_EXECUTABLE) +else() + set(ClangFormat_FOUND FALSE) +endif() diff --git a/cmake/FindCobol.cmake b/cmake/FindCobol.cmake index 3f14ec669b..c85a5cb466 100644 --- a/cmake/FindCobol.cmake +++ b/cmake/FindCobol.cmake @@ -2,7 +2,7 @@ # CMake Find GNU/Cobol by Parra Studios # CMake script to find GNU/Cobol compiler and runtime. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindCoreCLR.cmake b/cmake/FindCoreCLR.cmake index bec858ef8e..884d3c1132 100644 --- a/cmake/FindCoreCLR.cmake +++ b/cmake/FindCoreCLR.cmake @@ -2,7 +2,7 @@ # CMake Find CoreCLR NET Engine by Parra Studios # CMake script to find CoreCLR NET Engine. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,8 +24,6 @@ # CORECLR_LIBRARIES - List of CoreCLR libraries # CORECLR_CGINFO - List of CoreCLR libraries - - # Prevent vervosity if already included if(CORECLR_FOUND) set(CORECLR_FIND_QUIETLY TRUE) diff --git a/cmake/FindDotNET.cmake b/cmake/FindDotNET.cmake index e071ca6f68..57c2d4dc95 100644 --- a/cmake/FindDotNET.cmake +++ b/cmake/FindDotNET.cmake @@ -2,7 +2,7 @@ # CMake Find Dot NET Engine by Parra Studios # CMake script to find DotNET Engine. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindGBench.cmake b/cmake/FindGBench.cmake index bf5612fa08..74eacb0b8a 100644 --- a/cmake/FindGBench.cmake +++ b/cmake/FindGBench.cmake @@ -2,7 +2,7 @@ # CMake Find Google Benchmark by Parra Studios # CMake script to find Google Benchmark library. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ find_library(GBENCH_LIBRARY NAMES benchmark) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(google-benchmark +find_package_handle_standard_args(GBench REQUIRED_VARS GBENCH_INCLUDE_DIR GBENCH_LIBRARY ) diff --git a/cmake/FindJulia.cmake b/cmake/FindJulia.cmake new file mode 100644 index 0000000000..bf3c30d3b8 --- /dev/null +++ b/cmake/FindJulia.cmake @@ -0,0 +1,201 @@ +# +# CMake Find Julia Runtime by Parra Studios +# CMake script to find Julia runtime. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Julia runtime library and include paths +# +# JULIA_FOUND - True if Julia runtime was found +# JULIA_EXECUTABLE - Path to the Julia executable +# JULIA_INCLUDE_DIRS - Julia headers path +# JULIA_LIBRARY - List of Julia libraries +# JULIA_LIBRARY_DIR - Folder containing Julia libraries +# JULIA_VERSION - Julia version string +# JULIA_VERSION_MAJOR - Julia major version +# JULIA_VERSION_MINOR - Julia minor version +# JULIA_VERSION_PATCH - Julia patch version +# JULIA_HOME - Folder containing Julia libraries +# JULIA_LLVM_VERSION - Version of LLVM that Julia is using +# JULIA_WORD_SIZE - Whether Julia supports 32 or 64 bits +# +# Options +# +# JULIA_DIR - Defines an alternative search path where Julia runtime is located +# + +# Prevent vervosity if already included +if(JULIA_FOUND) + set(JULIA_FIND_QUIETLY TRUE) +endif() + +# Julia search path +option(JULIA_DIR "Defines an alternative search path where Julia runtime is located." OFF) + +if(JULIA_DIR) + list(APPEND CMAKE_PREFIX_PATH ${JULIA_DIR}) +endif() + +# Julia Executable +find_program(JULIA_EXECUTABLE julia DOC "Julia executable") + +# Julia Version +set(JULIA_VERSION_RESULT 1) + +if(JULIA_EXECUTABLE) + execute_process( + COMMAND "${JULIA_EXECUTABLE}" --startup-file=no --version + OUTPUT_VARIABLE JULIA_VERSION_STRING + RESULT_VARIABLE JULIA_VERSION_RESULT + ) +endif() + +if(JULIA_VERSION_RESULT NOT EQUAL 0) + find_file(JULIA_VERSION_INCLUDE + julia_version.h + PATH_SUFFIXES include/julia + ) + file(READ ${JULIA_VERSION_INCLUDE} JULIA_VERSION_STRING) + string(REGEX MATCH "JULIA_VERSION_STRING.*" JULIA_VERSION_STRING ${JULIA_VERSION_STRING}) +endif() + +string( + REGEX REPLACE ".*([0-9]+\\.[0-9]+\\.[0-9]+).*" "\\1" + JULIA_VERSION_STRING "${JULIA_VERSION_STRING}" +) + +string(REPLACE "." ";" JULIA_VERSION_LIST ${JULIA_VERSION_STRING}) +list(GET JULIA_VERSION_LIST 0 JULIA_VERSION_MAJOR) +list(GET JULIA_VERSION_LIST 1 JULIA_VERSION_MINOR) +list(GET JULIA_VERSION_LIST 2 JULIA_VERSION_PATCH) + +set(JULIA_VERSION ${JULIA_VERSION_STRING} CACHE STRING "Julia version string") + +if(${JULIA_VERSION} VERSION_LESS "0.7.0") + set(JULIA_HOME_NAME "JULIA_HOME") +else() + set(JULIA_HOME_NAME "Sys.BINDIR") + set(JULIA_USING_LIBDL "using Libdl") +endif() + +# Julia Headers +if(JULIA_DIR) + set(JULIA_INCLUDE_DIRS ${JULIA_DIR}/include/julia CACHE STRING "Location of Julia include files") +elseif(JULIA_EXECUTABLE) + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "julia_include_dir = joinpath(match(r\"(.*)(bin)\",${JULIA_HOME_NAME}).captures[1],\"include\",\"julia\")\n + if !isdir(julia_include_dir) # then we're running directly from build\n + julia_base_dir_aux = splitdir(splitdir(${JULIA_HOME_NAME})[1])[1] # useful for running-from-build\n + julia_include_dir = joinpath(julia_base_dir_aux, \"usr\", \"include\" )\n + julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\", \"support\" )\n + julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\" )\n + end\n + julia_include_dir" + OUTPUT_VARIABLE JULIA_INCLUDE_DIRS + ) + + string(REGEX REPLACE "\"" "" JULIA_INCLUDE_DIRS "${JULIA_INCLUDE_DIRS}") + string(REGEX REPLACE "\n" "" JULIA_INCLUDE_DIRS "${JULIA_INCLUDE_DIRS}") + + set(JULIA_INCLUDE_DIRS ${JULIA_INCLUDE_DIRS} CACHE STRING "Location of Julia include files") +else() + find_path(JULIA_INCLUDE_DIRS + NAMES julia.h + PATHS "/usr/include/julia" + DOC "Location of Julia include files" + ) + get_filename_component(JULIA_INCLUDE_DIRS ${JULIA_INCLUDE_DIRS} DIRECTORY) +endif() + +# Julia Libraries +if(JULIA_EXECUTABLE) + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "${JULIA_USING_LIBDL}\nabspath(Libdl.dlpath((ccall(:jl_is_debugbuild, Cint, ()) != 0) ? \"libjulia-debug\" : \"libjulia\"))" + OUTPUT_VARIABLE JULIA_LIBRARY + ) + + string(REGEX REPLACE "\"" "" JULIA_LIBRARY "${JULIA_LIBRARY}") + string(REGEX REPLACE "\n" "" JULIA_LIBRARY "${JULIA_LIBRARY}") + string(STRIP "${JULIA_LIBRARY}" JULIA_LIBRARY) + + if(WIN32) + get_filename_component(JULIA_LIBRARY_DIR ${JULIA_LIBRARY} DIRECTORY) + get_filename_component(JULIA_LIBRARY_DIR ${JULIA_LIBRARY_DIR} DIRECTORY) + find_library(WIN32_JULIA_LIBRARY + NAMES libjulia.dll.a + PATHS "${JULIA_LIBRARY_DIR}/lib" + NO_DEFAULT_PATH + ) + set(JULIA_LIBRARY "${WIN32_JULIA_LIBRARY}") + endif() + + set(JULIA_LIBRARY "${JULIA_LIBRARY}" CACHE PATH "Julia library") +else() + set(JULIA_LIBRARY_NAMES + libjulia.so.${JULIA_VERSION_MAJOR}.${JULIA_VERSION_MINOR}.${JULIA_VERSION_PATCH} + libjulia.so.${JULIA_VERSION_MAJOR}.${JULIA_VERSION_MINOR} + libjulia.so.${JULIA_VERSION_MAJOR} + libjulia.so + libjulia.${JULIA_VERSION}.dylib + julia + libjulia + libjulia.dll.a + ) + find_library(JULIA_LIBRARY + NAMES ${JULIA_LIBRARY_NAMES} + DOC "Location of Julia library" + CMAKE_FIND_ROOT_PATH_BOTH + ) +endif() + +get_filename_component(JULIA_LIBRARY_DIR ${JULIA_LIBRARY} DIRECTORY) + +# Additional Julia information +if(JULIA_EXECUTABLE) + # Julia Home + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "${JULIA_HOME_NAME}" + OUTPUT_VARIABLE JULIA_HOME + ) + + string(REGEX REPLACE "\"" "" JULIA_HOME "${JULIA_HOME}") + string(REGEX REPLACE "\n" "" JULIA_HOME "${JULIA_HOME}") + + + # LLVM Version + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "Base.libllvm_version" + OUTPUT_VARIABLE JULIA_LLVM_VERSION + ) + + string(REGEX REPLACE "\"" "" JULIA_LLVM_VERSION "${JULIA_LLVM_VERSION}") + string(REGEX REPLACE "\n" "" JULIA_LLVM_VERSION "${JULIA_LLVM_VERSION}") + + # Word Size + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "Sys.WORD_SIZE" + OUTPUT_VARIABLE JULIA_WORD_SIZE + ) + string(REGEX REPLACE "\n" "" JULIA_WORD_SIZE "${JULIA_WORD_SIZE}") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(JULIA + REQUIRED_VARS JULIA_LIBRARY JULIA_LIBRARY_DIR JULIA_INCLUDE_DIRS JULIA_EXECUTABLE JULIA_VERSION + VERSION_VAR JULIA_VERSION + FAIL_MESSAGE "Julia not found" +) diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake deleted file mode 100644 index 43c2f5714f..0000000000 --- a/cmake/FindLLVM.cmake +++ /dev/null @@ -1,104 +0,0 @@ -# Find LLVM -# This module can be used to find LLVM. -# It requires that the llvm-config executable be available on the system path. -# Once found, llvm-config is used for everything else. -# -# Typical usage could be: -# find_package(LLVM QUIET REQUIRED COMPONENTS jit native interpreter) -# -# If the QUIET flag is not set, the specified components and LLVM version are -# outputted. -# -# If the COMPONENTS are not set, the default set of "all" is used. -# -# The following variables are set: -# -# LLVM_FOUND - Set to YES if LLVM is found. -# LLVM_VERSION - Set to the decimal version of the LLVM library. -# LLVM_C_FLAGS - All flags that should be passed to a C compiler. -# LLVM_CXX_FLAGS - All flags that should be passed to a C++ compiler. -# LLVM_CPP_FLAGS - All flags that should be passed to the C pre-processor. -# LLVM_LD_FLAGS - Additional flags to pass to the linker. -# LLVM_LIBRARY_DIRS - A list of directories where the LLVM libraries are located. -# LLVM_INCLUDE_DIRS - A list of directories where the LLVM headers are located. -# LLVM_LIBRARIES - A list of libraries which should be linked against. - -# A macro to run llvm config -macro(_llvm_config _var_name) - # Firstly, locate the LLVM config executable - find_program(_llvm_config_exe - NAMES llvm-config - DOC "llvm-config executable location" - ) - - # If no llvm-config executable was found, set the output variable to not - # found. - if(NOT _llvm_config_exe) - set(${_var_name} "${_var_name}-NOTFOUND") - else(NOT _llvm_config_exe) - # Otherwise, run llvm-config - execute_process( - COMMAND ${_llvm_config_exe} ${ARGN} - OUTPUT_VARIABLE ${_var_name} - RESULT_VARIABLE _llvm_config_retval - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(RESULT_VARIABLE) - message(SEND_ERROR - "Error running llvm-config with arguments: ${ARGN}") - endif(RESULT_VARIABLE) - endif(NOT _llvm_config_exe) -endmacro(_llvm_config) - -include(FindPackageHandleStandardArgs) - -# The default set of components -set(_llvm_components all) - -# If components have been specified via find_package, use them -if(LLVM_FIND_COMPONENTS) - set(_llvm_components ${LLVM_FIND_COMPONENTS}) -endif(LLVM_FIND_COMPONENTS) - -if(NOT LLVM_FIND_QUIETLY) - message(STATUS "Looking for LLVM components: ${_llvm_components}") -endif(NOT LLVM_FIND_QUIETLY) - -_llvm_config(LLVM_VERSION --version) -_llvm_config(LLVM_C_FLAGS --cflags ${_llvm_components}) -_llvm_config(LLVM_CXX_FLAGS --cxxflags ${_llvm_components}) -_llvm_config(LLVM_CPP_FLAGS --cppflags ${_llvm_components}) -_llvm_config(LLVM_LD_FLAGS --ldflags ${_llvm_components}) -_llvm_config(LLVM_LIBRARY_DIRS --libdir ${_llvm_components}) -_llvm_config(LLVM_INCLUDE_DIRS --includedir ${_llvm_components}) -_llvm_config(LLVM_LIBRARIES --libs ${_llvm_components}) - -# Filter preprocessor flags -string(REGEX REPLACE "( )+" ";" LLVM_CPP_FLAGS_LIST ${LLVM_CPP_FLAGS}) - -set(LLVM_CPP_FLAGS_FILTER "") - -foreach(FLAG ${LLVM_CPP_FLAGS_LIST}) - string(REGEX MATCH "^(-I)((/[^/]+)+)" FLAG_MATCH ${FLAG}) - - if("${FLAG_MATCH}" STREQUAL "") - string(REGEX REPLACE "^(-D)" "" FLAG_FILTER ${FLAG}) - - set(FLAG_FILTER "${FLAG_FILTER}=1") - - if(NOT ("${FLAG_FILTER}" STREQUAL "")) - set(LLVM_CPP_FLAGS_FILTER "${LLVM_CPP_FLAGS_FILTER} ${FLAG_FILTER}") - endif() - endif() -endforeach() - -set(LLVM_CPP_FLAGS ${LLVM_CPP_FLAGS_FILTER}) - -if(NOT LLVM_FIND_QUIETLY) - message(STATUS "Found LLVM version: ${LLVM_VERSION}") - message(STATUS "LLVM preprocessor flags: ${LLVM_CPP_FLAGS}") -endif(NOT LLVM_FIND_QUIETLY) - -# Handle the QUIETLY and REQUIRED arguments and set LLVM_FOUND to TRUE if -# all listed variables are TRUE -find_package_handle_standard_args(LLVM DEFAULT_MSG LLVM_LIBRARIES LLVM_INCLUDE_DIRS LLVM_LIBRARY_DIRS) diff --git a/cmake/FindLibClang.cmake b/cmake/FindLibClang.cmake new file mode 100644 index 0000000000..bbf4d2c25f --- /dev/null +++ b/cmake/FindLibClang.cmake @@ -0,0 +1,91 @@ +# +# CMake Find Clang library by Parra Studios +# CMake script to find Clang C API library. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Clang C API library and include paths +# +# LibClang_FOUND - True if Clang library was found +# LibClang_INCLUDE_DIR - Clang headers path +# LibClang_LIBRARY - Clang C API library + +# Prevent vervosity if already included +if(LibClang_FOUND) + set(LibClang_FIND_QUITELY TRUE) +endif() + +option(LibClang_CMAKE_DEBUG "Print paths for debugging LibClang dependencies." OFF) + +# Define what version to search for +if(LibClang_FIND_VERSION) + set(LibClang_VERSION_LIST ${LibClang_FIND_VERSION}) +else() + set(LibClang_VERSION_LIST 17 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8) +endif() + +macro(_libclang_generate_search_paths template result) + set(${result}) + foreach(version ${LibClang_VERSION_LIST}) + string(REPLACE "VERSION" "${version}" output_path "${template}") + list(APPEND ${result} ${output_path}) + endforeach() +endmacro() + +include(FindPackageHandleStandardArgs) + +# Find Clang C API Library +_libclang_generate_search_paths("/usr/lib/llvm-VERSION/lib/" LibClang_LIBRARY_PATHS) + +if(NOT LibClang_LIBRARY) + find_library(LibClang_LIBRARY + NAMES clang + PATHS ${LibClang_LIBRARY_PATHS} /usr/lib # Use this path as a fallback + ) +endif() + +# Find Clang C API Headers +_libclang_generate_search_paths("/usr/lib/llvm-VERSION/include/clang-c" LibClang_INCLUDE_PATHS) + +set(LibClang_INCLUDE_HEADERS + BuildSystem.h + CXCompilationDatabase.h + CXErrorCode.h + CXString.h + Documentation.h + Index.h + Platform.h +) + +if(NOT LibClang_INCLUDE_DIR) + find_path(LibClang_INCLUDE_DIR + NAMES ${LibClang_INCLUDE_HEADERS} + PATHS ${LibClang_INCLUDE_PATHS} /usr/include/clang-c # Use this path as a fallback + ) +endif() + +get_filename_component(LibClang_INCLUDE_DIR ${LibClang_INCLUDE_DIR} DIRECTORY) + +# Define LibClang cmake module +find_package_handle_standard_args(LibClang DEFAULT_MSG LibClang_LIBRARY LibClang_INCLUDE_DIR) + +# Mark cmake module as advanced +mark_as_advanced(LibClang_LIBRARY LibClang_INCLUDE_DIR) + +if(LibClang_CMAKE_DEBUG) + message(STATUS "LibClang_INCLUDE_DIRS: ${LibClang_INCLUDE_DIR}") + message(STATUS "LibClang_LIBRARY: ${LibClang_LIBRARY}") +endif() diff --git a/cmake/FindLibFFI.cmake b/cmake/FindLibFFI.cmake index d84f1f1c85..313925b13e 100644 --- a/cmake/FindLibFFI.cmake +++ b/cmake/FindLibFFI.cmake @@ -2,7 +2,7 @@ # CMake Find Foreing Function Interface library by Parra Studios # CMake script to find FFI library. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,9 +20,8 @@ # Find FFI library and include paths # # LIBFFI_FOUND - True if FFI library was found -# LIBFFI_INCLUDE_DIRS - FFI headers path -# LIBFFI_LIBRARIES - List of FFI libraries -# LIBFFI_DEFINITIONS - FFI definitions +# LIBFFI_INCLUDE_DIR - FFI headers path +# LIBFFI_LIBRARY - List of FFI libraries # Prevent vervosity if already included if(LIBFFI_INCLUDE_DIRS) @@ -31,22 +30,54 @@ endif() include(FindPackageHandleStandardArgs) -# Find package configuration module -find_package(PkgConfig) +set(LIBFFI_SUFFIXES + ffi + x86_64-linux-gnu + aarch64-linux-gnu + arm-linux-gnueabi + arm-linux-gnueabihf + i386-linux-gnu + mips64el-linux-gnuabi64 + mipsel-linux-gnu + powerpc64le-linux-gnu + s390x-linux-gnu +) -# Find module -pkg_check_modules(PC_LIBFFI QUIET libffi) +find_library(LIBFFI_LIBRARY + NAMES ffi libffi + PATHS /usr /usr/lib /usr/local /opt/local + PATH_SUFFIXES lib lib64 ${LIBFFI_SUFFIXES} +) -# Find include path -find_path(LIBFFI_INCLUDE_DIR ffi.h HINTS ${PC_LIBFFI_INCLUDEDIR} ${PC_LIBFFI_INCLUDE_DIRS}) +if(APPLE) + execute_process(COMMAND brew --prefix libffi OUTPUT_VARIABLE LIBFFI_PREFIXES) +else() + # TODO: Windows? + set(LIBFFI_PREFIXES) +endif() + +if(NOT LIBFFI_INCLUDE_DIR) + find_path(LIBFFI_INCLUDE_DIR ffi.h + PATHS /usr /usr/include /usr/local /opt/local /usr/include/ffi + PATH_SUFFIXES ${LIBFFI_SUFFIXES} + HINT LIBFFI_PREFIXES + ) +endif() -# Find library -find_library(LIBFFI_LIBRARY NAMES ffi HINTS ${PC_LIBFFI_LIBDIR} ${PC_LIBFFI_LIBRARY_DIRS}) +# Try to load by using PkgConfig +if(NOT LIBFFI_LIBRARY OR NOT LIBFFI_INCLUDE_DIR) + # Find package configuration module + find_package(PkgConfig) -# Define moudle variables -set(LIBFFI_DEFINITIONS ${PC_LIBFFI_CFLAGS_OTHER}) -set(LIBFFI_LIBRARIES ${LIBFFI_LIBRARY}) -set(LIBFFI_INCLUDE_DIRS ${LIBFFI_INCLUDE_DIR}) + # Find module + pkg_check_modules(PC_LIBFFI QUIET libffi) + + # Find include path + find_path(LIBFFI_INCLUDE_DIR ffi.h HINTS ${PC_LIBFFI_INCLUDEDIR} ${PC_LIBFFI_INCLUDE_DIRS}) + + # Find library + find_library(LIBFFI_LIBRARY NAMES ffi HINTS ${PC_LIBFFI_LIBDIR} ${PC_LIBFFI_LIBRARY_DIRS}) +endif() # Define FFI cmake module find_package_handle_standard_args(LibFFI DEFAULT_MSG LIBFFI_LIBRARY LIBFFI_INCLUDE_DIR) diff --git a/cmake/FindLibGit2.cmake b/cmake/FindLibGit2.cmake new file mode 100644 index 0000000000..cb1c0cf0a1 --- /dev/null +++ b/cmake/FindLibGit2.cmake @@ -0,0 +1,82 @@ +# +# CMake Find LibGit2 Library by Parra Studios +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# + +# Find libgit2 library and include paths +# +# LibGit2_FOUND - True if LibGit2 was found +# LibGit2_INCLUDE_DIR - LibGit2 headers path +# LibGit2_VERSION - LibGit2 version +# LibGit2_VERSION_MAJOR - LibGit2 major version +# LibGit2_VERSION_MINOR - LibGit2 minor version +# LibGit2_VERSION_REVISION - LibGit2 patch version +# LibGit2_LIBRARY - LibGit2 shared library +# LibGit2_LIBRARY_DIR - LibGit2 shared library folder +# + +# Prevent vervosity if already included +if(LibGit2_LIBRARY) + set(LibGit2_FIND_QUIETLY TRUE) +endif() + +# Include package manager +include(FindPackageHandleStandardArgs) + +# Find via PkgConfig +find_package(PkgConfig QUIET) +pkg_check_modules(PKG_GIT2 QUIET libgit2) + +if(NOT PKG_GIT2_FOUND) + return() +endif() + +if(NOT LibGit2_DEFINITIONS) + set(LibGit2_DEFINITIONS ${PKG_GIT2_CFLAGS_OTHER}) +endif() + +if(NOT LibGit2_INCLUDE_DIR) + find_path(LibGit2_INCLUDE_DIR + NAMES git2.h + HINTS ${PKG_GIT2_INCLUDE_DIRS} + ) +endif() + +if(NOT LibGit2_VERSION AND LibGit2_INCLUDE_DIR) + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_MAJOR REGEX "^#define LIBGIT2_VER_MAJOR +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_MAJOR "${LibGit2_VERSION_MAJOR}") + + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_MINOR REGEX "^#define LIBGIT2_VER_MINOR +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_MINOR "${LibGit2_VERSION_MINOR}") + + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_REVISION REGEX "^#define LIBGIT2_VER_REVISION +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_REVISION "${LibGit2_VERSION_REVISION}") + + set(LibGit2_VERSION "${LibGit2_VERSION_MAJOR}.${LibGit2_VERSION_MINOR}.${LibGit2_VERSION_REVISION}") +endif() + +if(NOT LibGit2_LIBRARY) + find_library(LibGit2_LIBRARY + NAMES git2 + HINTS ${PKG_GIT2_LIBRARY_DIRS} + ) +endif() + +set(LibGit2_LIBRARIES ${LibGit2_LIBRARY}) +set(LibGit2_INCLUDE_DIRS ${LibGit2_INCLUDE_DIR}) +get_filename_component(LibGit2_LIBRARY_DIR ${LibGit2_LIBRARY} DIRECTORY) + +# Define package +find_package_handle_standard_args(LibGit2 + FOUND_VAR + LibGit2_FOUND + REQUIRED_VARS + LibGit2_LIBRARY + LibGit2_LIBRARY_DIR + LibGit2_INCLUDE_DIR + LibGit2_INCLUDE_DIRS + VERSION_VAR + LibGit2_VERSION +) + +mark_as_advanced(LibGit2_LIBRARY LibGit2_LIBRARY_DIR LibGit2_INCLUDE_DIR) diff --git a/cmake/FindLibTCC.cmake b/cmake/FindLibTCC.cmake new file mode 100644 index 0000000000..83c2e4ac59 --- /dev/null +++ b/cmake/FindLibTCC.cmake @@ -0,0 +1,65 @@ +# +# CMake Find Tiny C Compiler library by Parra Studios +# CMake script to find TCC library. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find TCC library and include paths +# +# LIBTCC_FOUND - True if TCC library was found +# LIBTCC_INCLUDE_DIR - TCC headers path +# LIBTCC_LIBRARY - List of TCC libraries + +# Prevent vervosity if already included +if(LIBTCC_FOUND) + set(LIBTCC_FIND_QUITELY TRUE) +endif() + +include(FindPackageHandleStandardArgs) + +set(LIBTCC_SUFFIXES + x86_64-linux-gnu + aarch64-linux-gnu + arm-linux-gnueabi + arm-linux-gnueabihf + i386-linux-gnu + mips64el-linux-gnuabi64 + mipsel-linux-gnu + powerpc64le-linux-gnu + s390x-linux-gnu +) + +# Require TCC as shared library +set(LIBTCC_LIBRARY_NAMES + ${CMAKE_SHARED_LIBRARY_PREFIX}tcc${CMAKE_SHARED_LIBRARY_SUFFIX} +) + +find_library(LIBTCC_LIBRARY + NAMES ${LIBTCC_LIBRARY_NAMES} + PATHS /usr /usr/lib /usr/local /opt/local + PATH_SUFFIXES lib lib64 ${LIBTCC_SUFFIXES} +) + +find_path(LIBTCC_INCLUDE_DIR libtcc.h + PATHS /usr /usr/include /usr/local /opt/local + PATH_SUFFIXES ${LIBTCC_SUFFIXES} +) + +# Define TCC cmake module +find_package_handle_standard_args(LibTCC DEFAULT_MSG LIBTCC_LIBRARY LIBTCC_INCLUDE_DIR) + +# Mark cmake module as advanced +mark_as_advanced(LIBTCC_INCLUDE_DIR LIBTCC_LIBRARY) diff --git a/cmake/FindMetaCall.cmake b/cmake/FindMetaCall.cmake index f2915d8f26..f974e9509d 100644 --- a/cmake/FindMetaCall.cmake +++ b/cmake/FindMetaCall.cmake @@ -2,7 +2,7 @@ # CMake Find MetaCall library by Parra Studios # CMake script to find and include MetaCall library for development. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindNPM.cmake b/cmake/FindNPM.cmake new file mode 100644 index 0000000000..2a61990e25 --- /dev/null +++ b/cmake/FindNPM.cmake @@ -0,0 +1,89 @@ +# +# CMake Find NPM by Parra Studios +# CMake script to find NodeJS Package Manager. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find NPM executable and paths +# +# NPM_FOUND - True if npm was found +# NPM_GLOBAL_DIR - The global node_modules directory +# NPM_EXECUTABLE - The path to the npm executable +# NPM_VERSION - The version number + +set(NPM_ROOT /usr/bin CACHE STRING "NPM directory") + +find_program(NPM_EXECUTABLE + NAMES npm + HINTS /usr $ENV{NPM_ROOT}/npm ${NPM_ROOT}/npm +) + +if(NOT NPM_EXECUTABLE) + if(NPM_FIND_REQUIRED) + message(FATAL_ERROR "NPM was not found") + endif() + + return() +endif() + +# On Windows set NPM to the cmd version +if(WIN32 AND EXISTS "${NPM_EXECUTABLE}.cmd") + set(NPM_EXECUTABLE "${NPM_EXECUTABLE}.cmd") +endif() + +# Get NPM version +execute_process(COMMAND ${NPM_EXECUTABLE} -v + OUTPUT_VARIABLE NPM_VERSION + ERROR_VARIABLE NPM_VERSION_ERROR + RESULT_VARIABLE NPM_VERSION_CODE +) + +if(NPM_VERSION_CODE) + if(NPM_FIND_REQUIRED) + message(FATAL_ERROR "${NPM_EXECUTABLE} -v failed:\n${NPM_VERSION_CODE}") + endif() +endif() + +if(NPM_VERSION) + string(STRIP ${NPM_VERSION} NPM_VERSION) +endif() + +# Get global node_modules location +execute_process(COMMAND ${NPM_EXECUTABLE} root -g + OUTPUT_VARIABLE NPM_GLOBAL_DIR + ERROR_VARIABLE NPM_GLOBAL_DIR_ERROR + RESULT_VARIABLE NPM_GLOBAL_DIR_CODE +) + +if(NPM_GLOBAL_DIR) + string(STRIP ${NPM_GLOBAL_DIR} NPM_GLOBAL_DIR) +endif() + +if(NPM_GLOBAL_DIR_CODE) + if(NPM_FIND_REQUIRED) + message(FATAL_ERROR "${NPM_EXECUTABLE} root -g failed:\n${NPM_GLOBAL_DIR_ERROR}") + endif() +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(NPM + FOUND_VAR NPM_FOUND + REQUIRED_VARS NPM_EXECUTABLE + VERSION_VAR NPM_VERSION +) + +mark_as_advanced(NPM_FOUND NPM_GLOBAL_DIR NPM_EXECUTABLE NPM_VERSION) diff --git a/cmake/FindNodeJS.cmake b/cmake/FindNodeJS.cmake index ca7355c69d..6cea42bf48 100644 --- a/cmake/FindNodeJS.cmake +++ b/cmake/FindNodeJS.cmake @@ -1,49 +1,54 @@ # # CMake Find NodeJS JavaScript Runtime by Parra Studios -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Find NodeJS executable and include paths # -# NODEJS_FOUND - True if NodeJS was found -# NODEJS_INCLUDE_DIRS - NodeJS headers paths -# NODEJS_VERSION - NodeJS version -# NODEJS_VERSION_MAJOR - NodeJS major version -# NODEJS_VERSION_MINOR - NodeJS minor version -# NODEJS_VERSION_PATCH - NodeJS patch version -# NODEJS_MODULE_VERSION - NodeJS module version -# NODEJS_UV_VERSION - UV version of NodeJS -# NODEJS_UV_VERSION_MAJOR - UV major version of NodeJS -# NODEJS_UV_VERSION_MINOR - UV minor version of NodeJS -# NODEJS_UV_VERSION_PATCH - UV patch version of NodeJS -# NODEJS_V8_VERSION - V8 version of NodeJS -# NODEJS_V8_VERSION_MAJOR - V8 major version of NodeJS -# NODEJS_V8_VERSION_MINOR - V8 minor version of NodeJS -# NODEJS_V8_VERSION_PATCH - V8 patch version of NodeJS -# NODEJS_V8_VERSION_TWEAK - V8 patch version of NodeJS -# NODEJS_V8_VERSION_HEX - V8 version of NodeJS in hexadecimal format -# NODEJS_LIBRARY - NodeJS shared library -# NODEJS_EXECUTABLE - NodeJS shell +# NodeJS_FOUND - True if NodeJS was found +# NodeJS_INCLUDE_DIRS - NodeJS headers paths +# NodeJS_VERSION - NodeJS version +# NodeJS_VERSION_MAJOR - NodeJS major version +# NodeJS_VERSION_MINOR - NodeJS minor version +# NodeJS_VERSION_PATCH - NodeJS patch version +# NodeJS_MODULE_VERSION - NodeJS module version +# NodeJS_UV_VERSION - UV version of NodeJS +# NodeJS_UV_VERSION_MAJOR - UV major version of NodeJS +# NodeJS_UV_VERSION_MINOR - UV minor version of NodeJS +# NodeJS_UV_VERSION_PATCH - UV patch version of NodeJS +# NodeJS_V8_VERSION - V8 version of NodeJS +# NodeJS_V8_VERSION_MAJOR - V8 major version of NodeJS +# NodeJS_V8_VERSION_MINOR - V8 minor version of NodeJS +# NodeJS_V8_VERSION_PATCH - V8 patch version of NodeJS +# NodeJS_V8_VERSION_TWEAK - V8 patch version of NodeJS +# NodeJS_V8_VERSION_HEX - V8 version of NodeJS in hexadecimal format +# NodeJS_LIBRARY - NodeJS shared library +# NodeJS_EXECUTABLE - NodeJS shell # # Configuration variables: # -# NODEJS_CMAKE_DEBUG - Print paths for debugging -# NODEJS_EXECUTABLE_ONLY - Find only NodeJS executable (avoid library and include files) +# NodeJS_CMAKE_DEBUG - Print paths for debugging +# NodeJS_EXECUTABLE_ONLY - Find only NodeJS executable (avoid library and include files) +# NodeJS_SHARED_UV - If it is enabled, libuv won't be required by this script +# NodeJS_BUILD_FROM_SOURCE - If it is enabled, NodeJS runtime library will be built from source +# NodeJS_BUILD_WITHOUT_ICU - If it is enabled, NodeJS runtime library will be built without internationalization support +# NodeJS_INSTALL_PREFIX - Define a custom install prefix for NodeJS (Linux / Darwin only) # Prevent vervosity if already included -if(NODEJS_EXECUTABLE) - set(NODEJS_FIND_QUIETLY TRUE) +if(NodeJS_EXECUTABLE) + set(NodeJS_FIND_QUIETLY TRUE) endif() -option(NODEJS_CMAKE_DEBUG "Print paths for debugging NodeJS dependencies." OFF) -option(NODEJS_SHARED_UV "If it is enabled, libuv won't be required by this script." OFF) -option(NODEJS_BUILD_FROM_SOURCE "If it is enabled, NodeJS runtime library will be built from source." OFF) +option(NodeJS_CMAKE_DEBUG "Print paths for debugging NodeJS dependencies." OFF) +option(NodeJS_SHARED_UV "If it is enabled, libuv won't be required by this script." OFF) +option(NodeJS_BUILD_FROM_SOURCE "If it is enabled, NodeJS runtime library will be built from source." OFF) +option(NodeJS_BUILD_WITHOUT_ICU "If it is enabled, NodeJS runtime library will be built without internationalization support." OFF) # Include package manager include(FindPackageHandleStandardArgs) # NodeJS paths -set(NODEJS_PATHS +set(NodeJS_PATHS ${NODE_HOME} ${NODE_ROOT} $ENV{ProgramFiles}/node @@ -65,21 +70,21 @@ set(NODEJS_PATHS # Find NodeJS include directories if(MSVC OR CMAKE_BUILD_TYPE EQUAL "Debug") - set(NODEJS_V8_HEADERS v8.h v8-version.h v8-profiler.h) # v8-debug.h + set(NodeJS_V8_HEADERS v8.h v8-version.h v8-profiler.h) # v8-debug.h else() - set(NODEJS_V8_HEADERS v8.h v8-version.h) + set(NodeJS_V8_HEADERS v8.h v8-version.h) endif() -if(NOT NODEJS_SHARED_UV) - set(NODEJS_UV_HEADERS uv.h) +if(NOT NodeJS_SHARED_UV) + set(NodeJS_UV_HEADERS uv.h) endif() -set(NODEJS_HEADERS +set(NodeJS_HEADERS node.h node_api.h ) -set(NODEJS_INCLUDE_SUFFIXES +set(NodeJS_INCLUDE_SUFFIXES include include/node include/nodejs @@ -91,388 +96,570 @@ set(NODEJS_INCLUDE_SUFFIXES src ) -set(NODEJS_INCLUDE_PATHS +set(NodeJS_INCLUDE_PATHS /usr /usr/local ) # Find NodeJS executable -find_program(NODEJS_EXECUTABLE - NAMES node nodejs node.exe - HINTS ${NODEJS_PATHS} - PATH_SUFFIXES bin - DOC "NodeJS JavaScript Runtime Interpreter" -) +if(NOT NodeJS_EXECUTABLE) + find_program(NodeJS_EXECUTABLE + NAMES node nodejs node.exe + HINTS ${NodeJS_PATHS} + PATH_SUFFIXES bin + DOC "NodeJS JavaScript Runtime Interpreter" + ) +endif() -if(NODEJS_EXECUTABLE) +if(NodeJS_EXECUTABLE AND NOT NodeJS_VERSION) # Detect NodeJS version - execute_process(COMMAND ${NODEJS_EXECUTABLE} --version - OUTPUT_VARIABLE NODEJS_VERSION_TAG - RESULT_VARIABLE NODEJS_VERSION_TAG_RESULT + execute_process(COMMAND ${NodeJS_EXECUTABLE} --version + OUTPUT_VARIABLE NodeJS_VERSION_TAG + RESULT_VARIABLE NodeJS_VERSION_TAG_RESULT ) - if(NODEJS_VERSION_TAG_RESULT EQUAL 0) - string(REPLACE "v" "" NODEJS_VERSION_TAG "${NODEJS_VERSION_TAG}") - string(REPLACE "\n" "" NODEJS_VERSION_TAG "${NODEJS_VERSION_TAG}") - set(NODEJS_VERSION "${NODEJS_VERSION_TAG}") - string(REPLACE "." ";" NODEJS_VERSION_LIST "${NODEJS_VERSION}") - list(GET NODEJS_VERSION_LIST 0 NODEJS_VERSION_MAJOR) - list(GET NODEJS_VERSION_LIST 1 NODEJS_VERSION_MINOR) - list(GET NODEJS_VERSION_LIST 2 NODEJS_VERSION_PATCH) + if(NodeJS_VERSION_TAG_RESULT EQUAL 0) + string(REPLACE "v" "" NodeJS_VERSION_TAG "${NodeJS_VERSION_TAG}") + string(REPLACE "\n" "" NodeJS_VERSION_TAG "${NodeJS_VERSION_TAG}") + set(NodeJS_VERSION "${NodeJS_VERSION_TAG}") + string(REPLACE "." ";" NodeJS_VERSION_LIST "${NodeJS_VERSION}") + list(GET NodeJS_VERSION_LIST 0 NodeJS_VERSION_MAJOR) + list(GET NodeJS_VERSION_LIST 1 NodeJS_VERSION_MINOR) + list(GET NodeJS_VERSION_LIST 2 NodeJS_VERSION_PATCH) endif() # Detect UV version - execute_process(COMMAND ${NODEJS_EXECUTABLE} -e "console.log(process.versions.uv)" - OUTPUT_VARIABLE NODEJS_UV_VERSION_TAG - RESULT_VARIABLE NODEJS_UV_VERSION_TAG_RESULT + execute_process(COMMAND ${NodeJS_EXECUTABLE} -e "console.log(process.versions.uv)" + OUTPUT_VARIABLE NodeJS_UV_VERSION_TAG + RESULT_VARIABLE NodeJS_UV_VERSION_TAG_RESULT ) - if(NODEJS_VERSION_TAG_RESULT EQUAL 0) - string(REPLACE "\n" "" NODEJS_UV_VERSION_TAG "${NODEJS_UV_VERSION_TAG}") - set(NODEJS_UV_VERSION "${NODEJS_UV_VERSION_TAG}") - string(REPLACE "." ";" NODEJS_UV_VERSION_LIST "${NODEJS_UV_VERSION}") - list(GET NODEJS_UV_VERSION_LIST 0 NODEJS_UV_VERSION_MAJOR) - list(GET NODEJS_UV_VERSION_LIST 1 NODEJS_UV_VERSION_MINOR) - list(GET NODEJS_UV_VERSION_LIST 2 NODEJS_UV_VERSION_PATCH) + if(NodeJS_VERSION_TAG_RESULT EQUAL 0) + string(REPLACE "\n" "" NodeJS_UV_VERSION_TAG "${NodeJS_UV_VERSION_TAG}") + set(NodeJS_UV_VERSION "${NodeJS_UV_VERSION_TAG}") + string(REPLACE "." ";" NodeJS_UV_VERSION_LIST "${NodeJS_UV_VERSION}") + list(GET NodeJS_UV_VERSION_LIST 0 NodeJS_UV_VERSION_MAJOR) + list(GET NodeJS_UV_VERSION_LIST 1 NodeJS_UV_VERSION_MINOR) + list(GET NodeJS_UV_VERSION_LIST 2 NodeJS_UV_VERSION_PATCH) endif() # TODO: Implement V8 version by command? # Check if NodeJS executable only is requested - if(NODEJS_EXECUTABLE_ONLY) - find_package_handle_standard_args(NODEJS - REQUIRED_VARS NODEJS_EXECUTABLE - VERSION_VAR NODEJS_VERSION + if(NodeJS_EXECUTABLE_ONLY) + find_package_handle_standard_args(NodeJS + REQUIRED_VARS NodeJS_EXECUTABLE + VERSION_VAR NodeJS_VERSION ) - mark_as_advanced(NODEJS_EXECUTABLE) + mark_as_advanced(NodeJS_EXECUTABLE) - if(NODEJS_CMAKE_DEBUG) - message(STATUS "NODEJS_VERSION: ${NODEJS_VERSION}") - message(STATUS "NODEJS_EXECUTABLE: ${NODEJS_EXECUTABLE}") + if(NodeJS_CMAKE_DEBUG) + message(STATUS "NodeJS_VERSION: ${NodeJS_VERSION}") + message(STATUS "NodeJS_EXECUTABLE: ${NodeJS_EXECUTABLE}") endif() return() endif() endif() -# Find NodeJS includes -find_path(NODEJS_INCLUDE_DIR - NAMES ${NODEJS_HEADERS} - PATHS ${NODEJS_INCLUDE_PATHS} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} - DOC "NodeJS JavaScript Runtime Headers" -) +if(NOT NodeJS_BUILD_FROM_SOURCE) + if(NOT NodeJS_INCLUDE_DIR) + # Find NodeJS includes + find_path(NodeJS_INCLUDE_DIR + NAMES ${NodeJS_HEADERS} + PATHS ${NodeJS_INCLUDE_PATHS} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime Headers" + ) + endif() -# Check if the include directory contains all headers in the same folder -if(NODEJS_INCLUDE_DIR) - foreach(HEADER IN ITEMS ${NODEJS_HEADERS}) - if(NOT EXISTS ${NODEJS_INCLUDE_DIR}/${HEADER}) - message(WARNING "NodeJS header ${HEADER} not found in ${NODEJS_INCLUDE_DIR}") - set(NODEJS_INCLUDE_DIR FALSE) - break() - endif() - endforeach() -endif() + # Check if the include directory contains all headers in the same folder + if(NodeJS_INCLUDE_DIR) + foreach(HEADER IN ITEMS ${NodeJS_HEADERS}) + if(NOT EXISTS ${NodeJS_INCLUDE_DIR}/${HEADER}) + message(WARNING "NodeJS header ${HEADER} not found in ${NodeJS_INCLUDE_DIR}") + unset(NodeJS_INCLUDE_DIR CACHE) + break() + endif() + endforeach() + endif() -# Find NodeJS V8 includes -find_path(NODEJS_V8_INCLUDE_DIR - NAMES ${NODEJS_V8_HEADERS} - PATHS ${NODEJS_INCLUDE_PATHS} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} - DOC "NodeJS JavaScript Runtime V8 Headers" -) + # Find NodeJS V8 includes + if(NOT NodeJS_V8_INCLUDE_DIR) + find_path(NodeJS_V8_INCLUDE_DIR + NAMES ${NodeJS_V8_HEADERS} + PATHS ${NodeJS_INCLUDE_PATHS} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime V8 Headers" + ) + endif() -# Check if the include directory contains all headers in the same folder -if(NODEJS_V8_INCLUDE_DIR) - foreach(HEADER IN ITEMS ${NODEJS_V8_HEADERS}) - if(NOT EXISTS ${NODEJS_V8_INCLUDE_DIR}/${HEADER}) - message(WARNING "NodeJS header ${HEADER} not found in ${NODEJS_V8_INCLUDE_DIR}") - set(NODEJS_V8_INCLUDE_DIR FALSE) - break() - endif() - endforeach() -endif() + # Check if the include directory contains all headers in the same folder + if(NodeJS_V8_INCLUDE_DIR) + foreach(HEADER IN ITEMS ${NodeJS_V8_HEADERS}) + if(NOT EXISTS ${NodeJS_V8_INCLUDE_DIR}/${HEADER}) + message(WARNING "NodeJS header ${HEADER} not found in ${NodeJS_V8_INCLUDE_DIR}") + unset(NodeJS_V8_INCLUDE_DIR CACHE) + break() + endif() + endforeach() + endif() -# Find NodeJS UV includes -find_path(NODEJS_UV_INCLUDE_DIR - NAMES ${NODEJS_UV_HEADERS} - PATHS ${NODEJS_INCLUDE_PATHS} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} - DOC "NodeJS JavaScript Runtime UV Headers" -) + # Find NodeJS UV includes + if(NOT NodeJS_UV_INCLUDE_DIR) + find_path(NodeJS_UV_INCLUDE_DIR + NAMES ${NodeJS_UV_HEADERS} + PATHS ${NodeJS_INCLUDE_PATHS} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime UV Headers" + ) + endif() -# Check if the include directory contains all headers in the same folder -if(NODEJS_UV_INCLUDE_DIR) - foreach(HEADER IN ITEMS ${NODEJS_UV_HEADERS}) - if(NOT EXISTS ${NODEJS_UV_INCLUDE_DIR}/${HEADER}) - message(WARNING "NodeJS header ${HEADER} not found in ${NODEJS_UV_INCLUDE_DIR}") - set(NODEJS_UV_INCLUDE_DIR FALSE) - break() - endif() - endforeach() + # Check if the include directory contains all headers in the same folder + if(NodeJS_UV_INCLUDE_DIR) + foreach(HEADER IN ITEMS ${NodeJS_UV_HEADERS}) + if(NOT EXISTS ${NodeJS_UV_INCLUDE_DIR}/${HEADER}) + message(WARNING "NodeJS header ${HEADER} not found in ${NodeJS_UV_INCLUDE_DIR}") + unset(NodeJS_UV_INCLUDE_DIR CACHE) + break() + endif() + endforeach() + endif() endif() # Download includes in case they are not distributed -if(NOT NODEJS_INCLUDE_DIR OR NOT NODEJS_V8_INCLUDE_DIR OR NOT NODEJS_UV_INCLUDE_DIR) - if(NOT NODEJS_VERSION) +if(NOT NodeJS_INCLUDE_DIR OR NOT NodeJS_V8_INCLUDE_DIR OR NOT NodeJS_UV_INCLUDE_DIR) + if(NOT NodeJS_VERSION) # We do not have any way to know what version to install message(WARNING "NodeJS headers could not be found, neither a valid NodeJS version.") return() endif() # NodeJS download and output path (workaround for NodeJS headers) - set(NODEJS_DOWNLOAD_URL "https://nodejs.org/dist/v$%7BNODEJS_VERSION%7D/node-v$%7BNODEJS_VERSION%7D-headers.tar.gz") - set(NODEJS_BASE_PATH "${CMAKE_CURRENT_BINARY_DIR}/headers") - set(NODEJS_DOWNLOAD_FILE "${NODEJS_BASE_PATH}/node-v${NODEJS_VERSION}-headers.tar.gz") - set(NODEJS_OUTPUT_PATH "${NODEJS_BASE_PATH}/node-v${NODEJS_VERSION}") + set(NodeJS_DOWNLOAD_URL "https://nodejs.org/dist/v$%7BNodeJS_VERSION%7D/node-v$%7BNodeJS_VERSION%7D-headers.tar.gz") + set(NodeJS_BASE_PATH "${CMAKE_CURRENT_BINARY_DIR}/headers") + set(NodeJS_DOWNLOAD_FILE "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}-headers.tar.gz") + set(NodeJS_HEADERS_OUTPUT_PATH "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}") # Download node if needed - if(NOT EXISTS "${NODEJS_DOWNLOAD_FILE}") + if(NOT EXISTS "${NodeJS_DOWNLOAD_FILE}") message(STATUS "Downloading NodeJS headers") - file(DOWNLOAD ${NODEJS_DOWNLOAD_URL} ${NODEJS_DOWNLOAD_FILE}) + file(DOWNLOAD ${NodeJS_DOWNLOAD_URL} ${NodeJS_DOWNLOAD_FILE} SHOW_PROGRESS) endif() # Decompress node if needed - if(NOT EXISTS "${NODEJS_OUTPUT_PATH}") + if(NOT EXISTS "${NodeJS_HEADERS_OUTPUT_PATH}") message(STATUS "Extract NodeJS headers") - execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${NODEJS_DOWNLOAD_FILE}" WORKING_DIRECTORY "${NODEJS_BASE_PATH}" OUTPUT_QUIET) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${NodeJS_DOWNLOAD_FILE}" WORKING_DIRECTORY "${NodeJS_BASE_PATH}" OUTPUT_QUIET) endif() # Find NodeJS includes - find_path(NODEJS_INCLUDE_DIR - NAMES ${NODEJS_HEADERS} - PATHS ${NODEJS_OUTPUT_PATH} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} + find_path(NodeJS_INCLUDE_DIR + NAMES ${NodeJS_HEADERS} + PATHS ${NodeJS_HEADERS_OUTPUT_PATH} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} DOC "NodeJS JavaScript Runtime Headers" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) + + # Find NodeJS V8 includes + find_path(NodeJS_V8_INCLUDE_DIR + NAMES ${NodeJS_V8_HEADERS} + PATHS ${NodeJS_HEADERS_OUTPUT_PATH} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime V8 Headers" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) + + # Find NodeJS UV includes + find_path(NodeJS_UV_INCLUDE_DIR + NAMES ${NodeJS_UV_HEADERS} + PATHS ${NodeJS_HEADERS_OUTPUT_PATH} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime UV Headers" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH ) endif() -if(NODEJS_INCLUDE_DIR) +if(NodeJS_INCLUDE_DIR) # Get node module version - find_file(NODEJS_VERSION_FILE_PATH node_version.h - PATHS ${NODEJS_INCLUDE_DIR} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} + find_file(NodeJS_VERSION_FILE_PATH node_version.h + PATHS ${NodeJS_INCLUDE_DIR} DOC "NodeJS JavaScript Version Header" ) - if(NODEJS_VERSION_FILE_PATH) - file(READ ${NODEJS_VERSION_FILE_PATH} NODEJS_VERSION_FILE) + if(NOT NodeJS_VERSION_FILE_PATH) + find_file(NodeJS_VERSION_FILE_PATH node_version.h + PATHS ${NodeJS_INCLUDE_DIR} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Version Header" + ) + endif() - string(REGEX MATCH "#define NODE_MODULE_VERSION ([0-9]+)" NODEJS_MODULE_VERSION_DEF ${NODEJS_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_MODULE_VERSION ${NODEJS_MODULE_VERSION_DEF}) + if(NodeJS_VERSION_FILE_PATH) + file(READ ${NodeJS_VERSION_FILE_PATH} NodeJS_VERSION_FILE) + + string(REGEX MATCH "#define NODE_MODULE_VERSION ([0-9]+)" NodeJS_MODULE_VERSION_DEF ${NodeJS_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_MODULE_VERSION ${NodeJS_MODULE_VERSION_DEF}) endif() endif() -if(NODEJS_V8_INCLUDE_DIR) +if(NodeJS_V8_INCLUDE_DIR) # Detect NodeJS V8 version - find_file(NODEJS_V8_VERSION_FILE_PATH v8-version.h - PATHS ${NODEJS_V8_INCLUDE_DIR} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} + find_file(NodeJS_V8_VERSION_FILE_PATH v8-version.h + PATHS ${NodeJS_V8_INCLUDE_DIR} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} DOC "NodeJS V8 JavaScript Version Header" ) - if(NODEJS_V8_VERSION_FILE_PATH) - file(READ ${NODEJS_V8_VERSION_FILE_PATH} NODEJS_V8_VERSION_FILE) + if(NodeJS_V8_VERSION_FILE_PATH) + file(READ ${NodeJS_V8_VERSION_FILE_PATH} NodeJS_V8_VERSION_FILE) - string(REGEX MATCH "#define V8_MAJOR_VERSION ([0-9]+)" NODEJS_V8_VERSION_MAJOR_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_MAJOR ${NODEJS_V8_VERSION_MAJOR_DEF}) + string(REGEX MATCH "#define V8_MAJOR_VERSION ([0-9]+)" NodeJS_V8_VERSION_MAJOR_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_MAJOR ${NodeJS_V8_VERSION_MAJOR_DEF}) - string(REGEX MATCH "#define V8_MINOR_VERSION ([0-9]+)" NODEJS_V8_VERSION_MINOR_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_MINOR ${NODEJS_V8_VERSION_MINOR_DEF}) + string(REGEX MATCH "#define V8_MINOR_VERSION ([0-9]+)" NodeJS_V8_VERSION_MINOR_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_MINOR ${NodeJS_V8_VERSION_MINOR_DEF}) - string(REGEX MATCH "#define V8_BUILD_NUMBER ([0-9]+)" NODEJS_V8_VERSION_PATCH_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_PATCH ${NODEJS_V8_VERSION_PATCH_DEF}) + string(REGEX MATCH "#define V8_BUILD_NUMBER ([0-9]+)" NodeJS_V8_VERSION_PATCH_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_PATCH ${NodeJS_V8_VERSION_PATCH_DEF}) - string(REGEX MATCH "#define V8_PATCH_LEVEL ([0-9]+)" NODEJS_V8_VERSION_TWEAK_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_TWEAK ${NODEJS_V8_VERSION_TWEAK_DEF}) + string(REGEX MATCH "#define V8_PATCH_LEVEL ([0-9]+)" NodeJS_V8_VERSION_TWEAK_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_TWEAK ${NodeJS_V8_VERSION_TWEAK_DEF}) - set(NODEJS_V8_VERSION "${NODEJS_V8_VERSION_MAJOR}.${NODEJS_V8_VERSION_MINOR}.${NODEJS_V8_VERSION_PATCH}.${NODEJS_V8_VERSION_TWEAK}") + set(NodeJS_V8_VERSION "${NodeJS_V8_VERSION_MAJOR}.${NodeJS_V8_VERSION_MINOR}.${NodeJS_V8_VERSION_PATCH}.${NodeJS_V8_VERSION_TWEAK}") - set(NODEJS_V8_VERSION_HEX 0x0${NODEJS_V8_VERSION_MAJOR}${NODEJS_V8_VERSION_MINOR}${NODEJS_V8_VERSION_PATCH}${NODEJS_V8_VERSION_TWEAK}) - string(LENGTH "${NODEJS_V8_VERSION_HEX}" NODEJS_V8_VERSION_HEX_LENGTH) + set(NodeJS_V8_VERSION_HEX 0x0${NodeJS_V8_VERSION_MAJOR}${NodeJS_V8_VERSION_MINOR}${NodeJS_V8_VERSION_PATCH}${NodeJS_V8_VERSION_TWEAK}) + string(LENGTH "${NodeJS_V8_VERSION_HEX}" NodeJS_V8_VERSION_HEX_LENGTH) - while(NODEJS_V8_VERSION_HEX_LENGTH LESS 8) + while(NodeJS_V8_VERSION_HEX_LENGTH LESS 8) - set(NODEJS_V8_VERSION_HEX "${NODEJS_V8_VERSION_HEX}0") - string(LENGTH "${NODEJS_V8_VERSION_HEX}" NODEJS_V8_VERSION_HEX_LENGTH) + set(NodeJS_V8_VERSION_HEX "${NodeJS_V8_VERSION_HEX}0") + string(LENGTH "${NodeJS_V8_VERSION_HEX}" NodeJS_V8_VERSION_HEX_LENGTH) endwhile() endif() endif() # Find NodeJS library from module version -if(NODEJS_MODULE_VERSION AND NOT NODEJS_BUILD_FROM_SOURCE) +if(NodeJS_MODULE_VERSION) # NodeJS library names - set(NODEJS_LIBRARY_NAMES - libnode.so.${NODEJS_MODULE_VERSION} - libnode.${NODEJS_MODULE_VERSION}.dylib - libnode.${NODEJS_MODULE_VERSION}.dll - node.lib - ) - - message(STATUS "Searching NodeJS library version ${NODEJS_MODULE_VERSION}") - if(WIN32) - set(NODEJS_COMPILE_PATH "${NODEJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}") + if(NodeJS_VERSION_MAJOR GREATER_EQUAL 14) + set(NodeJS_LIBRARY_NAMES + libnode.${NodeJS_MODULE_VERSION}.dll + libnode.dll + libnode.lib + ) + else() + set(NodeJS_LIBRARY_NAMES + node.${NodeJS_MODULE_VERSION}.dll + node.dll + node.lib + ) + endif() else() - set(NODEJS_COMPILE_PATH "${NODEJS_OUTPUT_PATH}/out/${CMAKE_BUILD_TYPE}") + set(NodeJS_LIBRARY_NAMES + libnode.so.${NodeJS_MODULE_VERSION} + libnode.so + libnode.${NodeJS_MODULE_VERSION}.dylib + libnode.dylib + ) endif() - if(WIN32) - set(NODEJS_LIBRARY_PATH "${NODEJS_COMPILE_PATH}") # TODO: Set a valid install path - else() - set(NODEJS_LIBRARY_PATH "/usr/local/lib") - endif() + if(NOT NodeJS_BUILD_FROM_SOURCE) + message(STATUS "Searching NodeJS library version ${NodeJS_MODULE_VERSION}") + + # Find library + find_library(NodeJS_LIBRARY + NAMES ${NodeJS_LIBRARY_NAMES} + HINTS ${NodeJS_PATHS} + PATH_SUFFIXES lib + DOC "NodeJS JavaScript Runtime Library" + ) - set(NODEJS_SYSTEM_LIBRARY_PATH "/lib/x86_64-linux-gnu") # TODO: Add others + if(NOT NodeJS_LIBRARY) + if(WIN32) + set(NodeJS_LIBRARY_PATH "C:/Program Files/nodejs") + else() + set(NodeJS_LIBRARY_PATH "/usr/local/lib" "/usr/lib") + endif() - # Find library - find_library(NODEJS_LIBRARY - NAMES ${NODEJS_LIBRARY_NAMES} - PATHS ${NODEJS_COMPILE_PATH} ${NODEJS_LIBRARY_PATH} ${NODEJS_SYSTEM_LIBRARY_PATH} - DOC "NodeJS JavaScript Runtime Library" - ) + set(NodeJS_SYSTEM_LIBRARY_PATH "/lib/x86_64-linux-gnu" "/usr/lib/x86_64-linux-gnu") # TODO: Add others + + find_library(NodeJS_LIBRARY + NAMES ${NodeJS_LIBRARY_NAMES} + PATHS ${NodeJS_LIBRARY_PATH} ${NodeJS_SYSTEM_LIBRARY_PATH} + DOC "NodeJS JavaScript Runtime Library" + ) + endif() + + if(NodeJS_LIBRARY) + message(STATUS "NodeJS Library Found") + endif() + endif() endif() # Install NodeJS library in case it is not distributed -if(NOT NODEJS_LIBRARY) +if(NOT NodeJS_LIBRARY) + message(STATUS "NodeJS library not found, trying to build it from source") + # NodeJS download and output path (workaround to compile node as a shared library) - set(NODEJS_DOWNLOAD_URL "https://nodejs.org/dist/v$%7BNODEJS_VERSION%7D/node-v$%7BNODEJS_VERSION%7D.tar.gz") - set(NODEJS_BASE_PATH "${CMAKE_CURRENT_BINARY_DIR}/sources") - set(NODEJS_DOWNLOAD_FILE "${NODEJS_BASE_PATH}/node-v${NODEJS_VERSION}.tar.gz") - set(NODEJS_OUTPUT_PATH "${NODEJS_BASE_PATH}/node-v${NODEJS_VERSION}") + set(NodeJS_DOWNLOAD_URL "https://nodejs.org/dist/v$%7BNodeJS_VERSION%7D/node-v$%7BNodeJS_VERSION%7D.tar.gz") + set(NodeJS_BASE_PATH "${CMAKE_CURRENT_BINARY_DIR}/sources") + set(NodeJS_DOWNLOAD_FILE "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}.tar.gz") + set(NodeJS_OUTPUT_PATH "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}") # Download node if needed - if(NOT EXISTS "${NODEJS_DOWNLOAD_FILE}") - message(STATUS "Downloading NodeJS distribution v${NODEJS_VERSION}") - file(DOWNLOAD ${NODEJS_DOWNLOAD_URL} ${NODEJS_DOWNLOAD_FILE}) + if(NOT EXISTS "${NodeJS_DOWNLOAD_FILE}") + message(STATUS "Downloading NodeJS distribution v${NodeJS_VERSION}") + file(DOWNLOAD ${NodeJS_DOWNLOAD_URL} ${NodeJS_DOWNLOAD_FILE} SHOW_PROGRESS) endif() # Decompress node if needed - if(NOT EXISTS "${NODEJS_OUTPUT_PATH}") + if(NOT EXISTS "${NodeJS_OUTPUT_PATH}") message(STATUS "Extract NodeJS distribution") - execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${NODEJS_DOWNLOAD_FILE}" WORKING_DIRECTORY "${NODEJS_BASE_PATH}" OUTPUT_QUIET) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${NodeJS_DOWNLOAD_FILE}" WORKING_DIRECTORY "${NodeJS_BASE_PATH}" OUTPUT_QUIET) + endif() + + if(WIN32) + if(NodeJS_VERSION_MAJOR LESS 14) + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}") + else() + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/out/${CMAKE_BUILD_TYPE}") + endif() + else() + if(NodeJS_VERSION_MAJOR LESS 14) + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/out") + else() + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/out/${CMAKE_BUILD_TYPE}") + endif() endif() # Compile node as a shared library if needed - if(NOT EXISTS "${NODEJS_COMPILE_PATH}") - if(WIN32) - if(NOT EXISTS "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/node.dll") + if(NOT EXISTS "${NodeJS_COMPILE_PATH}") + if(WIN32 AND MSVC) + if(NOT EXISTS "${NodeJS_COMPILE_PATH}/node.dll" AND NOT EXISTS "${NodeJS_COMPILE_PATH}/libnode.dll") message(STATUS "Build NodeJS shared library") if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86") - set(NODEJS_COMPILE_ARCH "x86") + set(NodeJS_COMPILE_ARCH "x86") else() - set(NODEJS_COMPILE_ARCH "x64") + set(NodeJS_COMPILE_ARCH "x64") endif() - # Check vs2017 or vs2015 (TODO: Add more versions if they are supported by NodeJS) - if(MSVC_VERSION EQUAL 1900) - set(NODEJS_MSVC_VER vs2015) + # Check for Visual Studio Version and configure the build command + if(MSVC_VERSION GREATER_EQUAL 1930) + set(NodeJS_MSVC_VER vs2022) + elseif(MSVC_VERSION GREATER_EQUAL 1920) + set(NodeJS_MSVC_VER vs2019) elseif(MSVC_VERSION GREATER 1900) - set(NODEJS_MSVC_VER vs2017) + set(NodeJS_MSVC_VER vs2017) + elseif(MSVC_VERSION EQUAL 1900) + set(NodeJS_MSVC_VER vs2015) + else() + message(FATAL_ERROR "Version of Visual Studio too old, add other toolsets in FindNodeJS.cmake in order to support them") endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(NODEJS_BUILD_TYPE debug) + set(NodeJS_BUILD_TYPE debug) + else() + set(NodeJS_BUILD_TYPE release) + endif() + + if(NodeJS_BUILD_WITHOUT_ICU) + set(BUILD_ICU_FLAGS without-intl) else() - set(NODEJS_BUILD_TYPE release) + set(BUILD_ICU_FLAGS) endif() - execute_process(COMMAND vcbuild.bat dll ${NODEJS_BUILD_TYPE} ${NODEJS_COMPILE_ARCH} ${NODEJS_MSVC_VER} WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + # Building NodeJS 14 as library in Windows is broken (so we need to patch it) + if(WIN32 AND NodeJS_VERSION_MAJOR GREATER_EQUAL 14) + find_package(Python COMPONENTS Interpreter REQUIRED) - # TODO: Implement msi build - # execute_process(COMMAND vcbuild.bat dll ${NODEJS_BUILD_TYPE} ${NODEJS_COMPILE_ARCH} ${NODEJS_MSVC_VER} msi WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + execute_process( + COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/cmake/NodeJSGYPPatch.py ${NodeJS_OUTPUT_PATH}/node.gyp + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + RESULT_VARIABLE NodeJS_PATCH_SCRIPT + ) - # Copy library to MetaCall output path - file(COPY ${NODEJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}/node.dll DESTINATION ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/node.dll) + if(NOT NodeJS_PATCH_SCRIPT EQUAL 0) + message(FATAL_ERROR "FindNodeJS.cmake failed to patch node.gyp project") + endif() + endif() - message(STATUS "Install NodeJS shared library") + execute_process( + COMMAND vcbuild.bat dll ${NodeJS_BUILD_TYPE} ${NodeJS_COMPILE_ARCH} ${NodeJS_MSVC_VER} ${BUILD_ICU_FLAGS} + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + ) - # TODO: Implement install command - #execute_process(COMMAND msiexec /a "node-v${NODEJS_VERSION}-${NODEJS_COMPILE_ARCH}.msi" WORKING_DIRECTORY "${NODEJS_COMPILE_PATH}" OUTPUT_QUIET) - endif() + if(EXISTS "${NodeJS_COMPILE_PATH}/node.dll") + set(NodeJS_LIBRARY_NAME "node.dll") + elseif(EXISTS "${NodeJS_COMPILE_PATH}/libnode.dll") + set(NodeJS_LIBRARY_NAME "libnode.dll") + else() + message(FATAL_ERROR "FindNodeJS.cmake failed to build node library") + endif() - # TODO: Delete this workaround after implementing the install command - set(NODEJS_LIBRARY_PATH ${NODEJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}) + # Copy library to MetaCall output path + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_OUTPUT_DIR}) + file(COPY "${NodeJS_COMPILE_PATH}/${NodeJS_LIBRARY_NAME}" DESTINATION ${PROJECT_OUTPUT_DIR}) + endif() else() message(STATUS "Configure NodeJS shared library") - # Select the ICU library depending on the NodeJS version - if("${NODEJS_VERSION_MAJOR}" GREATER_EQUAL "14") - set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-66-1/icu4c-66_1-src.zip") - elseif("${NODEJS_VERSION_MAJOR}" GREATER_EQUAL "12") - set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-65-1/icu4c-65_1-src.zip") - elseif("${NODEJS_VERSION_MAJOR}" GREATER_EQUAL "10") - set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-src.zip") + if(NodeJS_BUILD_WITHOUT_ICU) + set(BUILD_ICU_FLAGS "--without-intl") + else() + # Select the ICU library depending on the NodeJS version + if("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "20") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-73-1/icu4c-73_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "18") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-72-1/icu4c-72_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "16") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-69-1/icu4c-69_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "15") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-67-1/icu4c-67_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "14") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-66-1/icu4c-66_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "12") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-65-1/icu4c-65_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "10") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-src.zip") + endif() + + # Workaround for configure bug: ERROR: Could not load deps/icu/source/common/unicode/uvernum.h - is ICU installed? + if("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "18") + set(NodeJS_ICU_DOWNLOAD_FILE "${NodeJS_BASE_PATH}/icu4c-src.zip") + file(DOWNLOAD ${ICU_URL} "${NodeJS_ICU_DOWNLOAD_FILE}") + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}/deps" + COMMAND ${CMAKE_COMMAND} -E tar xzf "${NodeJS_ICU_DOWNLOAD_FILE}" + ) + set(ICU_URL "${NodeJS_OUTPUT_PATH}/deps/icu") + endif() + + # Workaround for OpenSSL bug: https://github.com/metacall/core/issues/223 + if(APPLE) + set(ICU_ENV_VAR ${CMAKE_COMMAND} -E env PYTHONHTTPSVERIFY=0) + else() + set(ICU_ENV_VAR) + endif() + + set(BUILD_ICU_FLAGS "--with-icu-source=${ICU_URL}") endif() - if("${CMAKE_BUILD_TYPE}" EQUAL "Debug") - execute_process(COMMAND sh -c "./configure --with-icu-source=${ICU_URL} --shared --debug" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + if(NodeJS_INSTALL_PREFIX) + set(NodeJS_PREFIX "--prefix=${NodeJS_INSTALL_PREFIX}") else() - execute_process(COMMAND sh -c "./configure --with-icu-source=${ICU_URL} --shared" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + set(NodeJS_PREFIX) + endif() + + set(BUILD_DEBUG) + set(BUILD_DEBUG_ASAN) + set(BUILD_DEBUG_ASAN_OPTIONS) + + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set(BUILD_DEBUG --debug) + + if(OPTION_BUILD_ADDRESS_SANITIZER) + set(BUILD_DEBUG_ASAN --enable-asan) + + if("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "20") + set(BUILD_DEBUG_ASAN_OPTIONS ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_container_overflow=0) + endif() + endif() endif() + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + COMMAND ${ICU_ENV_VAR} ./configure ${NodeJS_PREFIX} ${BUILD_ICU_FLAGS} --shared ${BUILD_DEBUG} ${BUILD_DEBUG_ASAN} + ) + message(STATUS "Build NodeJS shared library") + # Create build output directory + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${NodeJS_OUTPUT_PATH}/out) + include(ProcessorCount) ProcessorCount(N) - if(NOT N EQUAL 0) - execute_process(COMMAND sh -c "alias python=`which python2.7`; make -j${N} -C out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}" OUTPUT_QUIET) + if(N GREATER 1) + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + COMMAND ${BUILD_DEBUG_ASAN_OPTIONS} make -j${N} -C ${NodeJS_OUTPUT_PATH}/out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1 + ) else() - execute_process(COMMAND sh -c "alias python=`which python2.7`; make -C out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}" OUTPUT_QUIET) + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + COMMAND ${BUILD_DEBUG_ASAN_OPTIONS} make -C ${NodeJS_OUTPUT_PATH}/out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1 + ) endif() - - message(STATUS "Install NodeJS shared library") - - execute_process(COMMAND sh -c "make install" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}" OUTPUT_QUIET) endif() endif() - if(NODEJS_MODULE_VERSION) - # NodeJS library names - set(NODEJS_LIBRARY_NAMES - libnode.so.${NODEJS_MODULE_VERSION} - libnode.${NODEJS_MODULE_VERSION}.dylib - libnode.${NODEJS_MODULE_VERSION}.dll - node.lib - ) + # Set up the compile path in case of prefix is specified + if(NOT WIN32 AND NOT MSVC AND NodeJS_INSTALL_PREFIX) + set(NodeJS_COMPILE_PREFIX_PATH "${NodeJS_INSTALL_PREFIX}/lib") + endif() - # Find library - find_library(NODEJS_LIBRARY - NAMES ${NODEJS_LIBRARY_NAMES} - PATHS ${NODEJS_LIBRARY_PATH} - DOC "NodeJS JavaScript Runtime Library" - ) + # Find compiled library + find_library(NodeJS_LIBRARY + NAMES ${NodeJS_LIBRARY_NAMES} + PATHS ${NodeJS_COMPILE_PATH} ${NodeJS_COMPILE_PREFIX_PATH} + DOC "NodeJS JavaScript Runtime Library" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) + + if(NOT NodeJS_LIBRARY) + message(FATAL_ERROR "NodeJS library not found and it could not be built from source") + else() + set(NodeJS_BUILD_FROM_SOURCE TRUE) endif() endif() -set(NODEJS_INCLUDE_DIRS "${NODEJS_INCLUDE_DIR}") +set(NodeJS_INCLUDE_DIRS "${NodeJS_INCLUDE_DIR}") -if(NODEJS_V8_INCLUDE_DIR) - set(NODEJS_INCLUDE_DIRS "${NODEJS_INCLUDE_DIRS}" "${NODEJS_V8_INCLUDE_DIR}") +if(NodeJS_V8_INCLUDE_DIR) + set(NodeJS_INCLUDE_DIRS "${NodeJS_INCLUDE_DIRS}" "${NodeJS_V8_INCLUDE_DIR}") +endif() + +if(NodeJS_UV_INCLUDE_DIR) + set(NodeJS_INCLUDE_DIRS "${NodeJS_INCLUDE_DIRS}" "${NodeJS_UV_INCLUDE_DIR}") +endif() + +if(NOT NodeJS_LIBRARY_NAME) + if(WIN32 AND MSVC) + get_filename_component(NodeJS_LIBRARY_NAME "${NodeJS_LIBRARY}" NAME_WLE) + set(NodeJS_LIBRARY_NAME "${NodeJS_LIBRARY_NAME}.dll") + else() + get_filename_component(NodeJS_LIBRARY_NAME "${NodeJS_LIBRARY}" NAME) + endif() endif() -if(NODEJS_UV_INCLUDE_DIR) - set(NODEJS_INCLUDE_DIRS "${NODEJS_INCLUDE_DIRS}" "${NODEJS_UV_INCLUDE_DIR}") +if(NOT NodeJS_LIBRARY_NAME_PATH AND WIN32) + string(REGEX REPLACE "[.]lib$" ".dll" NodeJS_LIBRARY_NAME_PATH ${NodeJS_LIBRARY}) endif() -find_package_handle_standard_args(NODEJS - REQUIRED_VARS NODEJS_EXECUTABLE NODEJS_INCLUDE_DIRS NODEJS_LIBRARY - VERSION_VAR NODEJS_VERSION +find_package_handle_standard_args(NodeJS + REQUIRED_VARS NodeJS_EXECUTABLE NodeJS_INCLUDE_DIRS NodeJS_LIBRARY NodeJS_LIBRARY_NAME + VERSION_VAR NodeJS_VERSION ) -mark_as_advanced(NODEJS_EXECUTABLE NODEJS_INCLUDE_DIRS NODEJS_LIBRARY) +mark_as_advanced(NodeJS_EXECUTABLE NodeJS_INCLUDE_DIRS NodeJS_LIBRARY NodeJS_LIBRARY_NAME) -if(NODEJS_CMAKE_DEBUG) - message(STATUS "NODEJS_INCLUDE_DIRS: ${NODEJS_INCLUDE_DIRS}") - message(STATUS "NODEJS_VERSION: ${NODEJS_VERSION}") - message(STATUS "NODEJS_UV_VERSION: ${NODEJS_UV_VERSION}") - message(STATUS "NODEJS_V8_VERSION: ${NODEJS_V8_VERSION}") - message(STATUS "NODEJS_V8_VERSION_HEX: ${NODEJS_V8_VERSION_HEX}") - message(STATUS "NODEJS_LIBRARY: ${NODEJS_LIBRARY}") - message(STATUS "NODEJS_EXECUTABLE: ${NODEJS_EXECUTABLE}") +if(NodeJS_CMAKE_DEBUG) + message(STATUS "NodeJS_INCLUDE_DIRS: ${NodeJS_INCLUDE_DIRS}") + message(STATUS "NodeJS_VERSION: ${NodeJS_VERSION}") + message(STATUS "NodeJS_UV_VERSION: ${NodeJS_UV_VERSION}") + message(STATUS "NodeJS_V8_VERSION: ${NodeJS_V8_VERSION}") + message(STATUS "NodeJS_V8_VERSION_HEX: ${NodeJS_V8_VERSION_HEX}") + message(STATUS "NodeJS_LIBRARY: ${NodeJS_LIBRARY}") + message(STATUS "NodeJS_EXECUTABLE: ${NodeJS_EXECUTABLE}") endif() diff --git a/.editorconfig b/cmake/FindPatchelf.cmake similarity index 55% rename from .editorconfig rename to cmake/FindPatchelf.cmake index f7c5913e11..044cd412d0 100644 --- a/.editorconfig +++ b/cmake/FindPatchelf.cmake @@ -1,8 +1,8 @@ # -# MetaCall EditorConfig Configuration by Parra Studios -# EditorConfig helps developers define and maintain consistent coding +# CMake Find Patchelf by Parra Studios +# CMake script to find Patchelf executable. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,17 +15,18 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# styles between different editors and IDEs (http://editorconfig.org). # -root = true +# Prevent vervosity if already included +if(Patchelf_FOUND) + set(Patchelf_FIND_QUIETLY TRUE) +endif() -[*] -indent_style = tab -indent_size = tab -tab_width = 4 -end_of_line = crlf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -max_line_length = 120 +# Find patchelf +find_program(Patchelf_EXECUTABLE NAMES patchelf) + +if(Patchelf_EXECUTABLE) + set(Patchelf_FOUND TRUE) +else() + set(Patchelf_FOUND FALSE) +endif() diff --git a/cmake/FindRapidJSON.cmake b/cmake/FindRapidJSON.cmake index 188d686786..26b0d6b1e1 100644 --- a/cmake/FindRapidJSON.cmake +++ b/cmake/FindRapidJSON.cmake @@ -2,7 +2,7 @@ # CMake Find RapidJSON by Parra Studios # CMake script to find RapidJSON library. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindRust.cmake b/cmake/FindRust.cmake new file mode 100644 index 0000000000..c9ef316aa2 --- /dev/null +++ b/cmake/FindRust.cmake @@ -0,0 +1,212 @@ +# +# CMake Find Rust by Parra Studios +# CMake script to find Rust compiler and tools. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Rust executables and paths +# +# Rust_FOUND - True if rust was found +# Rust_CARGO_HOME - Cargo home folder +# Rust_CARGO_EXECUTABLE - Cargo package manager executable path +# Rust_RUSTC_EXECUTABLE - Rust compiler executable path +# Rust_RUSTC_VERSION - Rust compiler vesion string +# Rust_RUSTC_SYSROOT - Rust compiler root location (includes binaries and libraries) +# Rust_RUSTC_LIBRARIES - Rust compiler runtime library list +# Rust_RUSTDOC_EXECUTABLE - Rust doc executable plath +# Rust_RUSTUP_EXECUTABLE - Rustup executable path +# Rust_GDB_EXECUTABLE - Rust GDB debugger executable path +# Rust_LLDB_EXECUTABLE - Rust LLDB debugger executable path +# Rust_TOOLCHAIN_TRIPLET - Current triplet used in rust compiler + +# Options +# +# Rust_CMAKE_DEBUG - Print the debug information and all constants values + +# Example for fixing the toolchain +# +# find_package(Rust COMPONENTS nightly-2021-10-09) + +option(Rust_CMAKE_DEBUG "Show full output of the Rust related commands for debugging." OFF) + +if(WIN32) + set(USER_HOME "$ENV{USERPROFILE}") +else() + set(USER_HOME "$ENV{HOME}") +endif() + +if(NOT DEFINED Rust_CARGO_HOME) + if("$ENV{Rust_CARGO_HOME}" STREQUAL "") + set(Rust_CARGO_HOME "${USER_HOME}/.cargo") + else() + set(Rust_CARGO_HOME "$ENV{Rust_CARGO_HOME}") + endif() +endif() + +set(Rust_CARGO_HOME "${Rust_CARGO_HOME}" CACHE PATH "Rust Cargo Home") + +set(Rust_PATHS + /usr + /usr/local + ${Rust_CARGO_HOME} +) + +find_program(Rust_RUSTUP_EXECUTABLE rustup + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +if(Rust_RUSTUP_EXECUTABLE AND Rust_FIND_COMPONENTS) + # Install the required toolchain (only one allowed by now) + list(GET Rust_FIND_COMPONENTS 0 Rust_TOOLCHAIN) + + if(Rust_TOOLCHAIN) + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} toolchain install ${Rust_TOOLCHAIN} --force + OUTPUT_VARIABLE Rust_OUTPUT + ERROR_VARIABLE Rust_OUTPUT + RESULT_VARIABLE Rust_STATUS + ) + + if(Rust_CMAKE_DEBUG) + message(STATUS "${Rust_OUTPUT}") + else() + if(Rust_STATUS AND NOT Rust_STATUS EQUAL 0) + message(FATAL_ERROR "${Rust_OUTPUT}") + endif() + endif() + + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} default ${Rust_TOOLCHAIN} + OUTPUT_VARIABLE Rust_OUTPUT + ERROR_VARIABLE Rust_OUTPUT + RESULT_VARIABLE Rust_STATUS + ) + + if(Rust_CMAKE_DEBUG) + message(STATUS "${Rust_OUTPUT}") + else() + if(Rust_STATUS AND NOT Rust_STATUS EQUAL 0) + message(FATAL_ERROR "${Rust_OUTPUT}") + endif() + endif() + + foreach(Rust_TOOLCHAIN_COMPONENT ${Rust_TOOLCHAIN_COMPONENT_LIST}) + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} toolchain install ${Rust_TOOLCHAIN} --component ${Rust_TOOLCHAIN_COMPONENT} + OUTPUT_VARIABLE Rust_OUTPUT + ERROR_VARIABLE Rust_OUTPUT + RESULT_VARIABLE Rust_STATUS + ) + + if(Rust_CMAKE_DEBUG) + message(STATUS "${Rust_OUTPUT}") + else() + if(Rust_STATUS AND NOT Rust_STATUS EQUAL 0) + message(FATAL_ERROR "${Rust_OUTPUT}") + endif() + endif() + endforeach() + + # Obtain toolchain full name and triplet + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} default + OUTPUT_VARIABLE Rust_TOOLCHAIN_FULL_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + string(REPLACE " " ";" Rust_TOOLCHAIN_FULL_NAME ${Rust_TOOLCHAIN_FULL_NAME}) + list(GET Rust_TOOLCHAIN_FULL_NAME 0 Rust_TOOLCHAIN_FULL_NAME) + string(REPLACE "${Rust_TOOLCHAIN}-" "" Rust_TOOLCHAIN_TRIPLET ${Rust_TOOLCHAIN_FULL_NAME}) + endif() + +endif() + +find_program(Rust_CARGO_EXECUTABLE cargo + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_RUSTC_EXECUTABLE rustc + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_RUSTDOC_EXECUTABLE rustdoc + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_GDB_EXECUTABLE rust-gdb + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_LLDB_EXECUTABLE rust-lldb + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +if(Rust_RUSTC_EXECUTABLE) + execute_process( + COMMAND ${Rust_RUSTC_EXECUTABLE} --version + OUTPUT_VARIABLE Rust_RUSTC_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX REPLACE "rustc ([^ ]+) .*" "\\1" Rust_RUSTC_VERSION "${Rust_RUSTC_VERSION}") + + execute_process( + COMMAND ${Rust_RUSTC_EXECUTABLE} --print sysroot + OUTPUT_VARIABLE Rust_RUSTC_SYSROOT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + file( + GLOB Rust_RUSTC_LIBRARIES + ${Rust_RUSTC_SYSROOT}/lib/*${CMAKE_SHARED_LIBRARY_SUFFIX} + ) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Rust + FOUND_VAR Rust_FOUND + REQUIRED_VARS Rust_CARGO_EXECUTABLE Rust_RUSTC_EXECUTABLE Rust_RUSTC_LIBRARIES + VERSION_VAR Rust_RUSTC_VERSION +) + +mark_as_advanced( + Rust_FOUND + Rust_CARGO_EXECUTABLE + Rust_RUSTC_EXECUTABLE + Rust_RUSTC_SYSROOT + Rust_RUSTC_LIBRARIES + Rust_RUSTUP_EXECUTABLE + Rust_RUSTDOC_EXECUTABLE + Rust_GDB_EXECUTABLE + Rust_LLDB_EXECUTABLE +) + +if(Rust_CMAKE_DEBUG) + message(STATUS "Rust_CARGO_EXECUTABLE: ${Rust_CARGO_EXECUTABLE}") + message(STATUS "Rust_RUSTC_EXECUTABLE: ${Rust_RUSTC_EXECUTABLE}") + message(STATUS "Rust_RUSTC_SYSROOT: ${Rust_RUSTC_SYSROOT}") + message(STATUS "Rust_RUSTC_LIBRARIES: ${Rust_RUSTC_LIBRARIES}") + message(STATUS "Rust_RUSTUP_EXECUTABLE: ${Rust_RUSTUP_EXECUTABLE}") + message(STATUS "Rust_GDB_EXECUTABLE: ${Rust_GDB_EXECUTABLE}") + message(STATUS "Rust_LLDB_EXECUTABLE: ${Rust_LLDB_EXECUTABLE}") + message(STATUS "Rust_TOOLCHAIN_TRIPLET: ${Rust_TOOLCHAIN_TRIPLET}") +endif() diff --git a/cmake/FindSpiderMonkey.cmake b/cmake/FindSpiderMonkey.cmake index 361a47899e..a952c9e4e9 100644 --- a/cmake/FindSpiderMonkey.cmake +++ b/cmake/FindSpiderMonkey.cmake @@ -2,7 +2,7 @@ # CMake Find Mozilla SpiderMonkey JavaScript Engine by Parra Studios # CMake script to find Mozilla SpiderMonkey Javascript Engine. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindV8.cmake b/cmake/FindV8.cmake index 6796ecd9b8..aedb8a8d7d 100644 --- a/cmake/FindV8.cmake +++ b/cmake/FindV8.cmake @@ -2,7 +2,7 @@ # CMake Find V8 Google JavaScript Engine by Parra Studios # CMake script to find V8 JavaScript Engine. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindWasmtime.cmake b/cmake/FindWasmtime.cmake new file mode 100644 index 0000000000..bf086293a9 --- /dev/null +++ b/cmake/FindWasmtime.cmake @@ -0,0 +1,173 @@ +# +# CMake Find Wasmtime WebAssembly Runtime by Parra Studios +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# + +# Find Wasmtime library and include paths +# +# Wasmtime_LIBRARY - Wasmtime shared library +# Wasmtime_INCLUDE_DIR - Wasmtime include directory + +option(Wasmtime_CMAKE_DEBUG "Show full output of the Wasmtime related commands for debugging." OFF) + +include(Portability) + +set(Wasmtime_VERSION ${Wasmtime_FIND_VERSION}) + +# See https://docs.wasmtime.dev/contributing-building.html#building-the-wasmtime-c-api +if(PROJECT_OS_LINUX) + set(Wasmtime_LIBRARY_NAME libwasmtime.so) + set(Wasmtime_PLATFORM "linux") + set(Wasmtime_ARCHIVE_EXTENSION "tar.xz") +elseif(PROJECT_OS_FAMILY STREQUAL "macos") + set(Wasmtime_LIBRARY_NAME libwasmtime.dylib) + set(Wasmtime_PLATFORM "macos") + set(Wasmtime_ARCHIVE_EXTENSION "tar.xz") +elseif(PROJECT_OS_WIN) + set(Wasmtime_LIBRARY_NAME wasmtime.lib) + set(Wasmtime_PLATFORM "windows") + set(Wasmtime_ARCHIVE_EXTENSION "zip") +elseif(PROJECT_OS_MINGW) + set(Wasmtime_LIBRARY_NAME wasmtime.a) + set(Wasmtime_PLATFORM "mingw") + set(Wasmtime_ARCHIVE_EXTENSION "zip") +else() + message(FATAL_ERROR "Could not determine target platform or target platform is not supported") +endif() + +if(PROJECT_ARCH_AMD64) + set(Wasmtime_ARCH "x86_64") +elseif(PROJECT_ARCH_AARCH64) + set(Wasmtime_ARCH "aarch64") +else() + set(Wasmtime_ARCH "x86_64") + message(WARNING "Could not determine target architecture, assuming x86_64") +endif() + +message(DEBUG "Set target architecture to ${Wasmtime_ARCH}") + +set(Wasmtime_LOCAL_PATH "${CMAKE_BINARY_DIR}/wasmtime") +set(Wasmtime_DOWNLOAD_DIR_NAME "wasmtime-v${Wasmtime_VERSION}-${Wasmtime_ARCH}-${Wasmtime_PLATFORM}-c-api") +set(Wasmtime_API_PATH "${Wasmtime_LOCAL_PATH}/${Wasmtime_DOWNLOAD_DIR_NAME}") +set(Wasmtime_DOWNLOAD_LIBRARY_PATH "${Wasmtime_API_PATH}/lib") +set(Wasmtime_DOWNLOAD_INCLUDE_DIR "${Wasmtime_API_PATH}/include") + +if(NOT Wasmtime_LIBRARY) + set(Wasmtime_DEFAULT_LIBRARY_PATHS + /usr/lib + ) + find_library(Wasmtime_LIBRARY + NAMES ${Wasmtime_LIBRARY_NAME} + DOC "Wasmtime C API library" + ) + + if(Wasmtime_LIBRARY) + if(PROJECT_OS_WIN) + string(REGEX REPLACE "[.]lib$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + elseif(PROJECT_OS_MINGW) + string(REGEX REPLACE "[.]a$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + endif() + endif() +endif() + +if(NOT Wasmtime_INCLUDE_DIR) + set(Wasmtime_DEFAULT_INCLUDE_PATHS + /usr/include + ) + find_path(Wasmtime_INCLUDE_DIR + NAMES wasm.h wasmtime.h + DOC "Wasmtime C API headers" + ) +endif() + +if(NOT Wasmtime_LIBRARY OR NOT Wasmtime_INCLUDE_DIR) + message(STATUS "Wasmtime C API library or headers not found, downloading from archive") + + if(Wasmtime_ARCH STREQUAL "x86_64" AND PROJECT_ARCH_32BIT) + # We assumed target architecture was x86_64, but it is 32-bit, so the + # assumption is definitely invalid. + message(FATAL_ERROR "No downloads available for target architecture, please install Wasmtime manually") + endif() + + set(Wasmtime_DOWNLOAD_ARCHIVE_NAME "${Wasmtime_DOWNLOAD_DIR_NAME}.${Wasmtime_ARCHIVE_EXTENSION}") + set(Wasmtime_DOWNLOAD_URL + "https://github.com/bytecodealliance/wasmtime/releases/download/v$%7BWasmtime_VERSION%7D/$%7BWasmtime_DOWNLOAD_ARCHIVE_NAME%7D") + set(Wasmtime_DOWNLOAD_DEST "${Wasmtime_LOCAL_PATH}/${Wasmtime_DOWNLOAD_ARCHIVE_NAME}") + + file(DOWNLOAD ${Wasmtime_DOWNLOAD_URL} ${Wasmtime_DOWNLOAD_DEST} + STATUS DOWNLOAD_STATUS + SHOW_PROGRESS) + + list(GET DOWNLOAD_STATUS 0 STATUS_CODE) + + if(NOT STATUS_CODE EQUAL 0) + list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE) + message(FATAL_ERROR "Failed to download Wasmtime C API: ${ERROR_MESSAGE}") + endif() + + file(ARCHIVE_EXTRACT + INPUT ${Wasmtime_DOWNLOAD_DEST} + DESTINATION ${Wasmtime_LOCAL_PATH} + ) + + if(NOT Wasmtime_LIBRARY) + find_library(Wasmtime_LIBRARY + NAMES ${Wasmtime_LIBRARY_NAME} + PATHS ${Wasmtime_DOWNLOAD_LIBRARY_PATH} + NO_DEFAULT_PATH + DOC "Wasmtime C API library" + ) + + if(Wasmtime_LIBRARY) + set(Wasmtime_LIBRARY_INSTALLED ON) + + if(PROJECT_OS_WIN) + string(REGEX REPLACE "[.]lib$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + elseif(PROJECT_OS_MINGW) + string(REGEX REPLACE "[.]a$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + endif() + endif() + endif() + + if(NOT Wasmtime_INCLUDE_DIR) + find_path(Wasmtime_INCLUDE_DIR + NAMES wasm.h wasmtime.h + PATHS ${Wasmtime_DOWNLOAD_INCLUDE_DIR} + NO_DEFAULT_PATH + DOC "Wasmtime C API headers" + ) + endif() +endif() + +# Copy Wasmtime DLL to the output directory +if(Wasmtime_LIBRARY_DLL) + file(COPY "${Wasmtime_LIBRARY_DLL}" DESTINATION "${PROJECT_OUTPUT_DIR}") + set(Wasmtime_LIBRARY_DEPENDENCIES + ws2_32.lib + advapi32.lib + userenv.lib + ntdll.lib + shell32.lib + ole32.lib + bcrypt.lib + ) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Wasmtime + FOUND_VAR Wasmtime_FOUND + REQUIRED_VARS Wasmtime_LIBRARY Wasmtime_INCLUDE_DIR + VERSION_VAR Wasmtime_VERSION +) + +mark_as_advanced( + Wasmtime_FOUND + Wasmtime_LIBRARY + Wasmtime_INCLUDE_DIR +) + +if(Wasmtime_CMAKE_DEBUG) + message(STATUS "Found Wasmtime C API library at ${Wasmtime_LIBRARY}") + message(STATUS "Found Wasmtime C API header in ${Wasmtime_INCLUDE_DIR}") +endif() diff --git a/cmake/FindZig.cmake b/cmake/FindZig.cmake new file mode 100644 index 0000000000..cb2764032c --- /dev/null +++ b/cmake/FindZig.cmake @@ -0,0 +1,47 @@ +# +# CMake Find Zig by Parra Studios +# CMake script to find Zig compiler and tools. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Zig executables and paths +# +# Zig_FOUND - True if Zig was found +# Zig_COMPILER_EXECUTABLE - Zig compiler executable path + +# Options +# +# Zig_CMAKE_DEBUG - Print the debug information and all constants values + +option(Zig_CMAKE_DEBUG "Show full output of the Zig related commands for debugging." OFF) + +find_program(Zig_COMPILER_EXECUTABLE zig +) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Zig + FOUND_VAR Zig_FOUND +) + +mark_as_advanced( + Zig_FOUND + Zig_COMPILER_EXECUTABLE +) + +if(Zig_CMAKE_DEBUG) + message(STATUS "Zig_COMPILER_EXECUTABLE: ${Zig_COMPILER_EXECUTABLE}") +endif() diff --git a/cmake/InstallGBench.cmake b/cmake/InstallGBench.cmake index a5d51e649e..ccf3073d7a 100644 --- a/cmake/InstallGBench.cmake +++ b/cmake/InstallGBench.cmake @@ -2,7 +2,7 @@ # CMake Install Google Benchmark by Parra Studios # CMake script to install Google Benchmark library. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,15 +24,15 @@ if(NOT GBENCH_FOUND OR USE_BUNDLED_GBENCH) if(NOT GBENCH_VERSION OR USE_BUNDLED_GBENCH) - set(GBENCH_VERSION 1.4.1) - set(GBENCH_URL_MD5 482dddb22bec43f5507a000456d6bb88) + set(GBENCH_VERSION 1.6.1) + set(GBENCH_URL_MD5 8c33c51f9b7154e6c290df3750081c87) endif() ExternalProject_Add(google-bench-depends DOWNLOAD_NAME GBench-${GBENCH_VERSION}.tar.gz URL https://github.com/google/benchmark/archive/v${GBENCH_VERSION}.tar.gz URL_MD5 ${GBENCH_URL_MD5} - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_ENABLE_TESTING=OFF + CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX= -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_ENABLE_TESTING=OFF TEST_COMMAND "" ) diff --git a/cmake/InstallGTest.cmake b/cmake/InstallGTest.cmake index 14e45c29b5..45fdf569c9 100644 --- a/cmake/InstallGTest.cmake +++ b/cmake/InstallGTest.cmake @@ -2,7 +2,7 @@ # CMake Install Google Test by Parra Studios # CMake script to install Google Test library. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) if(NOT GTEST_VERSION OR USE_BUNDLED_GTEST) - set(GTEST_VERSION 1.8.1) + set(GTEST_VERSION 1.16.0) endif() find_package(Threads REQUIRED) @@ -35,11 +35,23 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) set(GTEST_DISABLE_PTHREADS OFF) endif() + if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + if(OPTION_BUILD_THREAD_SANITIZER) + set(SANITIZER_FLAGS -DCMAKE_CXX_FLAGS=/fsanitize=thread -DCMAKE_C_FLAGS=/fsanitize=thread) + endif() + if(OPTION_BUILD_ADDRESS_SANITIZER) + set(SANITIZER_FLAGS -DCMAKE_CXX_FLAGS=/fsanitize=address -DCMAKE_C_FLAGS=/fsanitize=address) + endif() + if(OPTION_BUILD_MEMORY_SANITIZER) + set(SANITIZER_FLAGS -DCMAKE_CXX_FLAGS="/fsanitize=memory /fsanitize=leak" -DCMAKE_C_FLAGS="/fsanitize=memory /fsanitize=leak") + endif() + endif() + # Import Google Test Framework ExternalProject_Add(google-test-depends GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-${GTEST_VERSION} - CMAKE_ARGS -Dgmock_build_tests=OFF + GIT_TAG v${GTEST_VERSION} + CMAKE_ARGS -Dgtest_build_samples=OFF -Dgtest_build_tests=OFF -Dgtest_disable_pthreads=${GTEST_DISABLE_PTHREADS} @@ -47,6 +59,8 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) -Dgtest_hide_internal_symbols=OFF -DINSTALL_GTEST=OFF -DBUILD_GMOCK=ON + -Dgmock_build_tests=OFF + ${SANITIZER_FLAGS} PREFIX "${CMAKE_CURRENT_BINARY_DIR}" UPDATE_COMMAND "" INSTALL_COMMAND "" @@ -62,19 +76,13 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) if(MSVC) set(GTEST_LIB_PREFIX "") set(GTEST_LIB_SUFFIX "lib") - set(GTEST_LIBS_DIR "${binary_dir}/googlemock/gtest/${CMAKE_BUILD_TYPE}") - set(GMOCK_LIBS_DIR "${binary_dir}/googlemock/${CMAKE_BUILD_TYPE}") - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(GTEST_LIB_DEBUG "d") - else() - set(GTEST_LIB_DEBUG "") - endif() + set(GTEST_LIBS_DIR "${binary_dir}/lib/${CMAKE_BUILD_TYPE}") + set(GMOCK_LIBS_DIR "${binary_dir}/lib/${CMAKE_BUILD_TYPE}") else() set(GTEST_LIB_PREFIX "lib") set(GTEST_LIB_SUFFIX "a") - set(GTEST_LIBS_DIR "${binary_dir}/googlemock/gtest") - set(GMOCK_LIBS_DIR "${binary_dir}/googlemock") - set(GTEST_LIB_DEBUG "") + set(GTEST_LIBS_DIR "${binary_dir}/lib") + set(GMOCK_LIBS_DIR "${binary_dir}/lib") endif() # Define Paths @@ -83,15 +91,30 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) "${GMOCK_INCLUDE_DIR}" ) + set(GTEST_LIBRARY + "${GTEST_LIBS_DIR}/${GTEST_LIB_PREFIX}gtest.${GTEST_LIB_SUFFIX}" + ) + + set(GMOCK_LIBRARY + "${GMOCK_LIBS_DIR}/${GTEST_LIB_PREFIX}gmock.${GTEST_LIB_SUFFIX}" + ) + set(GTEST_LIBRARIES - "${GTEST_LIBS_DIR}/${GTEST_LIB_PREFIX}gtest${GTEST_LIB_DEBUG}.${GTEST_LIB_SUFFIX}" - "${GMOCK_LIBS_DIR}/${GTEST_LIB_PREFIX}gmock${GTEST_LIB_DEBUG}.${GTEST_LIB_SUFFIX}" + "${GTEST_LIBRARY}" + "${GMOCK_LIBRARY}" "${CMAKE_THREAD_LIBS_INIT}" ) set(GTEST_FOUND TRUE) - mark_as_advanced(GTEST_INCLUDE_DIRS GTEST_LIBRARIES) + mark_as_advanced( + GTEST_LIBRARY + GMOCK_LIBRARY + GTEST_LIBRARIES + GTEST_INCLUDE_DIR + GMOCK_INCLUDE_DIR + GTEST_INCLUDE_DIRS + ) message(STATUS "Install Google Test v${GTEST_VERSION}") -endif () +endif() diff --git a/cmake/InstallLibTCC.cmake b/cmake/InstallLibTCC.cmake new file mode 100644 index 0000000000..23a780e264 --- /dev/null +++ b/cmake/InstallLibTCC.cmake @@ -0,0 +1,191 @@ +# +# CMake Install Tiny C Compiler by Parra Studios +# CMake script to install TCC library. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The following variables are set: +# +# LIBTCC_FOUND - True if TCC library was found +# LIBTCC_TARGET - Defines the TCC depends target (for using it as dependency on other targets) +# LIBTCC_INCLUDE_DIR - TCC headers path +# LIBTCC_LIBRARY - List of TCC libraries + +if(LIBTCC_FOUND) + return() +endif() + +include(Portability) + +set(LIBTCC_INSTALL_PREFIX "${PROJECT_OUTPUT_DIR}/libtcc") +file(MAKE_DIRECTORY ${LIBTCC_INSTALL_PREFIX}) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + set(LIBTCC_DEBUG "--debug") +else() + set(LIBTCC_DEBUG) +endif() + +# Configure +if(PROJECT_OS_FAMILY STREQUAL unix) + if(OPTION_BUILD_MUSL) + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG} --disable-static --config-musl) + else() + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG} --disable-static --with-selinux) + endif() +elseif(PROJECT_OS_FAMILY STREQUAL macos) + # TODO: --disable-static is not working on MacOS, this should be reported or further investigated + + # AddressSanitizer:DEADLYSIGNAL + # ================================================================= + # ==5339==ERROR: AddressSanitizer: BUS on unknown address 0x7fffac377b10 (pc 0x7fffac377b10 bp 0x7ffee8c2a0a0 sp 0x7ffee8c29f98 T0) + # #0 0x7fffac377b0f in off32 (libsystem_c.dylib:x86_64+0x3647db0f) + # #1 0x10bab502d in parse_btype tccgen.c + # #2 0x10babcd02 in decl0 tccgen.c:8197 + # #3 0x10baa74d8 in tccgen_compile tccgen.c:8449 + # #4 0x10ba90c48 in tcc_compile libtcc.c:742 + # #5 0x10ba9186a in tcc_add_file_internal libtcc.c:1101 + # #6 0x10b9e8db1 in c_loader_impl_load_from_file c_loader_impl.cpp:933 + # #7 0x1071b8fc5 in loader_impl_load_from_file loader_impl.c:842 + # #8 0x1071b302d in loader_load_from_file loader.c:317 + # #9 0x1071c9488 in metacall_load_from_file metacall.c:348 + # #10 0x106fd7ce1 in metacall_c_test_DefaultConstructor_Test::TestBody() metacall_c_test.cpp:53 + # #11 0x10704125d in void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-c-testd:x86_64+0x10006d25d) + # #12 0x107002e9a in void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-c-testd:x86_64+0x10002ee9a) + # #13 0x107002dd2 in testing::Test::Run() (metacall-c-testd:x86_64+0x10002edd2) + # #14 0x107004011 in testing::TestInfo::Run() (metacall-c-testd:x86_64+0x100030011) + # #15 0x1070051ff in testing::TestSuite::Run() (metacall-c-testd:x86_64+0x1000311ff) + # #16 0x107014ce5 in testing::internal::UnitTestImpl::RunAllTests() (metacall-c-testd:x86_64+0x100040ce5) + # #17 0x10704570d in bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (metacall-c-testd:x86_64+0x10007170d) + # #18 0x10701465a in bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (metacall-c-testd:x86_64+0x10004065a) + # #19 0x10701452f in testing::UnitTest::Run() (metacall-c-testd:x86_64+0x10004052f) + # #20 0x106fd6049 in RUN_ALL_TESTS() gtest.h:2490 + # #21 0x106fd5ee3 in main main.cpp:27 + # #22 0x7fff75eb03d4 in start (libdyld.dylib:x86_64+0x163d4) + + # ==5339==Register values: + # rax = 0x000000000000002c rbx = 0x0000000000000001 rcx = 0x00006250000062a0 rdx = 0x0000000000000000 + # rdi = 0x000000000000002c rsi = 0x00007ffee8c2a028 rbp = 0x00007ffee8c2a0a0 rsp = 0x00007ffee8c29f98 + # r8 = 0x000000010bb0f350 r9 = 0x0000000111c5457c r10 = 0x0000000000000000 r11 = 0x00007fffac377b10 + # r12 = 0x0000000000000000 r13 = 0x00007ffee8c29fe0 r14 = 0x000000010bb13c50 r15 = 0x000000000000053b + # AddressSanitizer can not provide additional info. + # SUMMARY: AddressSanitizer: BUS (libsystem_c.dylib:x86_64+0x3647db0f) in off32 + + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG}) # --disable-static +elseif(PROJECT_OS_FAMILY STREQUAL win32) + if(PROJECT_OS_NAME STREQUAL MinGW) + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG} --config-mingw32 --disable-static) + else() + set(LIBTCC_CONFIGURE "") + endif() +else() + message(FATAL_ERROR "TCC library install support not implemented in this platform") +endif() + +include(ProcessorCount) +ProcessorCount(N) + +# Build +if(PROJECT_OS_BSD) + set(LIBTCC_BUILD gmake -j${N}) +elseif(PROJECT_OS_FAMILY STREQUAL unix) + set(LIBTCC_BUILD make -j${N}) +elseif(PROJECT_OS_FAMILY STREQUAL macos) + set(LIBTCC_BUILD make -j${N} MACOSX_DEPLOYMENT_TARGET=${PROJECT_OS_VERSION}) +elseif(PROJECT_OS_FAMILY STREQUAL win32) + if(PROJECT_OS_NAME STREQUAL MinGW) + set(LIBTCC_BUILD make -j${N}) + else() + set(LIBTCC_BUILD ./win32/build-tcc.bat -i ${LIBTCC_INSTALL_PREFIX}) + endif() +else() + message(FATAL_ERROR "TCC library install support not implemented in this platform") +endif() + +# Install +if(PROJECT_OS_BSD) + set(LIBTCC_INSTALL gmake install) +elseif(PROJECT_OS_FAMILY STREQUAL win32 AND PROJECT_OS_NAME STREQUAL Windows) + set(LIBTCC_INSTALL "") +else() + set(LIBTCC_INSTALL make install) +endif() + +set(LIBTCC_TARGET libtcc-depends) +set(LIBTCC_COMMIT_SHA "6ec4a10") +if(PROJECT_OS_FAMILY STREQUAL macos) + # TODO: --disable-static is not working on MacOS, this should be reported or further investigated, remove this when it is solved + set(LIBTTC_LIBRARY_NAME "${CMAKE_STATIC_LIBRARY_PREFIX}tcc${CMAKE_STATIC_LIBRARY_SUFFIX}") +else() + set(LIBTTC_LIBRARY_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}tcc${CMAKE_SHARED_LIBRARY_SUFFIX}") +endif() +set(LIBTTC_LIBRARY_PATH "${PROJECT_OUTPUT_DIR}/${LIBTTC_LIBRARY_NAME}") +set(LIBTTC_RUNTIME_PATH "${LIBTCC_INSTALL_PREFIX}/lib/tcc") +set(LIBTTC_RUNTIME_INCLUDE_PATH "${LIBTTC_RUNTIME_PATH}/include") +set(LIBTTC_RUNTIME_FILES + "${LIBTTC_RUNTIME_PATH}/libtcc1.a" + "${LIBTTC_RUNTIME_PATH}/bcheck.o" + "${LIBTTC_RUNTIME_PATH}/bt-exe.o" + "${LIBTTC_RUNTIME_PATH}/bt-log.o" +) + +# LibTCC Proejct +ExternalProject_Add(${LIBTCC_TARGET} + DOWNLOAD_NAME tinycc.tar.gz + URL https://github.com/metacall/tinycc/archive/${LIBTCC_COMMIT_SHA}.tar.gz + URL_MD5 1d25d1a07a39c6d6671b7221d5286dc1 + CONFIGURE_COMMAND ${LIBTCC_CONFIGURE} + BUILD_COMMAND ${LIBTCC_BUILD} + BUILD_IN_SOURCE true + TEST_COMMAND "" + UPDATE_COMMAND "" + INSTALL_COMMAND ${LIBTCC_INSTALL} + COMMAND ${CMAKE_COMMAND} -E copy "${LIBTCC_INSTALL_PREFIX}/lib/${LIBTTC_LIBRARY_NAME}" "${LIBTTC_LIBRARY_PATH}" + COMMAND ${CMAKE_COMMAND} -E copy ${LIBTTC_RUNTIME_FILES} "${PROJECT_OUTPUT_DIR}" +) + +# Install Library +install(FILES + ${LIBTTC_LIBRARY_PATH} + DESTINATION ${INSTALL_LIB} + COMPONENT runtime +) + +# Runtime files +install(DIRECTORY + ${LIBTTC_RUNTIME_PATH}/ + DESTINATION ${INSTALL_LIB} + COMPONENT runtime + FILES_MATCHING + PATTERN "*.a" + PATTERN "*.o" + PATTERN "include" EXCLUDE +) + +# Header files +install(DIRECTORY + ${LIBTTC_RUNTIME_INCLUDE_PATH}/ + DESTINATION ${INSTALL_INCLUDE} + COMPONENT runtime +) + +set(LIBTCC_INCLUDE_DIR "${LIBTCC_INSTALL_PREFIX}/include") +set(LIBTCC_LIBRARY "${LIBTTC_LIBRARY_PATH}") +set(LIBTCC_FOUND TRUE) + +mark_as_advanced(LIBTCC_INCLUDE_DIR LIBTCC_LIBRARY) + +message(STATUS "Installing LibTCC ${LIBTCC_COMMIT_SHA}") diff --git a/cmake/InstallPatchelf.cmake b/cmake/InstallPatchelf.cmake new file mode 100644 index 0000000000..72b44a952d --- /dev/null +++ b/cmake/InstallPatchelf.cmake @@ -0,0 +1,57 @@ +# +# CMake Find Patchelf by Parra Studios +# CMake script to find Patchelf executable. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if(Patchelf_FOUND) + return() +endif() + +if(WIN32 OR APPLE) + # TODO: Download binaries (https://github.com/NixOS/patchelf/releases/tag/0.18.0) + message(WARNING "Patchelf not supported in MacOs or Windows") +endif() + +set(Patchelf_PREFIX_DIR "${CMAKE_BINARY_DIR}/Patchelf") +set(Patchelf_SOURCE_DIR "${Patchelf_PREFIX_DIR}/src/patchelf") +set(Patchelf_INSTALL_DIR "${Patchelf_PREFIX_DIR}/install/patchelf") +set(Patchelf_STAMP_DIR "${Patchelf_PREFIX_DIR}/stamp/patchelf") +set(Patchelf_TMP_DIR "${Patchelf_PREFIX_DIR}/tmp/patchelf") + +include(ExternalProject) + +ExternalProject_Add(Patchelf + PREFIX "${Patchelf_PREFIX_DIR}" + GIT_REPOSITORY "https://github.com/NixOS/patchelf" + GIT_TAG "0.18.0" + PATCH_COMMAND "" + SOURCE_DIR "${Patchelf_SOURCE_DIR}" + BINARY_DIR "${Patchelf_SOURCE_DIR}" + INSTALL_DIR "${Patchelf_INSTALL_DIR}" + STAMP_DIR "${Patchelf_STAMP_DIR}" + TMP_DIR "${Patchelf_TMP_DIR}" + CONFIGURE_COMMAND ${Patchelf_SOURCE_DIR}/bootstrap.sh + BUILD_COMMAND ${Patchelf_SOURCE_DIR}/configure + INSTALL_COMMAND make + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 + LOG_INSTALL 1 +) + +set(Patchelf_EXECUTABLE "${CMAKE_BINARY_DIR}/Patchelf/src/patchelf/src/patchelf") +set(Patchelf_FOUND TRUE) diff --git a/cmake/InstallRapidJSON.cmake b/cmake/InstallRapidJSON.cmake index 1e3336975c..f90dfc4a4e 100644 --- a/cmake/InstallRapidJSON.cmake +++ b/cmake/InstallRapidJSON.cmake @@ -2,7 +2,7 @@ # CMake Install RapidJSON by Parra Studios # CMake script to install RapidJSON library. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,29 +24,25 @@ if(NOT RAPIDJSON_FOUND OR USE_BUNDLED_RAPIDJSON) if(NOT RAPIDJSON_VERSION OR USE_BUNDLED_RAPIDJSON) - set(RAPIDJSON_VERSION 1.1.0) - set(RAPIDJSON_URL_MD5 badd12c511e081fec6c89c43a7027bce) + set(RAPIDJSON_VERSION 24b5e7a8b27f42fa16b96fc70aade9106cf7102f) endif() ExternalProject_Add(rapid-json-depends - DOWNLOAD_NAME RapidJSON-${RAPIDJSON_VERSION}.tar.gz - URL https://github.com/miloyip/rapidjson/archive/v${RAPIDJSON_VERSION}.tar.gz - URL_MD5 ${RAPIDJSON_URL_MD5} - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX= - -DRAPIDJSON_BUILD_DOC=Off - -DRAPIDJSON_BUILD_EXAMPLES=Off - -DRAPIDJSON_BUILD_TESTS=Off - TEST_COMMAND "" + GIT_REPOSITORY "https://github.com/Tencent/rapidjson.git" + GIT_TAG "${RAPIDJSON_VERSION}" + BUILD_COMMAND "" + CONFIGURE_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" ) - ExternalProject_Get_Property(rapid-json-depends INSTALL_DIR) + ExternalProject_Get_Property(rapid-json-depends SOURCE_DIR) - set(RAPIDJSON_ROOT_DIR ${INSTALL_DIR}) + set(RAPIDJSON_ROOT_DIR ${SOURCE_DIR}) set(RAPIDJSON_INCLUDE_DIRS ${RAPIDJSON_ROOT_DIR}/include) set(RAPIDJSON_FOUND TRUE) mark_as_advanced(RAPIDJSON_INCLUDE_DIRS) message(STATUS "Installing RapidJSON v${RAPIDJSON_VERSION}") -endif () +endif() diff --git a/cmake/NodeJSGYPPatch.py b/cmake/NodeJSGYPPatch.py new file mode 100644 index 0000000000..ec82a50edb --- /dev/null +++ b/cmake/NodeJSGYPPatch.py @@ -0,0 +1,54 @@ +from sys import argv +from os.path import isfile +from shutil import copy2 + +# Validate arguments +if len(argv) != 2: + print('Invalid number of arguments, you should pass the location of \'node.gyp\'.') + print('Usage: python3 NodeJSGYPPatch.py ') + exit(1) + +# Validate the project file +if not isfile(argv[1]) or not argv[1].endswith('node.gyp'): + print('The file \'' + argv[1] + '\' does not exist or is not a valid gyp file, it must be named as \'node.gyp\'.') + exit(2) + +# Read the file +f = open(argv[1]) +nodegyp = eval(f.read()) +f.close() + +# Validate that the target is present and it is correct +target = next((x for x in nodegyp['targets'] if 'target_name' in x and x['target_name'] == '<(node_lib_target_name)'), None) + +if target is None: + print('Invalid node.gyp configuration, the target \'node_lib_target_name\' is not present.') + exit(3) + +condition = next((x for x in target['conditions'] if x[0] == 'OS=="win"'), None) + +if condition is None or (condition is not None and len(condition) != 2): + print('Invalid node.gyp configuration, the condition \'OS=="win"\' is not present in target \'node_lib_target_name\'.') + exit(4) + +if not 'libraries' in condition[1]: + print('Invalid node.gyp configuration, \'libraries\' field is not present in the condition \'OS=="win"\' of the target target \'node_lib_target_name\'.') + exit(5) + +# Get the libraries +libraries = condition[1]['libraries'] + +# Check if the library to patch is present +if not 'Winmm' in libraries: + # Copy file as backup + copy2(argv[1], argv[1] + '.backup') + + # Apply the patch to the libraries + libraries.append('Winmm') + + # Overwrite the node.gyp project + f = open(argv[1], 'w') + f.write(repr(nodegyp)) + f.close() + +print('Build project node.gyp patched correctly') diff --git a/cmake/Portability.cmake b/cmake/Portability.cmake index ee4caa57c4..556427b187 100644 --- a/cmake/Portability.cmake +++ b/cmake/Portability.cmake @@ -2,7 +2,7 @@ # Portability CMake support by Parra Studios # Cross-platform and architecture detection utility. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,6 +54,36 @@ if(PROJECT_OS_SOLARIS) set(PROJECT_OS_FAMILY unix) endif() +# Check Android +if(ANDROID) + set(PROJECT_OS_ANDROID TRUE BOOL INTERNAL) + set(PROJECT_OS_NAME "Android") + set(PROJECT_OS_FAMILY unix) +endif() + +# Check Haiku +string(REGEX MATCH "Haiku" PROJECT_OS_HAIKU ${CMAKE_SYSTEM_NAME}) + +if(PROJECT_OS_HAIKU) + set(HAIKU 1) + set(PROJECT_OS_HAIKU TRUE BOOL INTERNAL) + set(PROJECT_OS_NAME "Haiku") + set(PROJECT_OS_FAMILY beos) + add_compile_definitions(__HAIKU__) + + # Workaround to enable Haiku with export headers + # This can be removed once export headers support Haiku + if(PROJECT_OS_HAIKU) + # As the function is already patched, repatch it again + function(_GENERATE_EXPORT_HEADER) + set(WIN32 1) + # When the function is redefined, the old function can be accessed through underscore + __GENERATE_EXPORT_HEADER(${ARGN}) + unset(WIN32) + endfunction() + endif() +endif() + # Check Windows if(WIN32) set(PROJECT_OS_WIN TRUE BOOL INTERNAL) @@ -61,6 +91,13 @@ if(WIN32) set(PROJECT_OS_FAMILY win32) endif() +# Check MinGW +if(MINGW) + set(PROJECT_OS_MINGW TRUE BOOL INTERNAL) + set(PROJECT_OS_NAME "MinGW") + set(PROJECT_OS_FAMILY win32) +endif() + # Check Apple OS if(APPLE) # Check if it's OS X or another MacOS @@ -76,8 +113,12 @@ if(APPLE) set(PROJECT_OS_FAMILY macos) - exec_program(uname ARGS -v OUTPUT_VARIABLE PROJECT_OS_VERSION) - string(REGEX MATCH "[0-9]+" PROJECT_OS_VERSION ${PROJECT_OS_VERSION}) + # Retrieve MacOs version from sv_vers + exec_program(sw_vers OUTPUT_VARIABLE SW_VERS_OUTPUT) + string(REPLACE "\n" ";" SW_VERS_OUTPUT "${SW_VERS_OUTPUT}") + string(REPLACE ":" ";" SW_VERS_OUTPUT "${SW_VERS_OUTPUT}") + list(GET SW_VERS_OUTPUT 3 PROJECT_OS_VERSION) + string(STRIP "${PROJECT_OS_VERSION}" PROJECT_OS_VERSION) endif() # Check QNX @@ -105,18 +146,22 @@ set(PROJECT_ARCH_NAME ${CMAKE_SYSTEM_PROCESSOR}) # 32 or 64 bit Linux if(PROJECT_OS_LINUX) - if(${PROJECT_ARCH_NAME} STREQUAL "x86") + if (${PROJECT_ARCH_NAME} MATCHES "^(x86|i[3-6]86|x86_64)$" AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") set(PROJECT_ARCH_32BIT TRUE BOOL INTERNAL) - endif() - - if(${PROJECT_ARCH_NAME} STREQUAL "x86_64") + set(PROJECT_ARCH_X86 TRUE BOOL INTERNAL) + elseif(${PROJECT_ARCH_NAME} MATCHES "(x86_64)|(amd64)|(AMD64)") set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_AMD64 TRUE BOOL INTERNAL) + elseif(${PROJECT_ARCH_NAME} STREQUAL "aarch64") + set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_AARCH64 TRUE BOOL INTERNAL) elseif(${PROJECT_ARCH_NAME} STREQUAL "ppc64") set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) elseif(${PROJECT_ARCH_NAME} STREQUAL "s390x") - set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) endif() + # Verify the architecture is correct if(PROJECT_ARCH_32BIT) if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") message(STATUS "Linux ${PROJECT_ARCH_NAME} 32bit detected") @@ -138,7 +183,7 @@ endif() if(NOT PROJECT_ARCH_32BIT AND NOT PROJECT_ARCH_64BIT) if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") message(STATUS "32bit architecture ${PROJECT_ARCH_NAME} detected") - set(PROJECT_ARCH_32BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_32BIT TRUE BOOL INTERNAL) if(PROJECT_OS_WIN) set(WINXBITS Win32) @@ -146,7 +191,7 @@ if(NOT PROJECT_ARCH_32BIT AND NOT PROJECT_ARCH_64BIT) elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") message(STATUS "64bit architecture ${PROJECT_ARCH_NAME} detected") - set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) if(PROJECT_OS_WIN) set(WINXBITS Win64) @@ -157,15 +202,35 @@ if(NOT PROJECT_ARCH_32BIT AND NOT PROJECT_ARCH_64BIT) endif() endif() -# Set the library directory suffix accordingly -#if(PROJECT_PROC_64BIT) -# # Set the install path to lib64 -# set(PROJECT_LIB_DIR "lib64") -# set(PROJECT_PLUGIN_DIR "lib64/${PROJECT_NAME}-${META_VERSION}") -#else(PROJECT_PROC_64BIT) -# set(PROJECT_LIB_DIR "lib") -# set(PROJECT_PLUGIN_DIR "lib/${PROJECT_NAME}-${META_VERSION}") -#endif() - -#message(STATUS "Installing Libraries to ${CMAKE_INSTALL_PREFIX}/${PROJECT_LIB_DIR}") -#message(STATUS "Installing Plugins to ${CMAKE_INSTALL_PREFIX}/${PROJECT_PLUGIN_DIR}") +# +# Define the library path environment variable name +# + +if(PROJECT_OS_FAMILY STREQUAL "unix") + set(PROJECT_LIBRARY_PATH_NAME "LD_LIBRARY_PATH") +elseif(PROJECT_OS_HAIKU) + set(PROJECT_LIBRARY_PATH_NAME "LIBRARY_PATH") +elseif(PROJECT_OS_WIN OR PROJECT_OS_MINGW) + set(PROJECT_LIBRARY_PATH_NAME "PATH") +elseif(PROJECT_OS_FAMILY STREQUAL "macos") + set(PROJECT_LIBRARY_PATH_NAME "DYLD_LIBRARY_PATH") +else() + message(FATAL_ERROR "Unsupported Platform for PROJECT_LIBRARY_PATH_NAME in Portability") +endif() + +# +# Define a macro for getting the library path with previous contents from environment variable +# + +macro(PROJECT_LIBRARY_PATH variable path) + set(LIBRARY_PATH_ENV_VAR "$ENV{${PROJECT_LIBRARY_PATH_NAME}}") + if("${LIBRARY_PATH_ENV_VAR}" STREQUAL "") + set(${variable} "${path}") + else() + if(PROJECT_OS_WIN OR PROJECT_OS_MINGW) + set(${variable} "${path};${LIBRARY_PATH_ENV_VAR}") + else() + set(${variable} "${path}:${LIBRARY_PATH_ENV_VAR}") + endif() + endif() +endmacro() diff --git a/cmake/ScriptProject.cmake b/cmake/ScriptProject.cmake index 9dca472fa8..fb7d138355 100644 --- a/cmake/ScriptProject.cmake +++ b/cmake/ScriptProject.cmake @@ -2,7 +2,7 @@ # Script project generator by Parra Studios # Generates a script project embedded into CMake. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/SecurityFlags.cmake b/cmake/SecurityFlags.cmake index 963ea8b515..b31ccbbf7d 100644 --- a/cmake/SecurityFlags.cmake +++ b/cmake/SecurityFlags.cmake @@ -2,7 +2,7 @@ # Compiler and linker options for hardening flags by Parra Studios # Enables hardening security flags if available. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/TestEnvironmentVariables.cmake b/cmake/TestEnvironmentVariables.cmake index b11cbcf8d1..1255f2bb46 100644 --- a/cmake/TestEnvironmentVariables.cmake +++ b/cmake/TestEnvironmentVariables.cmake @@ -2,7 +2,7 @@ # Test Environment Variables by Parra Studios # Utility for defining cross-platform environment variables in tests. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/Warnings.cmake b/cmake/Warnings.cmake index e77930f4a6..23f057afd2 100644 --- a/cmake/Warnings.cmake +++ b/cmake/Warnings.cmake @@ -2,7 +2,7 @@ # Cross-compiler warning utility by Parra Studios # Utility to enable cross-compiler warnings. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ if(WARNINGS_ENABLED) # Define C compiler warning flags if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + # TODO: Uncomment the rest of the warnings, enable Weverything for clang add_compile_options(-Wall) add_compile_options(-Wextra) add_compile_options(-Wunused) @@ -82,12 +83,15 @@ if(WARNINGS_ENABLED) set(WARNINGS_C_AVAILABLE 1) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4 /Wall /WX") + string(REPLACE "/W1" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REPLACE "/W2" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REPLACE "/W3" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4") # /Wall set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CTR_NONSTDC_NO_WARNINGS=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CTR_SECURE_NO_WARNINGS=1") set(WARNINGS_C_AVAILABLE 1) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W5 /Wall /Wcheck /Werror-all /WX") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W5 /Wall /Wcheck /Werror-all") set(WARNINGS_C_AVAILABLE 1) else() set(STATUS "Unknown C compiler warning level support") @@ -99,14 +103,17 @@ if(WARNINGS_ENABLED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(WARNINGS_CXX_AVAILABLE 1) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /Wall /WX") + string(REPLACE "/W1" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "/W2" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") # /Wall set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_NONSTDC_NO_WARNINGS=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_SECURE_NO_WARNINGS=1") set(WARNINGS_CXX_AVAILABLE 1) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W5 /Wall /Wcheck /Werror-all /WX") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W5 /Wall /Wcheck /Werror-all") set(WARNINGS_CXX_AVAILABLE 1) else() set(STATUS "Unknown C++ compiler warning level support") diff --git a/cmake/coverage/FindGcov.cmake b/cmake/coverage/FindGcov.cmake deleted file mode 100644 index 157fc88c6d..0000000000 --- a/cmake/coverage/FindGcov.cmake +++ /dev/null @@ -1,149 +0,0 @@ -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - - -# include required Modules -include(FindPackageHandleStandardArgs) - -# Search for gcov binary. -set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) -set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) - -get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) -foreach (LANG ${ENABLED_LANGUAGES}) - # Gcov evaluation is dependent on the used compiler. Check gcov support for - # each compiler that is used. If gcov binary was already found for this - # compiler, do not try to find it again. - if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN) - get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH) - - if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU") - # Some distributions like OSX (homebrew) ship gcov with the compiler - # version appended as gcov-x. To find this binary we'll build the - # suggested binary name with the compiler version. - string(REGEX MATCH "^[0-9]+" GCC_VERSION - "${CMAKE_${LANG}_COMPILER_VERSION}") - - find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov - HINTS ${COMPILER_PATH}) - - elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang") - # Some distributions like Debian ship llvm-cov with the compiler - # version appended as llvm-cov-x.y. To find this binary we'll build - # the suggested binary name with the compiler version. - string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION - "${CMAKE_${LANG}_COMPILER_VERSION}") - - # llvm-cov prior version 3.5 seems to be not working with coverage - # evaluation tools, but these versions are compatible with the gcc - # gcov tool. - if(LLVM_VERSION VERSION_GREATER 3.4) - find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}" - "llvm-cov" HINTS ${COMPILER_PATH}) - mark_as_advanced(LLVM_COV_BIN) - - if (LLVM_COV_BIN) - find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS - ${CMAKE_MODULE_PATH}) - if (LLVM_COV_WRAPPER) - set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "") - - # set additional parameters - set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV - "LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING - "Environment variables for llvm-cov-wrapper.") - mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV) - endif () - endif () - endif () - - if (NOT GCOV_BIN) - # Fall back to gcov binary if llvm-cov was not found or is - # incompatible. This is the default on OSX, but may crash on - # recent Linux versions. - find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH}) - endif () - endif () - - - if (GCOV_BIN) - set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING - "${LANG} gcov binary.") - - if (NOT CMAKE_REQUIRED_QUIET) - message("-- Found gcov evaluation for " - "${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}") - endif() - - unset(GCOV_BIN CACHE) - endif () - endif () -endforeach () - -# Add a new global target for all gcov targets. This target could be used to -# generate the gcov files for the whole project instead of calling -gcov -# for each target. -if (NOT TARGET gcov) - add_custom_target(gcov) -endif (NOT TARGET gcov) - -# This function will add gcov evaluation for target . Only sources of -# this target will be evaluated and no dependencies will be added. It will call -# Gcov on any source file of once and store the gcov file in the same -# directory. -function (add_gcov_target TNAME) - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) - - # We don't have to check, if the target has support for coverage, thus this - # will be checked by add_coverage_target in Findcoverage.cmake. Instead we - # have to determine which gcov binary to use. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(SOURCES "") - set(TCOMPILER "") - foreach (FILE ${TSOURCES}) - codecov_path_of_source(${FILE} FILE) - if (NOT "${FILE}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (NOT "${LANG}" STREQUAL "") - list(APPEND SOURCES "${FILE}") - set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - # If no gcov binary was found, coverage data can't be evaluated. - if (NOT GCOV_${TCOMPILER}_BIN) - message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") - return() - endif () - - set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") - set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") - - set(BUFFER "") - foreach(FILE ${SOURCES}) - get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH) - - # call gcov - add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov - COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null - DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno - WORKING_DIRECTORY ${FILE_PATH} - ) - - list(APPEND BUFFER ${TDIR}/${FILE}.gcov) - endforeach() - - # add target for gcov evaluation of - add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER}) - - # add evaluation target to the global gcov target. - add_dependencies(gcov ${TNAME}-gcov) -endfunction (add_gcov_target) diff --git a/cmake/coverage/FindLcov.cmake b/cmake/coverage/FindLcov.cmake deleted file mode 100644 index beb925ae06..0000000000 --- a/cmake/coverage/FindLcov.cmake +++ /dev/null @@ -1,354 +0,0 @@ -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - - -# configuration -set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data") -set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init") -set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture") -set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html") - - - - -# Search for Gcov which is used by Lcov. -find_package(Gcov) - - - - -# This function will add lcov evaluation for target . Only sources of -# this target will be evaluated and no dependencies will be added. It will call -# geninfo on any source file of once and store the info file in the same -# directory. -# -# Note: This function is only a wrapper to define this function always, even if -# coverage is not supported by the compiler or disabled. This function must -# be defined here, because the module will be exited, if there is no coverage -# support by the compiler or it is disabled by the user. -function (add_lcov_target TNAME) - if (LCOV_FOUND) - # capture initial coverage data - lcov_capture_initial_tgt(${TNAME}) - - # capture coverage data after execution - lcov_capture_tgt(${TNAME}) - endif () -endfunction (add_lcov_target) - - - - -# include required Modules -include(FindPackageHandleStandardArgs) - -# Search for required lcov binaries. -find_program(LCOV_BIN lcov) -find_program(GENINFO_BIN geninfo) -find_program(GENHTML_BIN genhtml) -find_package_handle_standard_args(lcov - REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN -) - -# enable genhtml C++ demangeling, if c++filt is found. -set(GENHTML_CPPFILT_FLAG "") -find_program(CPPFILT_BIN c++filt) -if (NOT CPPFILT_BIN STREQUAL "") - set(GENHTML_CPPFILT_FLAG "--demangle-cpp") -endif (NOT CPPFILT_BIN STREQUAL "") - -# enable no-external flag for lcov, if available. -if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG) - set(FLAG "") - execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP) - string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}") - if (GENINFO_RES) - set(FLAG "--no-external") - endif () - - set(GENINFO_EXTERN_FLAG "${FLAG}" - CACHE STRING "Geninfo flag to exclude system sources.") -endif () - -# If Lcov was not found, exit module now. -if (NOT LCOV_FOUND) - return() -endif (NOT LCOV_FOUND) - - - - -# Create directories to be used. -file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT}) -file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE}) - -set(LCOV_REMOVE_PATTERNS "") - -# This function will merge lcov files to a single target file. Additional lcov -# flags may be set with setting LCOV_EXTRA_FLAGS before calling this function. -function (lcov_merge_files OUTFILE ...) - # Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files. - list(REMOVE_AT ARGV 0) - - # Generate merged file. - string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}") - add_custom_command(OUTPUT "${OUTFILE}.raw" - COMMAND cat ${ARGV} > ${OUTFILE}.raw - DEPENDS ${ARGV} - COMMENT "Generating ${FILE_REL}" - ) - - add_custom_command(OUTPUT "${OUTFILE}" - COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE} - --base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS} - COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS} - --output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS} - DEPENDS ${OUTFILE}.raw - COMMENT "Post-processing ${FILE_REL}" - ) -endfunction () - - - - -# Add a new global target to generate initial coverage reports for all targets. -# This target will be used to generate the global initial info file, which is -# used to gather even empty report data. -if (NOT TARGET lcov-capture-init) - add_custom_target(lcov-capture-init) - set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "") -endif (NOT TARGET lcov-capture-init) - - -# This function will add initial capture of coverage data for target , -# which is needed to get also data for objects, which were not loaded at -# execution time. It will call geninfo for every source file of once and -# store the info file in the same directory. -function (lcov_capture_initial_tgt TNAME) - # We don't have to check, if the target has support for coverage, thus this - # will be checked by add_coverage_target in Findcoverage.cmake. Instead we - # have to determine which gcov binary to use. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(SOURCES "") - set(TCOMPILER "") - foreach (FILE ${TSOURCES}) - codecov_path_of_source(${FILE} FILE) - if (NOT "${FILE}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (NOT "${LANG}" STREQUAL "") - list(APPEND SOURCES "${FILE}") - set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - # If no gcov binary was found, coverage data can't be evaluated. - if (NOT GCOV_${TCOMPILER}_BIN) - message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") - return() - endif () - - set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") - set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") - - - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) - set(GENINFO_FILES "") - foreach(FILE ${SOURCES}) - # generate empty coverage files - set(OUTFILE "${TDIR}/${FILE}.info.init") - list(APPEND GENINFO_FILES ${OUTFILE}) - - add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN} - --quiet --base-directory ${PROJECT_SOURCE_DIR} --initial - --gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE} - ${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno - DEPENDS ${TNAME} - COMMENT "Capturing initial coverage data for ${FILE}" - ) - endforeach() - - # Concatenate all files generated by geninfo to a single file per target. - set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info") - set(LCOV_EXTRA_FLAGS "--initial") - lcov_merge_files("${OUTFILE}" ${GENINFO_FILES}) - add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE}) - - # add geninfo file generation to global lcov-geninfo target - add_dependencies(lcov-capture-init ${TNAME}-capture-init) - set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}" - "${OUTFILE}" CACHE INTERNAL "" - ) -endfunction (lcov_capture_initial_tgt) - - -# This function will generate the global info file for all targets. It has to be -# called after all other CMake functions in the root CMakeLists.txt file, to get -# a full list of all targets that generate coverage data. -function (lcov_capture_initial) - # Skip this function (and do not create the following targets), if there are - # no input files. - if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "") - return() - endif () - - # Add a new target to merge the files of all targets. - set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info") - lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES}) - add_custom_target(lcov-geninfo-init ALL DEPENDS ${OUTFILE} - lcov-capture-init - ) -endfunction (lcov_capture_initial) - - - - -# Add a new global target to generate coverage reports for all targets. This -# target will be used to generate the global info file. -if (NOT TARGET lcov-capture) - add_custom_target(lcov-capture) - set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "") -endif (NOT TARGET lcov-capture) - - -# This function will add capture of coverage data for target , which is -# needed to get also data for objects, which were not loaded at execution time. -# It will call geninfo for every source file of once and store the info -# file in the same directory. -function (lcov_capture_tgt TNAME) - # We don't have to check, if the target has support for coverage, thus this - # will be checked by add_coverage_target in Findcoverage.cmake. Instead we - # have to determine which gcov binary to use. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(SOURCES "") - set(TCOMPILER "") - foreach (FILE ${TSOURCES}) - codecov_path_of_source(${FILE} FILE) - if (NOT "${FILE}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (NOT "${LANG}" STREQUAL "") - list(APPEND SOURCES "${FILE}") - set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - # If no gcov binary was found, coverage data can't be evaluated. - if (NOT GCOV_${TCOMPILER}_BIN) - message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") - return() - endif () - - set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") - set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") - - - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) - set(GENINFO_FILES "") - foreach(FILE ${SOURCES}) - # Generate coverage files. If no .gcda file was generated during - # execution, the empty coverage file will be used instead. - set(OUTFILE "${TDIR}/${FILE}.info") - list(APPEND GENINFO_FILES ${OUTFILE}) - - add_custom_command(OUTPUT ${OUTFILE} - COMMAND test -f "${TDIR}/${FILE}.gcda" - && ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory - ${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN} - --output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG} - ${TDIR}/${FILE}.gcda - || cp ${OUTFILE}.init ${OUTFILE} - DEPENDS ${TNAME} ${TNAME}-capture-init - COMMENT "Capturing coverage data for ${FILE}" - ) - endforeach() - - # Concatenate all files generated by geninfo to a single file per target. - set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info") - lcov_merge_files("${OUTFILE}" ${GENINFO_FILES}) - add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE}) - - # add geninfo file generation to global lcov-capture target - add_dependencies(lcov-capture ${TNAME}-geninfo) - set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL - "" - ) - - # Add target for generating html output for this target only. - file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME}) - add_custom_target(${TNAME}-genhtml - COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR} - --baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info - --output-directory ${LCOV_HTML_PATH}/${TNAME} - --title "${CMAKE_PROJECT_NAME} - target ${TNAME}" - ${GENHTML_CPPFILT_FLAG} ${OUTFILE} - DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init - ) -endfunction (lcov_capture_tgt) - - -# This function will generate the global info file for all targets. It has to be -# called after all other CMake functions in the root CMakeLists.txt file, to get -# a full list of all targets that generate coverage data. -function (lcov_capture) - # Skip this function (and do not create the following targets), if there are - # no input files. - if ("${LCOV_CAPTURE_FILES}" STREQUAL "") - return() - endif () - - # Add a new target to merge the files of all targets. - set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info") - lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES}) - add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture) - - # Add a new global target for all lcov targets. This target could be used to - # generate the lcov html output for the whole project instead of calling - # -geninfo and -genhtml for each target. It will also be - # used to generate a html site for all project data together instead of one - # for each target. - if (NOT TARGET lcov) - file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets) - add_custom_target(lcov - COMMAND ${GENHTML_BIN} --quiet --sort - --baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info - --output-directory ${LCOV_HTML_PATH}/all_targets - --title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}" - ${GENHTML_CPPFILT_FLAG} ${OUTFILE} - DEPENDS lcov-geninfo-init lcov-geninfo - ) - endif () -endfunction (lcov_capture) - - - - -# Add a new global target to generate the lcov html report for the whole project -# instead of calling -genhtml for each target (to create an own report -# for each target). Instead of the lcov target it does not require geninfo for -# all targets, so you have to call -geninfo to generate the info files -# the targets you'd like to have in your report or lcov-geninfo for generating -# info files for all targets before calling lcov-genhtml. -file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets) -if (NOT TARGET lcov-genhtml) - add_custom_target(lcov-genhtml - COMMAND ${GENHTML_BIN} - --quiet - --output-directory ${LCOV_HTML_PATH}/selected_targets - --title \"${CMAKE_PROJECT_NAME} - targets `find - ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name - \"all_targets.info\" -exec basename {} .info \\\;`\" - --prefix ${PROJECT_SOURCE_DIR} - --sort - ${GENHTML_CPPFILT_FLAG} - `find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name - \"all_targets.info\"` - ) -endif (NOT TARGET lcov-genhtml) diff --git a/cmake/coverage/Findcodecov.cmake b/cmake/coverage/Findcodecov.cmake deleted file mode 100644 index 5bacd950c1..0000000000 --- a/cmake/coverage/Findcodecov.cmake +++ /dev/null @@ -1,312 +0,0 @@ -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - -# Add an option to choose, if coverage should be enabled or not. If enabled -# marked targets will be build with coverage support and appropriate targets -# will be added. If disabled coverage will be ignored for *ALL* targets. -option(ENABLE_COVERAGE "Enable coverage build." OFF) - -# Add an option to choose, if coverage should be enabled for all targets, even -# those which are not explictly marked as coverage targets. If disabled, only -# targets added by add_coverage will be marked for coverage build. This option -# is only available, if coverage was enabled. -if (ENABLE_COVERAGE) - option(ENABLE_COVERAGE_ALL "Enable coverage build for all targets." OFF) -endif () - -set(COVERAGE_FLAG_CANDIDATES - # gcc and clang - "-O0 -g -fprofile-arcs -ftest-coverage" - - # gcc and clang fallback - "-O0 -g --coverage" -) - -# Add coverage support for target ${TNAME} and register target for coverage -# evaluation. If coverage is disabled or not supported, this function will -# simply do nothing. -# -# Note: This function is only a wrapper to define this function always, even if -# coverage is not supported by the compiler or disabled. This function must -# be defined here, because the module will be exited, if there is no coverage -# support by the compiler or it is disabled by the user. -function (add_coverage TNAME) - # only add coverage for target, if coverage is support and enabled. - if (ENABLE_COVERAGE) - foreach (TNAME ${ARGV}) - add_coverage_target(${TNAME}) - endforeach () - endif () -endfunction (add_coverage) - -# Add global target to gather coverage information after all targets have been -# added. Other evaluation functions could be added here, after checks for the -# specific module have been passed. -# -# Note: This function is only a wrapper to define this function always, even if -# coverage is not supported by the compiler or disabled. This function must -# be defined here, because the module will be exited, if there is no coverage -# support by the compiler or it is disabled by the user. -function (coverage_evaluate) - # add lcov evaluation - if (LCOV_FOUND) - lcov_capture_initial() - lcov_capture() - endif (LCOV_FOUND) -endfunction () - -# Exit this module, if coverage is disabled. add_coverage is defined before this -# return, so this module can be exited now safely without breaking any build- -# scripts. -if (NOT ENABLE_COVERAGE) - return() -endif () - -# Find the required flags foreach language. -set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) -set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) - -get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) -foreach (LANG ${ENABLED_LANGUAGES}) - # Coverage flags are not dependent on language, but the used compiler. So - # instead of searching flags foreach language, search flags foreach compiler - # used. - set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - if (NOT COVERAGE_${COMPILER}_FLAGS) - foreach (FLAG ${COVERAGE_FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(COVERAGE_FLAG_DETECTED CACHE) - - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "Fortran") - # CheckFortranCompilerFlag was introduced in CMake 3.x. To be - # compatible with older Cmake versions, we will check if this - # module is present before we use it. Otherwise we will define - # Fortran coverage support as not available. - include(CheckFortranCompilerFlag OPTIONAL - RESULT_VARIABLE INCLUDED) - if (INCLUDED) - check_fortran_compiler_flag("${FLAG}" - COVERAGE_FLAG_DETECTED) - elseif (NOT CMAKE_REQUIRED_QUIET) - message("-- Performing Test COVERAGE_FLAG_DETECTED") - message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed" - " (Check not supported)") - endif () - endif() - - if (COVERAGE_FLAG_DETECTED) - set(COVERAGE_${COMPILER}_FLAGS "${FLAG}" - CACHE STRING "${COMPILER} flags for code coverage.") - mark_as_advanced(COVERAGE_${COMPILER}_FLAGS) - break() - else () - message(WARNING "Code coverage is not available for ${COMPILER}" - " compiler. Targets using this compiler will be " - "compiled without it.") - endif () - endforeach () - endif () -endforeach () - -set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) - -# Helper function to get the language of a source file. -function (codecov_lang_of_source FILE RETURN_VAR) - get_filename_component(FILE_EXT "${FILE}" EXT) - string(TOLOWER "${FILE_EXT}" FILE_EXT) - string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) - - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - foreach (LANG ${ENABLED_LANGUAGES}) - list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) - if (NOT ${TEMP} EQUAL -1) - set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) - return() - endif () - endforeach() - - set(${RETURN_VAR} "" PARENT_SCOPE) -endfunction () - -# Helper function to get the relative path of the source file destination path. -# This path is needed by FindGcov and FindLcov cmake files to locate the -# captured data. -function (codecov_path_of_source FILE RETURN_VAR) - string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE}) - - # If expression was found, SOURCEFILE is a generator-expression for an - # object library. Currently we found no way to call this function automatic - # for the referenced target, so it must be called in the directoryso of the - # object library definition. - if (NOT "${_source}" STREQUAL "") - set(${RETURN_VAR} "" PARENT_SCOPE) - return() - endif () - - string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}") - if(IS_ABSOLUTE ${FILE}) - file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE}) - endif() - - # get the right path for file - string(REPLACE ".." "__" PATH "${FILE}") - - set(${RETURN_VAR} "${PATH}" PARENT_SCOPE) -endfunction() - -# Add coverage support for target ${TNAME} and register target for coverage -# evaluation. -function(add_coverage_target TNAME) - # Check if all sources for target use the same compiler. If a target uses - # e.g. C and Fortran mixed and uses different compilers (e.g. clang and - # gfortran) this can trigger huge problems, because different compilers may - # use different implementations for code coverage. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(TARGET_COMPILER "") - set(ADDITIONAL_FILES "") - foreach (FILE ${TSOURCES}) - # If expression was found, FILE is a generator-expression for an object - # library. Object libraries will be ignored. - string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) - if ("${_file}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (LANG) - list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - - list(APPEND ADDITIONAL_FILES "${FILE}.gcno") - list(APPEND ADDITIONAL_FILES "${FILE}.gcda") - endif () - endif () - endforeach () - - list(REMOVE_DUPLICATES TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - - if (NUM_COMPILERS GREATER 1) - message(WARNING "Can't use code coverage for target ${TNAME}, because " - "it will be compiled by incompatible compilers. Target will be " - "compiled without code coverage.") - return() - elseif (NUM_COMPILERS EQUAL 0) - message(WARNING "Can't use code coverage for target ${TNAME}, because " - "it uses an unknown compiler. Target will be compiled without " - "code coverage.") - return() - elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS") - # A warning has been printed before, so just return if flags for this - # compiler aren't available. - return() - endif() - - # enable coverage for target - set_property(TARGET ${TNAME} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TNAME} APPEND_STRING - PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") - - # Add gcov files generated by compiler to clean target. - set(CLEAN_FILES "") - foreach (FILE ${ADDITIONAL_FILES}) - codecov_path_of_source(${FILE} FILE) - list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}") - endforeach() - - set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES - "${CLEAN_FILES}") - - - add_gcov_target(${TNAME}) - add_lcov_target(${TNAME}) -endfunction(add_coverage_target) - -# If ENABLE_COVERAGE_ALL is enabled, overload add_executable and add_library -# functions, to add coverage support for *ALL* targets. The functions will call -# the overloaded functions first and then add_coverage. -if (ENABLE_COVERAGE_ALL) - function(add_executable ARGV) - # add executable - _add_executable(${ARGV}) - - get_target_property(TARGET_TYPE "${ARGV0}" TYPE) - - if(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") - return() - endif() - - # check if target is supported for code coverage - get_target_property(TSOURCES ${ARGV0} SOURCES) - foreach (FILE ${TSOURCES}) - get_source_file_property(SLANG ${FILE} LANGUAGE) - if ((NOT ${SLANG} STREQUAL "C") AND (NOT ${SLANG} STREQUAL "CXX")) - # Target has source files that are not supported for code - # coverage. Do not add coverage for this target and print a - # warning. - message("-- Code coverage not supported for target ${ARGV0}") - return() - endif() - endforeach () - - get_target_property(ALIASED ${ARGV0} ALIASED_TARGET) - - if(NOT ALIASED) - # add coverage (if not alias and not interface) - add_coverage(${ARGV0}) - endif() - endfunction(add_executable) - - function(add_library ARGV) - # add library - _add_library(${ARGV}) - - get_target_property(TARGET_TYPE "${ARGV0}" TYPE) - - if(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") - return() - endif() - - # check if target is supported for code coverage - get_target_property(TSOURCES ${ARGV0} SOURCES) - foreach (FILE ${TSOURCES}) - get_source_file_property(SLANG ${FILE} LANGUAGE) - if ((NOT ${SLANG} STREQUAL "C") AND (NOT ${SLANG} STREQUAL "CXX")) - # Target has source files that are not supported for code - # coverage. Do not add coverage for this target and print a - # warning. - message("-- Code coverage not supported for target ${ARGV0}") - return() - endif() - endforeach () - - get_target_property(ALIASED ${ARGV0} ALIASED_TARGET) - - if(NOT ALIASED) - # add coverage (if not alias) - add_coverage(${ARGV0}) - endif() - - endfunction(add_library) -endif () - -# Include modules for parsing the collected data and output it in a readable -# format (like gcov and lcov). -find_package(Gcov) -find_package(Lcov) diff --git a/cmake/coverage/LICENSE b/cmake/coverage/LICENSE deleted file mode 100644 index 7f786a8415..0000000000 --- a/cmake/coverage/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright 2015-2017 RWTH Aachen University, Federal Republic of Germany -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/coverage/llvm-cov-wrapper b/cmake/coverage/llvm-cov-wrapper deleted file mode 100644 index a804898945..0000000000 --- a/cmake/coverage/llvm-cov-wrapper +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env sh - -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - -if [ -z "$LLVM_COV_BIN" ] -then - echo "LLVM_COV_BIN not set!" >& 2 - exit 1 -fi - - -# Get LLVM version to find out. -LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \ - | sed "s/^\([A-Za-z ]*\)\([0-9]\).\([0-9]\).*$/\2.\3/g") - -if [ "$1" = "-v" ] -then - echo "llvm-cov-wrapper $LLVM_VERSION" - exit 0 -fi - - -if [ -n "$LLVM_VERSION" ] -then - MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1) - MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2) - - if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ] - then - if [ -f "$1" ] - then - filename=$(basename "$1") - extension="${filename##*.}" - - case "$extension" in - "gcno") exec $LLVM_COV_BIN --gcno="$1" ;; - "gcda") exec $LLVM_COV_BIN --gcda="$1" ;; - esac - fi - fi - - if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ] - then - exec $LLVM_COV_BIN $@ - fi -fi - -exec $LLVM_COV_BIN gcov $@ diff --git a/deploy/packages/descriptor-metacall.json b/deploy/packages/descriptor-metacall.json index b7f9063e44..79c7fe2bab 100644 --- a/deploy/packages/descriptor-metacall.json +++ b/deploy/packages/descriptor-metacall.json @@ -26,7 +26,7 @@ "files": [{ "includePattern": "artifacts/packages/(.*\\.deb)", "uploadPattern": "$1", "matrixParams": { - "deb_distribution": "buster", + "deb_distribution": "bookworm", "deb_component": "main", "deb_architecture": "amd64" } diff --git a/deploy/packages/descriptor-metacall.json.in b/deploy/packages/descriptor-metacall.json.in index 4a28710f8b..1b12ab7aed 100644 --- a/deploy/packages/descriptor-metacall.json.in +++ b/deploy/packages/descriptor-metacall.json.in @@ -27,7 +27,7 @@ "files": [{ "includePattern": "@DEPLOY_ARTIFACTS_PATH@/(.*\\.deb)", "uploadPattern": "$1", "matrixParams": { - "deb_distribution": "buster", + "deb_distribution": "bookworm", "deb_component": "main", "deb_architecture": "amd64" } diff --git a/deploy/packages/postinst b/deploy/packages/postinst index 78c1080284..8ae7a6fa6c 100755 --- a/deploy/packages/postinst +++ b/deploy/packages/postinst @@ -4,7 +4,7 @@ # MetaCall Dependencies Bash Script by Parra Studios # Remove all packages and unused data from MetaCall building and testing. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/docker-compose.cache.yml b/docker-compose.cache.yml index 9d01d9a046..63b1b455c7 100644 --- a/docker-compose.cache.yml +++ b/docker-compose.cache.yml @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker compose infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,3 +37,9 @@ services: build: cache_from: - ${IMAGE_REGISTRY}/metacall/core:runtime + + cli: + image: metacall/core:cli + build: + cache_from: + - ${IMAGE_REGISTRY}/metacall/core:cli diff --git a/docker-compose.sh b/docker-compose.sh index a4de589f4c..e254cb103e 100755 --- a/docker-compose.sh +++ b/docker-compose.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash # # MetaCall Build Bash Script by Parra Studios # Build and install bash script utility for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,24 @@ # limitations under the License. # +set -euxo pipefail + +# Enable BuildKit whenever possible +export COMPOSE_DOCKER_CLI_BUILD=1 +export DOCKER_BUILDKIT=1 +export BUILDKIT_PROGRESS=plain +export PROGRESS_NO_TRUNC=1 + +# Check if docker compose command is available +if [ -x "$(command -v docker-compose)" ]; then + DOCKER_COMPOSE="docker-compose" +elif $(docker compose &>/dev/null) && [ $? -eq 0 ]; then + DOCKER_COMPOSE="docker compose" +else + echo "Docker Compose not installed, install it and re-run the script" + exit 1 +fi + # Pull MetaCall Docker Compose sub_pull() { if [ -z "$IMAGE_NAME" ]; then @@ -26,43 +44,139 @@ sub_pull() { exit 1 fi - docker pull $IMAGE_NAME:deps || true + docker pull $IMAGE_NAME:deps && docker tag $IMAGE_NAME:deps metacall/core:deps || true - docker pull $IMAGE_NAME:dev || true + docker pull $IMAGE_NAME:dev && docker tag $IMAGE_NAME:dev metacall/core:dev || true - docker pull $IMAGE_NAME:runtime || true + docker pull $IMAGE_NAME:runtime && docker tag $IMAGE_NAME:runtime metacall/core:runtime || true - docker pull $IMAGE_NAME:cli || true + docker pull $IMAGE_NAME:cli && docker tag $IMAGE_NAME:cli metacall/core:cli || true } # Build MetaCall Docker Compose (link manually dockerignore files) sub_build() { ln -sf tools/deps/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm deps + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm deps ln -sf tools/dev/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm dev + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm dev ln -sf tools/runtime/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm runtime + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm runtime ln -sf tools/cli/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm cli + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm cli } # Build MetaCall Docker Compose without cache (link manually dockerignore files) sub_rebuild() { ln -sf tools/deps/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache deps + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache deps ln -sf tools/dev/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache dev + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache dev ln -sf tools/runtime/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache runtime + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache runtime ln -sf tools/cli/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache cli + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache cli +} + +# Build MetaCall Docker Compose for testing (link manually dockerignore files) +sub_test() { + # Disable BuildKit as workaround due to log limits (TODO: https://github.com/docker/buildx/issues/484) + export DOCKER_BUILDKIT=0 + + # Disable build with sanitizer + export METACALL_BUILD_SANITIZER= + + # Disable build with coverage + export METACALL_BUILD_COVERAGE= + + # Define build type + export METACALL_BUILD_TYPE=${METACALL_BUILD_TYPE:-debug} + + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm deps + + ln -sf tools/dev/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev +} + +# Build MetaCall Docker Compose with Sanitizer for testing (link manually dockerignore files) +sub_test_sanitizer() { + # Disable BuildKit as workaround due to log limits (TODO: https://github.com/docker/buildx/issues/484) + export DOCKER_BUILDKIT=0 + + # Enable build with sanitizer + export METACALL_BUILD_SANITIZER=${METACALL_BUILD_SANITIZER:-address-sanitizer} + + # Disable build with coverage + export METACALL_BUILD_COVERAGE= + + # Define build type + export METACALL_BUILD_TYPE=${METACALL_BUILD_TYPE:-debug} + + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm deps + + ln -sf tools/dev/.dockerignore .dockerignore + + if [ ! -z "${SANITIZER_SKIP_SUMMARY:-}" ]; then + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev + else + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev | tee /tmp/metacall-test-output + + # Retrieve all the summaries + SUMMARY=$(grep "SUMMARY:" /tmp/metacall-test-output) + echo "${SUMMARY}" + printf "Number of leaks detected: " + echo "${SUMMARY}" | awk '{print $7}' | awk '{s+=$1} END {print s}' + + # Count the number of tests that really failed and avoid the false positives + FAILED=$(grep "FAILED TEST" /tmp/metacall-test-output) + printf "Number of tests failed: " + echo "${FAILED}" | awk '{print $1}' | awk '{s+=$1} END {print s}' + + # Get the potential tests that failed + BEGIN=$(grep -n "The following tests FAILED:" /tmp/metacall-test-output | cut -d : -f 1) + END=$(grep -n "Errors while running CTest" /tmp/metacall-test-output | cut -d : -f 1) + + if [ -z "${BEGIN}" ] || [ -z "${END}" ]; then + echo "ERROR! CTest failed to print properly the output, run tests again:" + echo " Recompiling everything: docker rmi metacall/core:dev && ./docker-compose.sh test-${METACALL_BUILD_SANITIZER}" + echo " Without recompiling (needs to be built successfully previously): docker run --rm -it metacall/core:dev sh -c \"cd build && ctest -j$(getconf _NPROCESSORS_ONLN) --output-on-failure\"" + else + BEGIN=$((BEGIN + 1)) + END=$((END - 1)) + echo "List of potential failed tests:" + sed -n "${BEGIN},${END}p" /tmp/metacall-test-output + fi + + rm /tmp/metacall-test-output + fi +} + +# Build MetaCall Docker Compose for coverage (link manually dockerignore files) +sub_coverage() { + # Disable BuildKit as workaround due to log limits (TODO: https://github.com/docker/buildx/issues/484) + export DOCKER_BUILDKIT=0 + + # Disable build with sanitizer + export METACALL_BUILD_SANITIZER= + + # Disable build with coverage + export METACALL_BUILD_COVERAGE=coverage + + # Define build type + export METACALL_BUILD_TYPE=debug + + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm deps + + ln -sf tools/dev/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev } # Build MetaCall Docker Compose with caching (link manually dockerignore files) @@ -73,20 +187,56 @@ sub_cache() { fi ln -sf tools/deps/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build deps + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build deps + + ln -sf tools/dev/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build dev + + ln -sf tools/runtime/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build runtime + + ln -sf tools/cli/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build cli +} + +# Build MetaCall Docker Compose with multi-platform specifier (link manually dockerignore files) +sub_platform() { + if [ -z "$METACALL_PLATFORM" ]; then + echo "Error: METACALL_PLATFORM variable not defined" + exit 1 + fi + + # Initialize QEMU for Buildkit + docker run --rm --privileged tonistiigi/binfmt --install all + + # Debian in Docker Hub does not support LoongArch64 yet, let's use official LoongArch repository instead + if [ "$METACALL_PLATFORM" = "linux/loong64" ]; then + source .env + export METACALL_BASE_IMAGE="ghcr.io/loong64/${METACALL_BASE_IMAGE}" + fi + + # Generate the docker compose file with all .env variables substituted (bake seems not to support this) + $DOCKER_COMPOSE -f docker-compose.yml config &> docker-compose.bake.yml + + # Build with Bake, so the image can be loaded into local docker context + ln -sf tools/deps/.dockerignore .dockerignore + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load deps ln -sf tools/dev/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build dev + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load dev ln -sf tools/runtime/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build runtime + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load runtime ln -sf tools/cli/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build cli + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load cli + + # Delete temporal docker compose file + rm -rf docker-compose.bake.yml } # Push MetaCall Docker Compose -sub_push(){ +sub_push() { if [ -z "$IMAGE_NAME" ]; then echo "Error: IMAGE_NAME variable not defined" exit 1 @@ -113,8 +263,38 @@ sub_push(){ docker push $IMAGE_NAME:latest } +# Version MetaCall Docker Compose +sub_version() { + if [ -z "$IMAGE_NAME" ]; then + echo "Error: IMAGE_NAME variable not defined" + exit 1 + fi + + VERSION=$(tail -n 1 VERSION | tr -d '\n') + + # Push deps image + docker tag metacall/core:deps $IMAGE_NAME:${VERSION}-deps + docker push $IMAGE_NAME:${VERSION}-deps + + # Push dev image + docker tag metacall/core:dev $IMAGE_NAME:${VERSION}-dev + docker push $IMAGE_NAME:${VERSION}-dev + + # Push runtime image + docker tag metacall/core:runtime $IMAGE_NAME:${VERSION}-runtime + docker push $IMAGE_NAME:${VERSION}-runtime + + # Push cli image + docker tag metacall/core:cli $IMAGE_NAME:${VERSION}-cli + docker push $IMAGE_NAME:${VERSION}-cli + + # Push cli image as version + docker tag metacall/core:cli $IMAGE_NAME:${VERSION} + docker push $IMAGE_NAME:${VERSION} +} + # Pack MetaCall Docker Compose -sub_pack(){ +sub_pack() { if [ -z "$ARTIFACTS_PATH" ]; then echo "Error: ARTIFACTS_PATH variable not defined" exit 1 @@ -149,7 +329,13 @@ sub_help() { echo " pull" echo " build" echo " rebuild" + echo " test" + echo " test-address-sanitizer" + echo " test-thread-sanitizer" + echo " test-memory-sanitizer" + echo " coverage" echo " cache" + echo " platform" echo " push" echo " pack" echo "" @@ -165,12 +351,36 @@ case "$1" in rebuild) sub_rebuild ;; + test) + sub_test + ;; + test-address-sanitizer) + export METACALL_BUILD_SANITIZER="address-sanitizer" + sub_test_sanitizer + ;; + test-thread-sanitizer) + export METACALL_BUILD_SANITIZER="thread-sanitizer" + sub_test_sanitizer + ;; + test-memory-sanitizer) + export METACALL_BUILD_SANITIZER="memory-sanitizer" + sub_test_sanitizer + ;; + coverage) + sub_coverage + ;; cache) sub_cache ;; + platform) + sub_platform + ;; push) sub_push ;; + version) + sub_version + ;; pack) sub_pack ;; diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000000..5802aeef51 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,34 @@ +# +# MetaCall Library by Parra Studios +# Docker compose infrastructure for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: "3.7" + +services: + deps: + image: metacall/core:deps + build: + args: + METACALL_BUILD_TYPE: ${METACALL_BUILD_TYPE} + METACALL_INSTALL_OPTIONS: base python ruby netcore8 nodejs typescript file rpc wasm java c cobol go rust rapidjson pack backtrace sandbox ${METACALL_BUILD_COVERAGE} # clangformat v8rep51 + dev: + image: metacall/core:dev + build: + args: + METACALL_BUILD_TYPE: ${METACALL_BUILD_TYPE} + METACALL_BUILD_OPTIONS: ${METACALL_BUILD_SANITIZER} python ruby netcore8 nodejs typescript file rpc wasm java c cobol go rust examples tests scripts ports install pack sandbox benchmarks ${METACALL_BUILD_COVERAGE} # v8 diff --git a/docker-compose.yml b/docker-compose.yml index 309f10ee0f..1c1997b138 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker compose infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,89 +17,93 @@ # limitations under the License. # -version: "3.7" - services: deps: image: metacall/core:deps container_name: metacall_core_deps build: + network: "host" context: . dockerfile: tools/deps/Dockerfile args: METACALL_BASE_IMAGE: $METACALL_BASE_IMAGE METACALL_PATH: $METACALL_PATH METACALL_TOOLS_PATH: $METACALL_PATH/tools - METACALL_INSTALL_OPTIONS: root base python ruby netcore2 nodejs typescript file rapidjson funchook swig pack # v8rep51 coverage + METACALL_BUILD_TYPE: $METACALL_BUILD_TYPE + METACALL_INSTALL_OPTIONS: base python ruby nodejs typescript file rpc rapidjson pack backtrace # clangformat v8rep51 coverage environment: DEBIAN_FRONTEND: noninteractive + # Work around https://github.com/dotnet/cli/issues/1582 until Docker releases a + # fix (https://github.com/docker/docker/issues/20818). This workaround allows + # the container to be run with the default seccomp Docker settings by avoiding + # the restart_syscall made by LTTng which causes a failed assertion. LTTNG_UST_REGISTER_TIMEOUT: 0 + # Trigger the population of the local NuGet package cache NUGET_XMLDOC_MODE: skip + # Disable NetCore telemetry + DOTNET_CLI_TELEMETRY_OPTOUT: "true" dev: image: metacall/core:dev container_name: metacall_core_dev build: + network: "host" context: . dockerfile: tools/dev/Dockerfile args: METACALL_PATH: $METACALL_PATH METACALL_BUILD_TYPE: $METACALL_BUILD_TYPE - METACALL_BUILD_OPTIONS: root python ruby netcore2 nodejs typescript file examples distributable tests benchmarks scripts ports dynamic install pack # v8 coverage + METACALL_BUILD_OPTIONS: python ruby nodejs typescript file rpc examples tests scripts ports install pack # v8 coverage benchmarks environment: DEBIAN_FRONTEND: noninteractive LTTNG_UST_REGISTER_TIMEOUT: 0 NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" LOADER_LIBRARY_PATH: $METACALL_PATH/build LOADER_SCRIPT_PATH: $METACALL_PATH/build/scripts CONFIGURATION_PATH: $METACALL_PATH/build/configurations/global.json SERIAL_LIBRARY_PATH: $METACALL_PATH/build DETOUR_LIBRARY_PATH: $METACALL_PATH/build - PORT_LIBRARY_PATH: $METACALL_PATH/build - depends_on: - - deps + NODE_PATH: /usr/lib/node_modules runtime: image: metacall/core:runtime container_name: metacall_core_runtime build: + network: "host" context: . dockerfile: tools/runtime/Dockerfile args: METACALL_PATH: $METACALL_PATH METACALL_BASE_IMAGE: $METACALL_BASE_IMAGE - METACALL_RUNTIME_OPTIONS: root base python ruby netcore2 nodejs typescript file ports clean # v8 + METACALL_RUNTIME_OPTIONS: base python ruby nodejs typescript file rpc backtrace ports clean # v8 environment: DEBIAN_FRONTEND: noninteractive LTTNG_UST_REGISTER_TIMEOUT: 0 NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" LOADER_LIBRARY_PATH: /usr/local/lib LOADER_SCRIPT_PATH: /usr/local/scripts CONFIGURATION_PATH: /usr/local/share/metacall/configurations/global.json SERIAL_LIBRARY_PATH: /usr/local/lib DETOUR_LIBRARY_PATH: /usr/local/lib - PORT_LIBRARY_PATH: /usr/local/lib NODE_PATH: /usr/local/lib/node_modules - depends_on: - - dev cli: image: metacall/core:cli container_name: metacall_core_cli build: + network: "host" context: . dockerfile: tools/cli/Dockerfile environment: DEBIAN_FRONTEND: noninteractive LTTNG_UST_REGISTER_TIMEOUT: 0 NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" LOADER_LIBRARY_PATH: /usr/local/lib LOADER_SCRIPT_PATH: /usr/local/scripts CONFIGURATION_PATH: /usr/local/share/metacall/configurations/global.json SERIAL_LIBRARY_PATH: /usr/local/lib DETOUR_LIBRARY_PATH: /usr/local/lib - PORT_LIBRARY_PATH: /usr/local/lib NODE_PATH: /usr/local/lib/node_modules - depends_on: - - dev - - runtime diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..55c101c5cd --- /dev/null +++ b/docs/README.md @@ -0,0 +1,884 @@ +
+ M E T A C A L L +

M E T A C A L L

+

A library for providing inter-language foreign function interface calls

+
+ +# Abstract + +**METACALL** is a library that allows calling functions, methods or procedures between programming languages. With **METACALL** you can transparently execute code from/to any programming language, e.g. by calling a Python function from NodeJS: + +`sum.py` + +```python +def sum(a, b): + return a + b +``` + +`main.js` + +```javascript +const { sum } = require("sum.py"); + +sum(3, 4); // 7 +``` + +Use the [installer](https://github.com/metacall/install) and try [some examples](#43-examples). + +
+ M E T A C A L L +
+ +# Table of Contents + + + +- [Abstract](#abstract) +- [Table of Contents](#table-of-contents) + - [1. Motivation](#1-motivation) + - [2. Language Support](#2-language-support) + - [2.1 Loaders (Backends)](#21-loaders-backends) + - [2.2 Ports (Frontends)](#22-ports-frontends) + - [3. Use Cases](#3-use-cases) + - [3.1 Known Projects Using MetaCall](#31-known-projects-using-metacall) + - [4. Usage](#4-usage) + - [4.1 Installation](#41-installation) + - [4.2 Environment Variables](#42-environment-variables) + - [4.3 Examples](#43-examples) + - [5. Architecture](#5-architecture) + - [5.1 Overview](#51-overview) + - [5.1.1 Design Decisions](#511-design-decisions) + - [5.1.2 Modules](#512-modules) + - [5.2 Reflect](#52-reflect) + - [5.2.1 Type System](#521-type-system) + - [5.2.2 Values](#522-values) + - [5.2.3 Functions](#523-functions) + - [5.3 Plugins](#53-plugins) + - [5.3.1 Loaders](#531-loaders) + - [5.3.1.1 Python](#5311-python) + - [5.3.1.2 NodeJS](#5312-nodejs) + - [5.3.1.3 JavaScript](#5313-javascript) + - [5.3.1.4 C#](#5314-c) + - [5.3.1.5 Ruby](#5315-ruby) + - [5.3.1.6 Mock](#5316-mock) + - [5.3.1.7 File](#5317-file) + - [5.3.2 Serials](#532-serials) + - [5.3.2.1 MetaCall](#5321-metacall) + - [5.3.2.2 RapidJSON](#5322-rapidjson) + - [5.3.3 Detours](#533-detours) + - [5.3.3.1 PLTHook](#5331-plthook) + - [5.4 Ports](#54-ports) + - [5.5 Serialization](#55-serialization) + - [5.6 Memory Layout](#56-memory-layout) + - [5.7 Fork Model](#57-fork-model) + - [5.8 Threading Model](#58-threading-model) + - [5. Application Programming Interface (API)](#5-application-programming-interface-api) + - [6. Build System](#6-build-system) + - [6.1 Build Options](#61-build-options) + - [6.2 Coverage](#62-coverage) + - [6.3 Debugging](#63-debugging) + - [6.4 Build on Cloud - Gitpod](#64-build-on-cloud---gitpod) + - [7. Platform Support](#7-platform-support) + - [7.1 Docker Support](#71-docker-support) + - [7.1.1 Docker Development](#711-docker-development) + - [7.1.2 Docker Testing](#712-docker-testing) + - [8. Benchmarks](#8-benchmarks) + - [9. License](#9-license) + + + +## 1. Motivation + +The **METACALL** project started a long time ago when I was coding a [Game Engine for an MMORPG](https://bitbucket.org/parrastudios/argentum-online-c). My idea was to provide an interface to allow other programmers to extend the Game Engine easily. By that time, I was finishing university so I decided to do my [Final Thesis](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-paper.pdf) and [Presentation](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-presentation.pdf) based on the plug-in system for my Game Engine. The Plugin Architecture designed for the Game Engine has similarities with **METACALL** although the architecture has been redefined and the code has been rewritten from scratch. After some refinement of the system, I came up with **METACALL** and other use cases for the tool. Currently we are using **METACALL** to build a cutting edge FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on this technique to provide high scalability of the functions among multiple cores and **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern, a new technique I have developed to transparently interconnect functions in a distributed system based on this library. + +## 2. Language Support + +This section describes all programming languages that **METACALL** supports. **METACALL** is offered through a C API. This means you can use it as a library to embed different runtimes into C. The **[Loaders](#21-loaders-backends)** are the ones that allow to call different functions from C. They are plugins (libraries) that **METACALL** loads and they have a common interface. They usually implement JITs, VMs or interpreters. On the other hand we have the **[Ports](#22-ports-frontends)** which are wrappers to the **METACALL** C API that expose the API to other languages. With the Python Loader we can execute calls to Python from C. With the Python Port we can install **METACALL** via `pip` and use it to call other languages from Python. The combination of both provides the opportunity for complete interoperability between virtually any two languages. + +### 2.1 Loaders (Backends) + +This section describes all programming languages that **METACALL** allows to load and invoke from C language, in other words all languages that **METACALL** can embed. If you are interested in design and implementation details of the loaders, please go to [loaders section](#531-loaders). + +- Currently supported languages and run-times: + +| Language | Runtime | Version | Tag | +| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------: | :--: | +| [Python](https://www.python.org/) | [Python C API](https://docs.python.org/3/c-api/intro.html) | **>= 3.2 <= 3.9** | py | +| [NodeJS](https://nodejs.org/) | [N API](https://nodejs.org/api/n-api.html) | **>= 10.22.0 <= 17.x.x** | node | +| [TypeScript](https://www.typescriptlang.org/) | [TypeScript Language Service API](https://github.com/microsoft/TypeScript/wiki/Using-the-Language-Service-API) | **4.2.3** | ts | +| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [V8](https://v8.dev/) | **5.1.117** | js | +| [C#](https://dotnet.microsoft.com/) | [NetCore](https://github.com/dotnet/docs/blob/master/docs/core/tutorials/netcore-hosting.md) | **>= 1.0.0-preview2 <= 7.0.4** | cs | +| [Ruby](https://ruby-lang.org/) | [Ruby C API](https://silverhammermba.github.io/emberb/c/) | **>= 2.1 <= 2.7** | rb | +| [Cobol](https://sourceforge.net/projects/open-cobol/) | [GNU/Cobol](https://open-cobol.sourceforge.io/doxygen/gnucobol-2/libcob_8h.html) | **>= 1.1.0** | cob | +| [File](/source/loaders/file_loader) | **∅** | **0.1.0** | file | +| [Mock](/source/loaders/mock_loader) | **∅** | **0.1.0** | mock | +| [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) | [cURL](https://curl.haxx.se/) | **>=7.64.0** | rpc | +| [Java](https://www.java.com) | [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine) | **>=11** | java | +| [WebAssembly](https://webassembly.org/) | [Wasmtime](https://github.com/bytecodealliance/wasmtime) | **>= 0.27 <= 8.0.1** | wasm | +| [C]() | [libclang](https://clang.llvm.org/doxygen/group__CINDEX.html) - [Tiny C Compiler](https://bellard.org/tcc/) - [libffi](http://sourceware.org/libffi/) | **>=12** - **>=2021-10-30** - **>=3.2** | c | +| [Rust](https://www.rust-lang.org/) | [rustc](https://doc.rust-lang.org/rustc/what-is-rustc.html) - [libffi](http://sourceware.org/libffi/) | **nightly-2021-12-04** | rs | + +- Languages and run-times under construction: + +| Language | Runtime | Tag | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | :--: | +| [C++](http://www.cplusplus.com/) | [Clang](https://clang.llvm.org/) - [LLVM](https://llvm.org/) | cpp | +| [PHP](https://php.net/) | [Zend](https://www.php.net/manual/en/internals2.ze1.zendapi.php) | php | +| [Go](https://golang.org/) | Go Runtime | go | +| [Haskell](https://www.haskell.org/) | [Haskell FFI](https://wiki.haskell.org/GHC/Using_the_FFI) | hs | +| [Crystal](https://crystal-lang.org/) | [Crystal Compiler Internals](https://github.com/crystal-lang/crystal/wiki/Compiler-internals) | cr | +| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [SpiderMonkey](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference) | jsm | +| [Dart](https://dart.dev/) | [Dart VM](https://dart.dev/tools/dart-vm) | dart | +| [LuaJIT](https://luajit.org/) | [LuaJIT2](https://github.com/openresty/luajit2) | lua | +| [LLVM IR](https://www.llvm.org/devmtg/2017-06/1-Davis-Chisnall-LLVM-2017.pdf) | [LLVM](https://llvm.org/) | llvm | +| [Julia](https://julialang.org/) | [Julia Runtime](https://docs.julialang.org/en/v1/devdocs/init/) | jl | + +### 2.2 Ports (Frontends) + +Ports are the frontends to the **METACALL C API** from other languages. They allow to use **METACALL** from different languages. If you are interested in design and implementation details of the ports, please go to [ports section](#54-ports). + +- Currently supported languages and run-times: + +| Language | Runtime | Version | +| ------------------------------------------------------------------ | ---------------------------------------------------------- | :-------------------: | +| [Python](https://www.python.org/) | [Python C API](https://docs.python.org/3/c-api/intro.html) | **3.x** | +| [NodeJS](https://nodejs.org/) | [N API](https://nodejs.org/api/n-api.html) | **>= 8.11.1** | +| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [D8 (V8)](https://v8.dev/docs/d8) | **5.1.117** | +| [C#](https://dotnet.microsoft.com/) | [NetCore](https://github.com/dotnet/core) | **>= 1.0.0-preview2** | +| [Ruby](https://ruby-lang.org/) | [Ruby C API](https://silverhammermba.github.io/emberb/c/) | **2.x** | +| [Go](https://golang.org/) | [CGO](https://golang.org/cmd/cgo/) | **1.x** | +| [D](https://dlang.org/) | [DMD](https://wiki.dlang.org/DMD) | **2.x** | +| [Rust](https://www.rust-lang.org/) | **∅** | **>= 1.47.0** | +| [Scala](https://www.scala-lang.org/) | [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine) | **>= 2.13.x** | +| [Nim](https://nim-lang.org/) | **∅** | **>= 1.4.2** | + +## 3. Use Cases + +**METACALL** can be used in the following cases: + +- Interconnect different technologies in the same project. It allows heterogeneous teams of developers to work on the same project in an isolated way and use different programming languages at the same time. + +- Embedding programming languages in existing software. Game Engines and 3D Editors like [Blender](https://www.blender.org/), among others can take benefit of **METACALL** and extend the core functionality with higher level programming languages (aka scripting). + +- Function as a Service. **METACALL** can be used to implement efficient FaaS architectures. We are using it to implement our own FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern and high performance function scalability thanks to this library. + +- Source code migrations. **METACALL** can wrap large and legacy codebases, and provide an agnostic way to work with the codebase in a new programming language. Eventually the code can be migrated in parts, without needing to create a new project or stop the production environment. Incremental changes can be made, solving the migration easily and with less time and effort. + +- Porting low level libraries to high level languages transparently. With **METACALL** you can get rid of extension APIs like Python C API or NodeJS N-API. You can call low level libraries directly from your high level languages without making a wrapper in C or C++ for it. + +As you can see, there are plenty of uses. **METACALL** introduces a new model of programming which allows high interoperability between technologies. If you find any other use case just let us know about it with a Pull Request and we will add it to the list. + +## 3.1 Known Projects Using MetaCall + +- **[Acid Cam](https://www.facebook.com/AcidCam/)**: A software for video manipulation that distorts videos for generating art by means of OpenCV. [Acid Cam CLI](https://github.com/lostjared/acidcam-cli) uses **METACALL** to allow custom filters written in Python and easily embed Python programming language into its plugin system. + +- **[Pragma](https://pragmalang.com/)**: Pragma is a language for building beautiful and extensible GraphQL APIs in no time. Within a single file, you can define your data models and authorization rules (roles and permissions), and import serverless functions for data validation, transformation, authorization, or any custom logic. [Pragma](https://github.com/pragmalang/pragma) uses **METACALL** to import and execute functions into the language so it can be extended with other programming languages. + +## 4. Usage + +## 4.1 Installation + +Before trying any of the examples, you must have **METACALL** installed in your system. To install **METACALL** you have the following options: + +- [Install precompiled tarball via shell script (downloads the tarball generated by Guix)](https://github.com/metacall/install). +- [Build and install it manually](#6-build-system). +- [Pull it from DockerHub](https://hub.docker.com/r/metacall/core). +- [Install via Guix package manager](https://github.com/metacall/distributable/blob/master/source/metacall.scm) (needs to fix the commit of [Guix channels](https://github.com/metacall/distributable/blob/master/channels/channels.scm)). +- [Download the precompiled tarball from Guix via Distributable Releases Assets](https://github.com/metacall/distributable/releases). +- [ArchLinux AUR](https://github.com/metacall/aur). +- [Homebrew](https://github.com/metacall/homebrew). +- [Download precompiled tarball (.tar.gz) or Debian (.deb) / RPM (.rpm) installers via Core Releases Assets](https://github.com/metacall/core/releases). + +### 4.2 Environment Variables + +The environment variables are optional, in case you want to modify default paths of **METACALL**. + +| Name | Description | Default Value | +| :-----------------------: | ---------------------------------------------------------------- | :------------------------------: | +| **`DETOUR_LIBRARY_PATH`** | Directory where detour plugins to be loaded are located | **`detours`** | +| **`SERIAL_LIBRARY_PATH`** | Directory where serial plugins to be loaded are located | **`serials`** | +| **`CONFIGURATION_PATH`** | File path where the **METACALL** global configuration is located | **`configurations/global.json`** | +| **`LOADER_LIBRARY_PATH`** | Directory where loader plugins to be loaded are located | **`loaders`** | +| **`LOADER_SCRIPT_PATH`** | Directory where scripts to be loaded are located | **`${execution_path}`** ¹ | + +¹ **`${execution_path}`** defines the path where the program is executed, **`.`** in Linux. + +### 4.3 Examples + +- [BeautifulSoup from Express](https://github.com/metacall/beautifulsoup-express-example): This example shows how to use [**METACALL** CLI](/source/cli/metacallcli) for building a **Polyglot Scraping API** that mixes NodeJS with Python. + +- [Higher Order Functions with Python & NodeJS](https://github.com/metacall/fn.py-javascript-example): An example of using [Fn.py](https://github.com/kachayev/fn.py) (Python) from JavaScript (NodeJS). + +- [Embedding NodeJS](https://github.com/metacall/embedding-nodejs-example): Example application for embedding NodeJS code into C/C++ using CMake as a build system. + +- [Embedding Python](https://github.com/metacall/embedding-python-example): Example application for embedding Python code into C/C++ using CMake as a build system. + +- [Embedding Ruby](https://github.com/metacall/embedding-ruby-example): Example application for embedding Ruby code into C/C++ using CMake as a build system. + +- [Mixing Go and TypeScript](https://github.com/metacall/golang-typescript-example): This example shows how to embed TypeScript into Go using METACALL. In other words, calling TypeScript functions from Go. + +- [Using `matplotlib` from C/C++](https://github.com/metacall/embedding-matplotlib-example): Example application for using Python `matplotlib` library into C/C++ using `gcc` for compiling it and installing **METACALL** by compining it by hand. + +- [Polyglot Redis Module](https://github.com/metacall/redis-module): Extend Redis DataBase modules with TypeScript, JavaScript, Python, C#, Ruby... + +- [Rotulin](https://github.com/metacall/rotulin): Example of a multi-language application built with **METACALL**. This application embeds a Django server with a Ruby DataBase and C# business layer based on ImageMagick. + +## 5. Architecture + +### 5.1 Overview + +#### 5.1.1 Design Decisions + +- To provide a high level API with a simple UX and to be easy to understand. + +- To work in high performance environments. + +- To be as cross-platform as possible. + +- To avoid modifying run-times directly or using the code inside **METACALL** to avoid maintaining them, or propagating security flaws or licenses into **METACALL**. + +- To provide support for any embeddable programming language and to provide support for **METACALL** to be used from any programming language. + +- All external code used in **METACALL** must be introduced by inversion of control in the plugin system so that the core must not remain aware of what software is used. + +- All code developed in **METACALL** must be implemented in standalone libraries that can work by themselves in an isolated way (aka modules). + +#### 5.1.2 Modules + +- [`adt`](/source/adt) provides a base for Abstract Data Types and algorithms used in **METACALL**. Implementation must be done in an efficient and generic way. Some of the data structures implemented are vector, set, hash, comparable or trie. + +- [`detour`](/source/detour) provides an interface to hook into functions. Detours are used by the [fork model](#57-fork-model) to intercept fork calls. + +- [`detours`](/source/detours) implement the [`detour`](/source/detour) interface by using a plugin architecture. The current list of available detour plugins is the following one. + + - [`plthook_detour`](/source/detours/plthook_detour) implemented by means of PLTHook library. + +- [`dynlink`](/source/dynlink) implements a cross-platform method to dynamically load libraries. It is used to dynamically load plugins into **METACALL**. + +- [`environment`](/source/environment) implements a standard way to deal with environment variables. **METACALL** uses environment variables to define custom paths for plugins and scripts. + +- [`examples`](/source/examples) ... + +- [`filesystem`](/source/filesystem) provides an abstraction for operative system file system. + +- [`format`](/source/format) provides a standard way for printing to standard input output for old C versions that does not support newest constructions. + +- [`loader`](/source/loader) ... + +- [`loaders`](/source/loaders) + +- [`log`](/source/log) + +- [`memory`](/source/memory) + +- [`metacall`](/source/metacall) + +- [`ports`](/source/ports) + +- [`preprocessor`](/source/preprocessor) + +- [`reflect`](/source/reflect) + +- [`scripts`](/source/scripts) + +- [`serial`](/source/serial) + +- [`serials`](/source/serials) + +- [`tests`](/source/tests) + +- [`version`](/source/version) + +### 5.2 Reflect + +The module that holds the representation of types, values and functions is called [`reflect`](/source/reflect) and it handles the abstraction of code loaded into **METACALL**. + +**METACALL** uses reflection and introspection techniques to inspect the code loaded by the [`loaders`](/source/loaders) in order to interpret it and provide a higher abstraction of it. With this higher abstraction **METACALL** can easily inter-operate between languages transparently. + +#### 5.2.1 Type System + +**METACALL** implements an abstract type system which is a binary representation of the types supported by it. This means that **METACALL** can convert any type of a language to its own type system and back. Each loader is responsible of doing these conversions. + +**METACALL** maintains most of the types of the languages but not all are supported. If new types are added they have to be implemented in the [`reflect`](/source/reflect) module and also in the [`loaders`](/source/loaders) and [`serials`](/source/serials) to fully support it. + +| Type | Value | +| :------: | ------------------------------------------------------------------------------ | +| Boolean | `true` or `false` | +| Char | `-128` to `127` | +| Short | `-32,768` to `32,767` | +| Int | `-2,147,483,648` to `2,147,483,647` | +| Long | `–9,223,372,036,854,775,808` to `9,223,372,036,854,775,807` | +| Float | `1.2E-38` to `3.4E+38` | +| Double | `2.3E-308` to `1.7E+308` | +| String | NULL terminated list of characters | +| Buffer | Blob of memory representing a binary data | +| Array | Arrangement of values of any type | +| Map | List of elements formed by a key (String) value (Any) pair (Array) | +| Pointer | Low level representation of a memory reference | +| Null | Representation of NULL value type | +| Future | Promise in Node Loader, and any other type equivalent in other languages. | +| Function | Block of code that takes inputs (Arguments) and produces output (Return value) | +| Class | Defines properties and methods that are common to all objects | +| Object | An instance of Class | + +- Boolean is mostly represented by an integer value. There are languages that does not support it so it gets converted to an integer value in the memory layout. + +- Integer and Floating Point values provide a complete abstraction to numerical types. Type sizes are preserved and the correct type is used when using any number. This depends on the internal implementation of the value by the run-time. Although there can be problems related to this. A `bignum` type from Ruby may overflow if it is too big when trying to convert it to a `float` type in C#. + +- String is represented by ASCII encoding currently. Future versions will implement multiple encodings to be interoperable between other language encodings. + +- Buffer represents a blob of raw memory (i.e. an array of bytes). This can be used to represent files as images or any other resources into memory. + +- Array is implemented using an array of values, which might lead one to ponder that it should be called _list_ instead. But as the memory layout is stored into a contiguous memory block of references to values, it is considered an array. + +- Map implements an associative key value pair container. A map is implemented with an array of two sized elements array. Each element of the map is an array of size two, where the first element of it is always a String and the second element is a value of any type. + +- Pointer is an opaque value representing a raw reference to a memory block. Some languages allow to use references to memory and some others do not. This type is opaque because **METACALL** does not know what kind of concrete value represents it. The representation may be a complex type handled by the developer source code inside the run-time. + +- Null type implements a null value. This type has only been implemented in order to support null value from multiple run-times. It represents a null value and it does not have data size on the value allocated. + +#### 5.2.2 Values + +Values represent the instances of the **METACALL** type system. + +The memory layout guarantees to fit at least the same size of the types into memory. This means if a boolean type can be represented with one bit inside a value of one byte size, maybe this value is stored in a bigger memory block and this fact is architecture and platform dependant. + +When converting values between different types, if any potential number overflow or invalid conversion between types is done, **METACALL** will warn about it. If any conversion of types can be handled by **METACALL**, it will automatically cast or transform the values into the target type automatically in order to avoid errors in the call. + +The value model is implemented by means of object pool. Each value is a reference to a memory block allocated from a memory pool (which can be injected into **METACALL**). The references can be passed by value, this means **METACALL** copies the reference value instead of the data which this reference is pointing to, like most run-times do when managing their own values. + +Each created value must be destroyed manually, otherwise it will lead to a memory leak. This only occurs when dealing with **METACALL** at C level. If **METACALL** is being used in a higher language through [`ports`](/source/ports), the developer does not have to care about memory management. + +The value memory layout is described in the following form. + +| Memory Offset | `0` to `sizeof(data) - 1` | `sizeof(data)` to `sizeof(data) + sizeof(type_id) - 1` | +| :-----------: | :-----------------------: | :----------------------------------------------------: | +| **Content** | **DATA** | **TYPE ID** | + +This layout is used for the following reasons: + +- Data is located at the first position of the memory block, so it can be used as a normal low level value. This allows to treat **METACALL** values as normal C values. Therefore you can use **METACALL** with normal pointers to existing variables, literal values as shown in the previous examples or **METACALL** values. + +- Data can be accessed faster as it is located at first position of the memory block. There is not extra calculation of an offset when trying to access the pointer. + +- Data and type id are contiguously allocated in order to treat it as the same memory block so it can be freed with one operation. + +#### 5.2.3 Functions + +Functions are abstract callable representations of functions, methods or procedures loaded by [`loaders`](/source/loaders). A function is like a template which is linked to a loader run-time and allows to do a foreign function call. + +A function is composed of a name and a signature. The signature defines the arguments names, types, and return type, if any. When a function is loaded, **METACALL** tries to inspect the signature and records the types, if any. It stores the arguments names and sizes and also a concrete type that will be used later by the loader to implement the call to the run-time. + +The function interface must be implemented by the [`loaders`](/source/loaders) and it has the following form. + +```c +typedef struct function_interface_type +{ + function_impl_interface_create create; + function_impl_interface_invoke invoke; + function_impl_interface_await await; + function_impl_interface_destroy destroy; + +} * function_interface; +``` + +- `create` instantiates the function concrete data related to the run-time. +- `invoke` transforms arguments from [`reflect`](/source/reflect) abstract types to run-time concrete types, executes the call in the run-time, and converts the result of the call from run-time concrete type to [`reflect`](/source/reflect) abstract type. +- `await` idem to invoke but awaiting the promise that is expected to be returned by the function. +- `destroy` clears all data previously instantiated in `create`. + +The type deduction can be done at different levels. For example, it is possible to guess function types from the loaded code. + +```python +def multiply_type(a: int, b: int) -> int: + return a * b +``` + +If this code is loaded, **METACALL** will be able to inspect the types and define the signature. Signature includes the names of the arguments, the types of those arguments, if any, and the return type, if any. + +It may be possible that the function loaded into **METACALL** is duck typed. This means it does not have information about what types it supports and therefore they cannot be inspected statically. + +```python +def multiply_duck(a, b): + return a * b +``` + +At low level **METACALL** must always know the types to do the call. This types can be inferred statically or dynamically and this has implications over the call model. + +In the first example, we can simply call the function without specifying the types. + +```c +metacall("multiply_type", 3, 4); // 12 +``` + +As the signature is already know the literal values `3` and `4` can be converted into **METACALL** values automatically. Note that in this case, as literal values are provided, if we pass a double floating point, the memory representation of the value will be corrupted as there is no possible way to detect input values and cast them to the correct target values. + +In the second example, the values are not know. If we use the same API to call the function, **METACALL** will not be able to call correctly the function as its types are not know. To allow calls to duck typed functions the developer must specify the value types he is passing to the function. + +```c +const enum metacall_value_id multiply_types[] = +{ + METACALL_INT, METACALL_INT +}; + +metacallt("multiply_duck", multiply_types, 3, 4); // 12 +``` + +This method allows to pass different value types to the same function. The following call would be valid too. + +```c +const enum metacall_value_id multiply_types[] = +{ + METACALL_DOUBLE, METACALL_DOUBLE +}; + +metacallt("multiply_duck", multiply_types, 3.0, 4.0); // 12.0 +``` + +### 5.3 Plugins + +**METACALL** has a plugin architecture implemented at multiple levels. + +- Loaders implement a layer of plugins related to the run-times. + +- Serials implement a layer of (de)serializers in order to transform input (arguments) or output (return value) of the calls into a generic format. + +- Detours is another layer of plugins focused on low level function interception (hooks). + +Each plugin is a piece of software that can be dynamically loaded into the **METACALL** core, used and unloaded when it is not needed anymore. + +#### 5.3.1 Loaders + +Loaders are responsible for embedding run-times into **METACALL**. Each loader has the following interface. + +```c +typedef struct loader_impl_interface_type +{ + loader_impl_interface_initialize initialize; + loader_impl_interface_execution_path execution_path; + loader_impl_interface_load_from_file load_from_file; + loader_impl_interface_load_from_memory load_from_memory; + loader_impl_interface_load_from_package load_from_package; + loader_impl_interface_clear clear; + loader_impl_interface_discover discover; + loader_impl_interface_destroy destroy; + +} * loader_impl_interface; +``` + +A loader must implement it to be considered a valid loader. + +- `initialize` starts up the run-time. +- `execution_path` defines a new import path to the run-time. +- `load_from_file` loads a code from file into the run-time and returns a handle which represents it. +- `load_from_memory` loads a code from memory into the run-time and returns a handle which represents it. +- `load_from_package` loads a code from a compiled library or package into the run-time and returns a handle which represents it. +- `clear` unloads a handle from the run-time. +- `discover` inspects a handle previously loaded. +- `destroy` shutdowns the run-time. + +##### 5.3.1.1 Python + +##### 5.3.1.2 NodeJS + +##### 5.3.1.3 JavaScript + +##### 5.3.1.4 C# + +##### 5.3.1.5 Ruby + +##### 5.3.1.6 Mock + +##### 5.3.1.7 File + +#### 5.3.2 Serials + +##### 5.3.2.1 MetaCall + +##### 5.3.2.2 RapidJSON + +#### 5.3.3 Detours + +##### 5.3.3.1 PLTHook + +### 5.4 Ports + +### 5.5 Serialization + +### 5.6 Memory Layout + +### 5.7 Fork Model + +**METACALL** implements a fork safe model. This means if **METACALL** is running in any program instance, the process which is running can be forked safely at any moment of the execution. This fact has many implications at design, implementation and use levels. But the whole **METACALL** architecture tries to remove all responsibility from the developer and make this transparent. + +To understand the **METACALL** fork model, first of all we have to understand the implications of the forking model in operative systems and the difference between [fork-one and fork-all models](https://docs.oracle.com/cd/E37838_01/html/E61057/gen-1.html). + +The main difference between fork-one and fork-all is that in fork-one only the thread which called the fork is preserved after the fork (i.e. gets cloned). In fork-all model, all threads are preserved after cloning. POSIX uses fork-one model, meanwhile Oracle Solaris use the fork-all model. + +Because of fork-one model, forking a running run-time like NodeJS (which has a thread pool) implies that in the child process the thread pool will be almost dead except the thread which did the fork call. So NodeJS run-time cannot continue the execution anymore and the event-loop enters into a deadlock state. + +When a fork is done, the status of the execution is lost by the moment. **METACALL** is not able to preserve the state when a fork is done. Some run-times do not allow to preserve the internal state. For example, the bad design[[0]](https://github.com/nodejs/node/issues/23265)[[1]](https://github.com/nodejs/node/issues/23265#issuecomment-452690239)[[2]](https://github.com/nodejs/node/issues/23265#issuecomment-496873739)[[3]](https://github.com/nodejs/node/issues/23265#issuecomment-496878712)[[4]](https://github.com/nodejs/node/issues/23265#issuecomment-496910654)[[5]](https://github.com/nodejs/node/issues/23265#issuecomment-496918901) of NodeJS does not allow to manage the thread pool from outside, so it cannot be preserved after a fork. + +Because of these restrictions, **METACALL** cannot preserve the status of the run-times. In the future this model will be improved to maintain consistency and preserve the execution state of the run-times making **METACALL** more robust. + +Although the state is not preserved, fork safety is. The mechanism **METACALL** uses to allow fork safety is described in the following enumeration. + +1. Intercept fork call done by the program where **METACALL** is running. + +2. Shutdown all run-times by means of unloading all loaders. + +3. Execute the real fork function. + +4. Restore all run-times by means of reloading all loaders. + +5. Execute user defined fork callback if any. + +To achieve this, **METACALL** hooks fork primitives depending on the platform. + +- `fork` on POSIX systems. +- `RtlCloneUserProcess` on Windows systems. + +If you use `clone` instead of `fork` to spawn a new process in a POSIX system, **METACALL** won't catch it. + +Whenever you call a to a cloning primitive **METACALL** intercepts it by means of [**`detour`**](/source/detour). Detours is a way to intercept functions at low level by editing the memory and introducing a jump over your own function preserving the address of the old one. **METACALL** uses this method instead of POSIX `pthread_atfork` for three main reasons. + +- The first one is that `pthread_atfork` is only supported by POSIX systems. So it is not a good solution because of the philosophy of **METACALL** is to be as cross-platform as possible. + +- The second is that `pthread_atfork` has a [bug in the design of the standard](https://stackoverflow.com/a/6605487). It was designed to solve a problem which cannot be solved with `pthread_atfork` itself. This means that even having the control of NodeJS thread pool, it will not be possible to restore the [mutexes](https://github.com/nodejs/node/blob/v8.x/src/node_platform.cc) in the child process. The only possibility is to re-implement the thread pool of NodeJS with async safe primitives like a semaphore. Async safe primitives will be able to work in the child process handler. But this is not possible as it enters in conflict with the design decision of to not modify the run-times. + +- The third one is that the mechanism of `pthread_atfork` also [will be deprecated](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html) because of second reason. + > The `pthread_atfork()` function may be formally deprecated (for example, by shading it OB) in a future version of this standard. + +Detours model is not safe. It is platform dependant and implies that the program modifies the memory of itself during the execution which is not safe at all and can induce bugs or security flaws if it is not done correctly. But because of limitations of run-times, there is not another alternative to solve the problem of fork safety. + +Usually the developer is the same who does the fork, but it may be possible that **METACALL** is embedded into a larger application and the developer is in the middle between the application code and **METACALL** so it is impossible to control when a fork is done. Because of this the developer can register a callback by means of [**`metacall_fork`**](/source/metacall/include/metacall/metacall_fork.h) to know when a fork is executed to do the actions needed after the fork, for example, re-loading all previous code and restore the state of the run-times. This gives a partial solution to the problem of losing the state when doing a fork. + +### 5.8 Threading Model + +The threading model is still experimental. We are discovering the best ways of designing and implementing it, so it may vary over time. At the moment of writing (check the commit history), there are some concerns that are already known and parts of the design already achieved thanks to the NodeJS event loop nature. + +The Node Loader is designed in a way in which the V8 instance is created in a new thread, and from there the event loop "blocks" that thread until the execution. Recent versions of N-API (since NodeJS 14.x) allow you to have control and reimplement your own event loop thanks to the new embedder API. But when this project started and NodeJS loader was implemented, only NodeJS 8.x exist. So the only option (without reimplementing part of NodeJS, because it goes against one design decisions of the project) was to use `node::Start`, a call that blocks your thread while executing the event loop. This also produces a lot of problems, because of lack of control over NodeJS, but they are not directly related to the thread model. + +To overcome the blocking nature of `node::Start`, the event loop is launched in a separated thread, and all calls to the loader are executed via submission to the event loop in that thread. In the first implementation, it was done using `uv_async_t`, but in the current implementation (since NodeJS 10.x), with thread safe mechanisms that allow you to enqueue safely into the event loop thanks to the new additions to the N-API. The current thread where the call is done waits with a condition `uv_cond_t` upon termination of the submission and resolution of the call. + +This solution of waiting to the call with the condition, introduces new problems. For completely async calls, there is no problem at all, but for synchronous calls, it can deadlock. For example, when calling recursively to the same synchronous function via **METACALL**, in the second call it will try to block twice and deadlock the thread. So in order to solve this an atomic variable was added in addition to a variable storing the thread id of the V8 thread. With this, recursive calls can be detected, and instead of blocking and enqueueing them, it is possible to call directly and safely to the function because we are already in the V8 thread when the second iteration is done. + +This solves all (known) issues related to NodeJS threading model **if and only if** you use **METACALL** from C/C++ or Rust as a library, and you don't mix languages. This means, you use directly the low level API directly, and you do not use any `Port` or you mix this with other languages, doing calls in between. You can still have a chance to generate deadlocks if your software uses incorreclty the API. For example, you use one condition which gets released in an async callback (a lambda in the argument of the call to `metacall_await`) and your JS code never resolves properly that promise. + +If you use the CLI instead, and your host language is Python or any other (which does not allow to use you the low level API), and you want to load scripts from other languages, you have to use **METACALL** through `Ports`. Ports provide a high abstraction of the low level API and allow you to load and call functions of other languages. Here is where the fun begins. + +There are few considerations we must take into account. In order to explain this we are going to use a simple example first, using Python and NodeJS. Depending on the runtime, there are different mechanisms to handle threads and thread safety: + +- Python: + + 1. Python uses a Global Interpreter Lock (GIL), which can be acquired from different threads in order to do thread safe calls. This can be problematic due to deadlocks. + 2. Python event loop can be decoupled from Python interpreter thread by using Python Thread API (work in progress: https://github.com/metacall/core/pull/64). This fact simplifies the design. + 3. Python can run multiple interpreter instances, starting from newer versions (not implemented yet). + +- NodeJS: + 1. NodeJS uses a submission queue and does not suffer from a global mutex like Python. + 2. NodeJS V8 thread is coupled to the event loop (at least with the current version used in **METACALL**, and it is difficult to have control over it). + 3. NodeJS can execute multiple V8 threads with the multi-isolate library from the latest versions of V8 (not implemented yet). + +Once these concerns are clear, now we can go further and inspect some cases where we can find deadlocks or problems related to them: + +1. **NodeJS is the host language**, and it launches the Python interprer in the V8 thread: + +![Threading Model NodeJS Python](https://github.com/metacall/core/blob/master/docs/diagrams/threading-model-nodejs-python.png) + +This model is relatively safe because Node Loader is completely reentrant, and Python GIL too. This means you can do recursive calls safely, and all those calls will always happen in V8. Even if we do callbacks, all of them will happen in the same thread, so there aren't potential deadlocks. This means we can safely use a functional library from NodeJS, and it won't deadlock. For example: [Using Fn.py from NodeJS](https://github.com/metacall/fn.py-javascript-example). + +But there is a problem when we try to destroy the loaders. Python interpreter does not allow to be destroyed from a different thread where it was launched. This means, if we destroy the Node Loader first, then it will be impossible to destroy the Python Loader, because the V8 thread has been finished. We must destroy the Loaders in order and in the correct thread. This means if we try to destroy Node Loader, during its destruction in the V8 thread, we must destroy Python Loader and any other loader that has been initialized in that thread. + +As a result, each loader must use the following instructions: + +- When the loader has finished the initialization, it must register its initialization order. It will record internally the current thread id too. + + ```c + loader_initialization_register(impl); + ``` + +- When the loader is going to be destroyed, but before destroy starts, the children must be destroyed in a recursive way, so the whole tree can be iterated properly in order. + + ```c + loader_unload_children(); + ``` + +The result of the current destruction model is that: **`metacall_initialize` and `metacall_destroy` must be done from the same thread**. This should not be a problem for developers using the CLI. But embedders must take this into account. + +2. **Python is the host language**, and it launches NodeJS in a new (V8) thread: + [TODO: Explain why callbacks deadlock in this context] + +In order to end this section, here's a list of ideas that are not completely implemented yet, but they are in progress: + +- Lock free data structures for holding the functions. +- Asynchronous non-deadlocking, non-stack growing callbacks between runtimes (running multiple event loops between languages). This will solve the second case where Python is the host language and deadlocks because of NodeJS event loop nature. +- Support for multi-isolate and multiple interpreters instances. + +## 5. Application Programming Interface (API) + +## 6. Build System + +Follow these steps to build and install **METACALL** manually. + +```sh +git clone https://github.com/metacall/core.git +mkdir core/build && cd core/build +cmake .. +# Unix (Linux and MacOs) +sudo HOME="$HOME" cmake --build . --target install +# Windows (or when installing to a path with permissions) +cmake --build . --target install +``` + +### 6.1 Build Options + +These options can be set using **`-D`** prefix when configuring CMake. For example, the following configuration enables the build of Python and Ruby loaders. + +```sh +cmake -DOPTION_BUILD_LOADERS_PY=On -DOPTION_BUILD_LOADERS_RB=On .. +``` + +Available build options are the following ones. + +| Build Option | Description | Default Value | +| :-------------------------: | ------------------------------------------------------ | :-----------: | +| **BUILD_SHARED_LIBS** | Build shared instead of static libraries. | ON | +| **OPTION_SELF_CONTAINED** | Create a self-contained install with all dependencies. | OFF | +| **OPTION_BUILD_TESTS** | Build tests. | ON | +| **OPTION_BUILD_BENCHMARKS** | Build benchmarks. | OFF | +| **OPTION_BUILD_DOCS** | Build documentation. | OFF | +| **OPTION_BUILD_EXAMPLES** | Build examples. | ON | +| **OPTION_BUILD_LOADERS** | Build loaders. | ON | +| **OPTION_BUILD_SCRIPTS** | Build scripts. | ON | +| **OPTION_BUILD_SERIALS** | Build serials. | ON | +| **OPTION_BUILD_DETOURS** | Build detours. | ON | +| **OPTION_BUILD_PORTS** | Build ports. | OFF | +| **OPTION_FORK_SAFE** | Enable fork safety. | OFF | +| **OPTION_THREAD_SAFE** | Enable thread safety. | OFF | +| **OPTION_COVERAGE** | Enable coverage. | OFF | +| **CMAKE_BUILD_TYPE** | Define the type of build. | Release | + +It is possible to enable or disable concrete loaders, script, ports, serials or detours. For building use the following options. + +| Build Option Prefix | Build Option Suffix | +| :-----------------------: | --------------------------------------------------------------------- | +| **OPTION_BUILD_LOADERS_*** | `C` `JS` `CS` `MOCK` `PY` `JSM` `NODE` `RB` `FILE` | +| **OPTION_BUILD_SCRIPTS_*** | `C` `CS` `JS` `NODE` `PY` `RB` `JAVA` | +| **OPTION_BUILD_SERIALS_*** | `METACALL` `RAPID_JSON` | +| **OPTION_BUILD_DETOURS_*** | `PLTHOOK` | +| **OPTION_BUILD_PORTS_*** | `CS` `CXX` `D` `GO` `JAVA` `JS` `LUA` `NODE` `PHP` `PL` `PY` `R` `RB` | + +To format the entire C/C++ codebase use: + +```sh +cmake --build build --target clang-format +``` + +Be aware that this target won't exist if clang-format was not installed when cmake was last run. + +### 6.2 Coverage + +In order to run code coverage and obtain html reports use the following commands (assuming you just clonned the repository): + +```sh +git clone https://github.com/metacall/core.git +mkdir core/build && cd core/build +cmake -DCMAKE_BUILD_TYPE=Debug -DOPTION_COVERAGE=On .. +make -j$(NPROC) +ctest +ctest -T Coverage +gcovr -r ../source/ . --html-details coverage.html +``` + +The output reports will be generated in `${CMAKE_BINARY_DIR}/coverage.html` in html format. + +### 6.3 Debugging + +For debugging memory leaks, undefined behaviors and other related problems, the following compile options are provided: + +| Build Option | Description | Default Value | +| :--------------------------------: | --------------------------------------------------------- | :-----------: | +| **OPTION_TEST_MEMORYCHECK** | Enable Valgrind with memcheck tool for the tests. | OFF | +| **OPTION_BUILD_ADDRESS_SANITIZER** | Build with AddressSanitizer family (GCC, Clang and MSVC). | OFF | +| **OPTION_BUILD_THREAD_SANITIZER** | Build with ThreadSanitizer family (GCC, Clang and MSVC). | OFF | +| **OPTION_BUILD_MEMORY_SANITIZER** | Build with MemorySanitizer family (Clang and MSVC). | OFF | + +All options are mutually exclusive. Valgrind is not compatible with AddressSanitizer and AddressSanitizer is not compatible with ThreadSanitizer and AddressSanitizer with MemorySanitizer. Some run-times may fail if they are not compiled with AddressSanitizer too, for example NetCore. Due to this, tests implying may fail with signal 11. The same problem happens with Valgrind, due to that, some tests are excluded of the memcheck target. + +For running all tests with Valgrind, enable the `OPTION_TEST_MEMORYCHECK` flag and then run: + +```sh +make memcheck +``` + +For runing a test (or all) with AddressSanitizer or ThreadSanitizer, enable the `OPTION_BUILD_ADDRESS_SANITIZER` or `OPTION_BUILD_THREAD_SANITIZER` flags respectively and then run: + +```sh +# Run one test +make py_loader rb_loader node_loader metacall-node-port-test # Build required dependencies and a test +ctest -VV -R metacall-node-port-test # Run one test (verbose) + +# Run all +make +ctest +``` + +For running other Valgrind's tools like helgrind or similar, I recommend running them manually. Just run one test with `ctest -VV -R metacall-node-port-test`, copy the environment variables, and configure the flags by yourself. + +### 6.4 Build on Cloud - Gitpod + +Instead of configuring a local setup, you can also use [Gitpod](https://www.gitpod.io/), an automated cloud dev environment. + +Click the button below. A workspace with all required environments will be created. + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/metacall/core) + +> To use it on your forked repo, edit the 'Open in Gitpod' button url to `https://gitpod.io/#https://github.com//core` + +## 7. Platform Support + +The following platforms and architectures have been tested and are known to work correctly with all plugins of **METACALL**. + +| Operative System | Architecture | Compiler | +| :--------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :---------: | +| **`ubuntu`** | **`amd64`** | **`gcc`** | +| **`debian`** | **`amd64`** **`amd64/v2`** **`amd64/v3`** **`386`** **`arm64`** **`riscv64`** **`ppc64le`** **`arm/v7`** **`arm/v6`** **`loong64`** | **`gcc`** | +| **`macos`** | **`amd64`** **`arm64`** | **`clang`** | +| **`windows`** | **`x86`** **`x64`** | **`msvc`** | + +### 7.1 Docker Support + +To provide a reproducible environment **METACALL** is also distributed under Docker on [DockerHub](https://hub.docker.com/r/metacall/core). Current images are based on `debian:bookworm-slim` for `amd64` architecture. + +For pulling the **METACALL** `latest` image containing the runtime, use: + +```sh +docker pull metacall/core +``` + +For pulling a specific image depending on the tag, use: + +- **METACALL** `deps` image. Includes all dependencies for development: + +```sh +docker pull metacall/core:deps +``` + +- **METACALL** `dev` image. Includes all dependencies, headers and libraries for development: + +```sh +docker pull metacall/core:dev +``` + +- **METACALL** `runtime` image. Includes all dependencies and libraries for runtime: + +```sh +docker pull metacall/core:runtime +``` + +- **METACALL** `cli` image. Includes all dependencies and libraries for runtime and the CLI as entry point (equivalent to `latest`): + +```sh +docker pull metacall/core:cli +``` + +### 7.1.1 Docker Development + +It is possible to develop **METACALL** itself or applications using **METACALL** as standalone library with Docker. The `dev` image can be used for development. It contains all dependencies with all run-times installed with the code, allowing debugging too. + +Use the following commands to start developing with **METACALL**: + +```sh +mkdir -p $HOME/metacall +code $HOME/metacall +``` + +We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. + +```sh +docker pull metacall/core:dev +docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:dev /bin/bash +``` + +Inside docker terminal you can run `python` or `ruby` command to test what you are developing. You can also run `metacallcli` to test (load, clear, inspect and call). + +### 7.1.2 Docker Testing + +An alternative for testing is to use a reduced image that includes the runtime and also the CLI. This alternative allows fast prototyping and CLI management in order to test and inspect your own scripts. + +Use the following commands to start testing with **METACALL**: + +```sh +mkdir -p $HOME/metacall +code $HOME/metacall +``` + +We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. + +```sh +docker pull metacall/core:cli +docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:cli +``` + +After the container is up, it is possible to load any script contained in host folder `$HOME/metacall`. If we have a `script.js` inside the folder, we can just load it (each line beginning with `>` is the input command): + +`script.js` + +```js +function sum(left, right) { + return left + right; +} + +module.exports = { + sum, +}; +``` + +`Command Line Interface` + +```sh +> load node script.js +Script (script.js) loaded correctly +> inspect +runtime node { + module script { + function sum(left, right) + } +} +runtime __metacall_host__ +> call sum(3, 5) +8.0 +> exit +``` + +Where `script.js` is a script contained in host folder `$HOME/metacall` that will be loaded on the CLI after starting up the container. Type `help` to see all available CLI commands. + +## 8. Benchmarks + +**METACALL** provides benchmarks for multiple operative systems in order to improve performance iteratively, those can be found in GitHub Pages: + +| Operative System | URL | +| :-----------------: | :--------------------------------------------------: | +| **`ubuntu-latest`** | https://metacall.github.io/core/bench/ubuntu-latest/ | +| **`macos-latest`** | https://metacall.github.io/core/bench/macos-latest/ | +| **`windows-2022`** | https://metacall.github.io/core/bench/windows-2022/ | +| **`windows-2025`** | https://metacall.github.io/core/bench/windows-2025/ | + +## 9. License + +**METACALL** is licensed under **[Apache License Version 2.0](/LICENSE)**. + +> Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia <> +> +> Licensed under the Apache License, Version 2.0 (the "License"); +> you may not use this file except in compliance with the License. +> You may obtain a copy of the License at +> +> http://www.apache.org/licenses/LICENSE-2.0 +> +> Unless required by applicable law or agreed to in writing, software +> distributed under the License is distributed on an "AS IS" BASIS, +> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +> See the License for the specific language governing permissions and +> limitations under the License. diff --git a/docs/api-docs/CMakeLists.txt b/docs/api-docs/CMakeLists.txt index 47e17b927b..767c47b1e6 100644 --- a/docs/api-docs/CMakeLists.txt +++ b/docs/api-docs/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Define dependencies set(depends ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::adt ${META_PROJECT_NAME}::dynlink + ${META_PROJECT_NAME}::adt ${META_PROJECT_NAME}::dynlink ${META_PROJECT_NAME}::plugin ${META_PROJECT_NAME}::detour ${META_PROJECT_NAME}::filesystem ${META_PROJECT_NAME}::reflect ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall) # TODO: add loaders, ports and examples (automatize with module names) diff --git a/docs/diagrams/threading-model-nodejs-python.drawio b/docs/diagrams/threading-model-nodejs-python.drawio new file mode 100644 index 0000000000..0ebc5bc73a --- /dev/null +++ b/docs/diagrams/threading-model-nodejs-python.drawio @@ -0,0 +1 @@ +7VjLctowFP0aL5vxAxuzDCRtFkmHKZ02rDrCUmwlwmKEANOv75Utv01JUhMymTIsdO/V85xz9bDhTJbJF4FW0R3HhBm2iRPDuTJs2xrYtqH+Jt5nHm/kZY5QUKwrlY4Z/U2009TeDcVkXasoOWeSrurOgMcxCWTNh4Tgu3q1B87qo65QSFqOWYBY2/uTYhlpr2WaZeCG0DDSQ/uuDixQ8BQKvon1eIbtPKS/LLxEeV+6/jpCmO8qLufacCaCc5mVlsmEMIVtDlvW7vOBaDFvQWL5rAY3v9jmbm16j+P5dD653yXb+0+Wk3WzRWxD8nWks5X7HKF0jUT1YhrOeBdRSWYrFKjoDjQBvkguGVgWFB8oYxPOuEjbAiLECwLwr6XgT6QSwcPRwlQd6gkQIUlycGlWARgIkfAlkWIPVXQDJxeT1uBg6Gb2rmTUyXmLKmRaQ+1EWkVh0XeJJBQ0mC8BdtABrMdg3PECCqEq/PChwvdIEITzGIxVhFtEAECyjnYd1ZjHpEGBdiFGwxjMAAAl4B8ruCkkwaUOLCnGaphOeusC6IMvu8GX3+Zr1EGXfTK2rI+YBgWGZ0yDFq7QLwLhpS0Bz+Ticf1vUPcA3MCpA+eYbeAsuwM471S42cf1SGJ8qc4/ldYMrdc0qONCEirvFYQXrrbmlchVotFNjX0ttwluHZkNJGEifCMCcoz5NuIVRLuUmPsEYUjSbX0aXSjrEaacwgQLQt3mBmM1iMqmr1tVD81GR4NRQxmjRkcSiZDIVkcp6cWyX68Dt6WDr3ADA88tRxh28j43KewSHw+6NinfXjied6Jc69ik3jbXvB5yLYaJVJJNmfM8wZRRpltqvTzfMp39ZRX+OfOtmSYD97X51khct3nknzjfhv1qwRvW1HBhWsMjikitKREUVqJuan3LZPQhZOK655WJ35LJdC8jHp9mY4Y3pX3g9ugtPLenjdlpYuqee2MedSRj9kzCdJu/kr5tFvsK7MUzqlKlXzL8gHSTsfBdJeheyHDN90aG1XUlbTxo7xCN/z9pDz4h3vZJ67ZPLjhAZtrkAvarkMeIXZfeBixlnVvOV5qfRyLlXn/GQxvJ39ejw3nmMZhVbLP57APuADdgll/0stOm/GzqXP8B \ No newline at end of file diff --git a/docs/diagrams/threading-model-nodejs-python.png b/docs/diagrams/threading-model-nodejs-python.png new file mode 100644 index 0000000000..3b77d8baff Binary files /dev/null and b/docs/diagrams/threading-model-nodejs-python.png differ diff --git a/docs/style/source/metacall_style.tex b/docs/style/source/metacall_style.tex index 88000761d3..aee41e857c 100644 --- a/docs/style/source/metacall_style.tex +++ b/docs/style/source/metacall_style.tex @@ -84,7 +84,6 @@ \section{Files \& Directory Tree} \dirtree{% .1 \treefolder{red}{build}. .1 \treefolder{red}{cmake}. -.1 \treefolder{red}{codegeneration}. .1 \treefolder{red}{data}. .1 \treefolder{red}{deploy}. .2 \treefolder{red}{images}. diff --git a/githooks/unix/canonicalize_filename.sh b/githooks/unix/canonicalize_filename.sh new file mode 100644 index 0000000000..5eecabf5bc --- /dev/null +++ b/githooks/unix/canonicalize_filename.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +# Provide the canonicalize filename (physical filename with out any symlinks) +# like the GNU version readlink with the -f option regardless of the version of +# readlink (GNU or BSD). + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +########################################################### +# There should be no need to change anything below this line. + +# Canonicalize by recursively following every symlink in every component of the +# specified filename. This should reproduce the results of the GNU version of +# readlink with the -f option. +# +# Reference: http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac +canonicalize_filename () { + local target_file="$1" + local physical_directory="" + local result="" + + # Need to restore the working directory after work. + local working_dir="`pwd`" + + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + + # Iterate down a (possible) chain of symlinks + while [ -L "$target_file" ] + do + target_file="$(readlink -- "$target_file")" + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + done + + # Compute the canonicalized name by finding the physical path + # for the directory we're in and appending the target file. + physical_directory="`pwd -P`" + result="$physical_directory/$target_file" + + # restore the working directory after work. + cd -- "$working_dir" + + echo "$result" +} diff --git a/githooks/unix/pre-commit b/githooks/unix/pre-commit new file mode 100644 index 0000000000..5afa02a1b4 --- /dev/null +++ b/githooks/unix/pre-commit @@ -0,0 +1,50 @@ +#!/bin/sh +# Git pre-commit hook that runs multiple hooks specified in $HOOKS. +# Make sure this script is executable. Bypass hooks with git commit --no-verify. + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + + +########################################################### +# CONFIGURATION: +# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder +# as this script. Hooks should return 0 if successful and nonzero to cancel the +# commit. They are executed in the order in which they are listed. +#HOOKS="pre-commit-compile pre-commit-uncrustify" +HOOKS="pre-commit-add-contributor pre-commit-clang-format" +########################################################### +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# Absolute path to this script, e.g. /home/user/bin/foo.sh +SCRIPT="$(canonicalize_filename "$0")" + +# Absolute path this script is in, thus /home/user/bin +SCRIPTPATH="$(dirname -- "$SCRIPT")" + + +for hook in $HOOKS +do + echo "Running hook: $hook" + # run hook if it exists + # if it returns with nonzero exit with 1 and thus abort the commit + if [ -f "$SCRIPTPATH/$hook" ]; then + "$SCRIPTPATH/$hook" + if [ $? != 0 ]; then + exit 1 + fi + else + echo "Error: file $hook not found." + echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable." + echo "You can disable it by removing it from the list in $SCRIPT." + echo "You can skip all pre-commit hooks with --no-verify (not recommended)." + exit 1 + fi +done diff --git a/githooks/unix/pre-commit-add-contributor b/githooks/unix/pre-commit-add-contributor new file mode 100644 index 0000000000..6dbb1972dd --- /dev/null +++ b/githooks/unix/pre-commit-add-contributor @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +if git log --pretty="%ae%n%ce" | sort | uniq | grep -q "$(git config user.email)"; then + exit 0 +else + if git log --pretty="%an%n%cn" | sort | uniq | grep -q "$(git config user.name)"; then + exit 0 + else + + expected=$(printf '%s <%s>' "$(git config user.name)" "$(git config user.email)"); + + if grep -q "$expected" CONTRIBUTORS; then + exit 0 # Already in the file but first time commit in the git history + else + echo "Please add yourself (copy the next line) to the CONTRIBUTORS.md file."; + echo $expected + exit 1 + fi + + fi +fi diff --git a/githooks/unix/pre-commit-clang-format b/githooks/unix/pre-commit-clang-format new file mode 100644 index 0000000000..7c76ce8e2b --- /dev/null +++ b/githooks/unix/pre-commit-clang-format @@ -0,0 +1,256 @@ +#!/usr/bin/env bash + +# git pre-commit hook that runs a clang-format stylecheck. +# Features: +# - abort commit when commit does not comply with the style guidelines +# - create a patch of the proposed style changes +# Modifications for clang-format by rene.milk@wwu.de + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +# Some quality of life modifications made for Godot Engine. + +# Some modifs for Metacall + +################################################################## +# SETTINGS +# Set path to clang-format binary. + +# To get consistent formatting, we recommend contributors to use the same +# clang-format version as CI. +RECOMMENDED_CLANG_FORMAT_MAJOR="11" + +function find_clang_format() { + for version in 11 12 13 14 15; do + program=`which clang-format-$version 2>/dev/null` + if [ ! -z "$program" ]; then + echo "$program" + return + fi + done + + program=`which clang-format 2>/dev/null` + + if [ ! -z "$program" ]; then + version="$($program --version | rev | cut -d' ' -f1 | rev)" + major="$(echo "$version" | cut -d'.' -f1)" + + if [ "$major" -ge "$RECOMMENDED_CLANG_FORMAT_MAJOR" ]; then + echo "$program" + return + fi + fi +} + +CLANG_FORMAT=$(find_clang_format) + +# Remove any older patches from previous commits. Set to true or false. +DELETE_OLD_PATCHES=false + +# Only parse files with the extensions in FILE_EXTS. Set to true or false. +# If false every changed file in the commit will be parsed with clang-format. +# If true only files matching one of the extensions are parsed with clang-format. +PARSE_EXTS=true + +# File types to parse. Only effective when PARSE_EXTS is true. +FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx" + +# Use pygmentize instead of cat to parse diff with highlighting. +# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac) +PYGMENTIZE=`which pygmentize 2>/dev/null` +if [ ! -z "$PYGMENTIZE" ]; then + READER="pygmentize -l diff" +else + READER=cat +fi + +# Path to zenity +ZENITY=`which zenity 2>/dev/null` + +# Path to xmessage +XMSG=`which xmessage 2>/dev/null` + +# Path to powershell (Windows only) +PWSH=`which powershell 2>/dev/null` + +################################################################## +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# check whether the given file matches any of the set extensions +matches_extension() { + local filename=$(basename "$1") + local extension=".${filename##*.}" + local ext + + for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done + + return 1 +} + +# necessary check for initial commit +if git rev-parse --verify HEAD >/dev/null 2>&1 ; then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +if [ ! -x "$CLANG_FORMAT" ] ; then + message="Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR.x.x. or superior" + + if [ ! -t 1 ] ; then + if [ -x "$ZENITY" ] ; then + $ZENITY --error --title="Error" --text="$message" + exit 1 + elif [ -x "$XMSG" ] ; then + $XMSG -center -title "Error" "$message" + exit 1 + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "$message" + exit 1 + fi + fi + printf "$message\n" + printf "Set the correct path in $(canonicalize_filename "$0").\n" + exit 1 +fi + +# create a random filename to store our generated patch +prefix="pre-commit-clang-format" +suffix="$(date +%s)" +patch="/tmp/$prefix-$suffix.patch" + +# clean up any older clang-format patches +$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch + +# create one patch containing all changes to the files +git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; +do + # ignore thirdparty folder + #if grep -q "thirdparty" <<< $file; then + # continue; + #fi + + # Only files from source folder + if grep -q "source" <<< $file; then + : # Correct + else + continue; + fi + + + # ignore file if we do check for file extensions and the file + # does not match any of the extensions specified in $FILE_EXTS + if $PARSE_EXTS && ! matches_extension "$file"; then + continue; + fi + + # clang-format our sourcefile, create a patch with diff and append it to our $patch + # The sed call is necessary to transform the patch from + # --- $file timestamp + # +++ - timestamp + # to both lines working on the same file and having a/ and b/ prefix. + # Else it can not be applied with 'git apply'. + "$CLANG_FORMAT" -style=file "$file" | \ + diff -u "$file" - | \ + sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch" +done + +# if no patch has been generated all is ok, clean up the file stub and exit +if [ ! -s "$patch" ] ; then + printf "Files in this commit comply with the clang-format rules.\n" + rm -f "$patch" + exit 0 +fi + +# a patch has been created, notify the user and exit +printf "\nThe following differences were found between the code to commit " +printf "and the clang-format rules:\n\n" + +if [ -t 1 ] ; then + $READER "$patch" + printf "\n" + # Allows us to read user input below, assigns stdin to keyboard + exec < /dev/tty + terminal="1" +else + cat "$patch" + printf "\n" + # Allows non zero zenity/powershell output + set +e + terminal="0" +fi + +while true; do + if [ $terminal = "0" ] ; then + if [ -x "$ZENITY" ] ; then + ans=$($ZENITY --text-info --filename="$patch" --width=800 --height=600 --title="Do you want to apply that patch?" --ok-label="Apply" --cancel-label="Do not apply" --extra-button="Apply and stage") + if [ "$?" = "0" ] ; then + yn="Y" + else + if [ "$ans" = "Apply and stage" ] ; then + yn="S" + else + yn="N" + fi + fi + elif [ -x "$XMSG" ] ; then + $XMSG -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + else + printf "Error: zenity, xmessage, or powershell executable not found.\n" + exit 1 + fi + else + read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + fi + case $yn in + [Yy] ) git apply $patch; + printf "The patch was applied. You can now stage the changes and commit again.\n\n"; + break + ;; + [Nn] ) printf "\nYou can apply these changes with:\n git apply $patch\n"; + printf "(may need to be called from the root directory of your repository)\n"; + printf "Aborting commit. Apply changes and commit again or skip checking with"; + printf " --no-verify (not recommended).\n\n"; + break + ;; + [Ss] ) git apply $patch; + git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; + do git add $file; + done + printf "The patch was applied and the changed files staged. You can now commit.\n\n"; + break + ;; + * ) echo "Please answer yes or no." + ;; + esac +done +exit 1 # we don't commit in any case diff --git a/githooks/unix/pre-push b/githooks/unix/pre-push new file mode 100644 index 0000000000..f8f287e478 --- /dev/null +++ b/githooks/unix/pre-push @@ -0,0 +1,50 @@ +#!/bin/bash +# Git pre-commit hook that runs multiple hooks specified in $HOOKS. +# Make sure this script is executable. Bypass hooks with git commit --no-verify. + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + + +########################################################### +# CONFIGURATION: +# pre-push hooks to be executed. They should be in the same .git/hooks/ folder +# as this script. Hooks should return 0 if successful and nonzero to cancel the +# commit. They are executed in the order in which they are listed. +#HOOKS="pre-push-compile pre-push-uncrustify" +HOOKS="pre-push-update-version" +########################################################### +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# Absolute path to this script, e.g. /home/user/bin/foo.sh +SCRIPT="$(canonicalize_filename "$0")" + +# Absolute path this script is in, thus /home/user/bin +SCRIPTPATH="$(dirname -- "$SCRIPT")" + + +for hook in $HOOKS +do + echo "Running hook: $hook" + # run hook if it exists + # if it returns with nonzero exit with 1 and thus abort the commit + if [ -f "$SCRIPTPATH/$hook" ]; then + "$SCRIPTPATH/$hook" ${@:1:2} + if [ $? != 0 ]; then + exit 1 + fi + else + echo "Error: file $hook not found." + echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable." + echo "You can disable it by removing it from the list in $SCRIPT." + echo "You can skip all pre-push hooks with --no-verify (not recommended)." + exit 1 + fi +done diff --git a/githooks/unix/pre-push-update-version b/githooks/unix/pre-push-update-version new file mode 100644 index 0000000000..187e6a84f9 --- /dev/null +++ b/githooks/unix/pre-push-update-version @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Fetch all tags first +git fetch --tags $1 + +# Get the latest tag version +GIT_TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1` | cut -c2-) +FILE_VERSION=$(cat VERSION) + +# Checkout if the file version matches the last tag +if [ "${GIT_TAG_VERSION}" != "${FILE_VERSION}" ]; then + # Undo the last tag, update the version file and commit as the new tag + git tag -d v${GIT_TAG_VERSION} + printf "${GIT_TAG_VERSION}" &> VERSION + git add VERSION + git commit -m "Update version to v${GIT_TAG_VERSION}." + git tag v${GIT_TAG_VERSION} + printf "\033[0;31mYour push has been rejected in order to update VERSION.\033[0m\n" + printf "\033[0;32mPush again with:\033[0m git push $1 `git rev-parse --abbrev-ref HEAD` v${GIT_TAG_VERSION}\n" + exit 1 +fi diff --git a/.github/ISSUE_TEMPLATE.md b/githooks/win32/TODO similarity index 100% rename from .github/ISSUE_TEMPLATE.md rename to githooks/win32/TODO diff --git a/hooks/env b/hooks/env deleted file mode 100755 index 2a69777c55..0000000000 --- a/hooks/env +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load default environment variables (this must be run from tools/ path) -source ../../.env - -# These values are passed by the Hub, but if we are running locally we can get them -[ -n "$SOURCE_TYPE" ] || SOURCE_TYPE=git -[ -n "$SOURCE_BRANCH" ] || SOURCE_BRANCH=$(git symbolic-ref -q --short HEAD) -[ -n "$GIT_SHA1" ] || GIT_SHA1=$(git rev-parse -q HEAD) - -if [[ "${SOURCE_BRANCH/-*/}" =~ ^[0-9][0-9.]*$ ]]; then - VERSION=${SOURCE_BRANCH/-*/} -fi - -# Set dockerfile path if it is not absolute or if it is not defined -if [ ! -n "$DOCKERFILE_PATH" ] || [ ! "${DOCKERFILE_PATH:0:1}" = "/" ]; then - DOCKERFILE_PATH="`pwd`/Dockerfile" -fi diff --git a/hooks/push b/hooks/push deleted file mode 100755 index 900df98f94..0000000000 --- a/hooks/push +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Show message -echo "[***] Push hook running" -echo "[***] - Tagging and pushing $DOCKER_REPO:{deps, dev, runtime, cli, latest}" - -# Tag the deps image and push it -docker tag metacall/core:deps $DOCKER_REPO:deps -docker push $DOCKER_REPO:deps - -# Tag the dev image and push it -docker tag metacall/core:dev $DOCKER_REPO:dev -docker push $DOCKER_REPO:dev - -# Tag the runtime image and push it -docker tag metacall/core:runtime $DOCKER_REPO:runtime -docker push $DOCKER_REPO:runtime - -# Tag the cli image and push it -docker tag metacall/core:cli $DOCKER_REPO:cli -docker push $DOCKER_REPO:cli - -# Tag the latest image and push it (alias for cli) -docker tag metacall/core:cli $DOCKER_REPO:latest -docker push $DOCKER_REPO:latest diff --git a/metacall-config-version.cmake.in b/metacall-config-version.cmake.in index cf213b3ab3..a25c169f21 100644 --- a/metacall-config-version.cmake.in +++ b/metacall-config-version.cmake.in @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # A library for providing a foreing function interface calls. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/metacall-config.cmake.in b/metacall-config.cmake.in index 6f52ef4fc2..eb193eed48 100644 --- a/metacall-config.cmake.in +++ b/metacall-config.cmake.in @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # A library for providing a foreing function interface calls. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 7372972f3d..b7c1e402b9 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -3,7 +3,7 @@ # # Export output script directory -set(LOADER_SCRIPT_PATH "${PROJECT_BINARY_DIR}/scripts/" CACHE PATH "MetaCall scripts path") +set(LOADER_SCRIPT_PATH "${PROJECT_BINARY_DIR}/scripts" CACHE PATH "MetaCall scripts path") # Export output loader plugin directory set(LOADER_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall loaders path") @@ -18,9 +18,6 @@ set(SERIAL_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall serial lib # Export output detour plugin directory set(DETOUR_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall detour library path") -# Export output port directory -set(PORT_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall port library path") - # Add extra environment varible set(EXTRA_ENVIRONMENT_VARIABLES "" CACHE PATH "MetaCall extra environment variable") @@ -54,16 +51,13 @@ set(TESTS_DETOUR_ENVIRONMENT_VARIABLES "DETOUR_LIBRARY_PATH=${DETOUR_LIBRARY_PATH}" ) -set(TESTS_PORT_ENVIRONMENT_VARIABLES - "PORT_LIBRARY_PATH=${PORT_LIBRARY_PATH}" -) - set(TESTS_ENVIRONMENT_VARIABLES ${TESTS_LOADER_ENVIRONMENT_VARIABLES} ${TESTS_CONFIGURATION_ENVIRONMENT_VARIABLES} ${TESTS_SERIAL_ENVIRONMENT_VARIABLES} ${TESTS_DETOUR_ENVIRONMENT_VARIABLES} - ${TESTS_PORT_ENVIRONMENT_VARIABLES} + ${TESTS_SANITIZER_ENVIRONMENT_VARIABLES} + ${TESTS_MEMCHECK_ENVIRONMENT_VARIABLES} ${EXTRA_ENVIRONMENT_VARIABLES} ) @@ -77,12 +71,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # Default definitions # -add_definitions( - -DCONFIGURATION_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_DATA}/configurations/global.json\" - -DSERIAL_LIBRARY_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}\" - -DLOADER_LIBRARY_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}\" - -DDETOUR_LIBRARY_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}\" - -DPORT_LIBRARY_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}\" +set(DEFAULT_COMPILE_DEFINITIONS + ${DEFAULT_COMPILE_DEFINITIONS} + CONFIGURATION_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_DATA}/configurations/global.json" + SERIAL_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" + LOADER_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" + DETOUR_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" + PORT_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" ) # @@ -98,9 +93,11 @@ add_subdirectory(format) add_subdirectory(log) add_subdirectory(memory) add_subdirectory(portability) +add_subdirectory(threading) add_subdirectory(adt) add_subdirectory(filesystem) add_subdirectory(dynlink) +add_subdirectory(plugin) add_subdirectory(detour) add_subdirectory(reflect) add_subdirectory(serial) @@ -108,46 +105,50 @@ add_subdirectory(configuration) add_subdirectory(loader) add_subdirectory(metacall) -# Tests -set(IDE_FOLDER "Tests") -add_subdirectory(tests) - -# CLIs -set(IDE_FOLDER "CLIs") -add_subdirectory(cli) - -# Examples -set(IDE_FOLDER "Examples") -add_subdirectory(examples) - -# Benchmarks -set(IDE_FOLDER "Benchmarks") -add_subdirectory(benchmarks) - # Loaders set(IDE_FOLDER "Loaders") add_subdirectory(loaders) -# Scripts -set(IDE_FOLDER "Scripts") -add_subdirectory(scripts) - # Serials set(IDE_FOLDER "Serials") add_subdirectory(serials) -# Serials +# Detours set(IDE_FOLDER "Detours") add_subdirectory(detours) -# Distributable -set(IDE_FOLDER "Distributable") -add_subdirectory(distributable) +# Extensions +set(IDE_FOLDER "Extensions") +add_subdirectory(extensions) + +# Extensions +set(IDE_FOLDER "Plugins") +add_subdirectory(plugins) # Ports set(IDE_FOLDER "Ports") add_subdirectory(ports) +# Scripts +set(IDE_FOLDER "Scripts") +add_subdirectory(scripts) + +# Tests +set(IDE_FOLDER "Tests") +add_subdirectory(tests) + +# Benchmarks +set(IDE_FOLDER "Benchmarks") +add_subdirectory(benchmarks) + +# CLIs +set(IDE_FOLDER "CLIs") +add_subdirectory(cli) + +# Examples +set(IDE_FOLDER "Examples") +add_subdirectory(examples) + # # Deployment # @@ -169,26 +170,22 @@ install_print("green" "LOADER_SCRIPT_PATH:") install_print("normal" "\tInstall Location: N/A") install_print("normal" "\tDefault Location: scripts") -if(BUILD_SHARED_LIBS) - install_print("green" "LOADER_LIBRARY_PATH:") - install_print("normal" "\tDescription: Directory where MetaCall loader plugins are located") - install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") - install_print("normal" "\tDefault Location: .") -endif() - install_print("green" "CONFIGURATION_PATH:") install_print("normal" "\tDescription: Path to the main global MetaCall configuration") install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_DATA}/configurations/global.json") install_print("normal" "\tDefault Location: configurations/global.json") if(BUILD_SHARED_LIBS) + install_print("green" "LOADER_LIBRARY_PATH:") + install_print("normal" "\tDescription: Directory where MetaCall loader plugins are located") + install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") + install_print("normal" "\tDefault Location: .") + install_print("green" "SERIAL_LIBRARY_PATH:") install_print("normal" "\tDescription: Directory where MetaCall serial plugins are located") install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") install_print("normal" "\tDefault Location: serials") -endif() -if(BUILD_SHARED_LIBS) install_print("green" "DETOUR_LIBRARY_PATH:") install_print("normal" "\tDescription: Directory where MetaCall detour plugins are located") install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") diff --git a/source/adt/CMakeLists.txt b/source/adt/CMakeLists.txt index a86fab8e8c..6083509df2 100644 --- a/source/adt/CMakeLists.txt +++ b/source/adt/CMakeLists.txt @@ -1,9 +1,3 @@ -# -# External dependencies -# - -# find_package(THIRDPARTY REQUIRED) - # # Library name and options # @@ -16,7 +10,6 @@ message(STATUS "Lib ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -44,9 +37,11 @@ set(headers ${include_path}/adt_comparable.h ${include_path}/adt_hash.h ${include_path}/adt_set.h - ${include_path}/adt_set_bucket.h + ${include_path}/adt_map.h + ${include_path}/adt_bucket.h ${include_path}/adt_trie.h ${include_path}/adt_vector.h + ${include_path}/adt_string.h ) set(sources @@ -54,7 +49,8 @@ set(sources ${source_path}/adt_comparable.c ${source_path}/adt_hash.c ${source_path}/adt_set.c - ${source_path}/adt_set_bucket.c + ${source_path}/adt_map.c + ${source_path}/adt_bucket.c ${source_path}/adt_trie.c ${source_path}/adt_vector.c ) @@ -81,30 +77,7 @@ add_library(${target} add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects -if(NOT OPTION_BUILD_DIST_LIBS) - export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -endif() - -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) # Create API export header generate_export_header(${target} @@ -150,6 +123,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log PUBLIC @@ -164,6 +138,7 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE + ${target_upper}_EXPORTS # Export API PUBLIC $<$>:${target_upper}_STATIC_DEFINE> @@ -189,7 +164,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC @@ -197,17 +172,3 @@ target_link_libraries(${target} INTERFACE ) - -# -# Deployment -# - -# Library -if(NOT OPTION_BUILD_DIST_LIBS) - install(TARGETS ${target} - EXPORT "${target}-export" COMPONENT dev - RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime - LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime - ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev - ) -endif() diff --git a/source/adt/include/adt/adt.h b/source/adt/include/adt/adt.h index 6f6c64508f..040bd1fc50 100644 --- a/source/adt/include/adt/adt.h +++ b/source/adt/include/adt/adt.h @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,14 +25,14 @@ #include #include -#include #include +#include #ifdef __cplusplus extern "C" { #endif -ADT_API const char * adt_print_info(void); +ADT_API const char *adt_print_info(void); #ifdef __cplusplus } diff --git a/source/adt/include/adt/adt_bucket.h b/source/adt/include/adt/adt_bucket.h new file mode 100644 index 0000000000..4fb799f189 --- /dev/null +++ b/source/adt/include/adt/adt_bucket.h @@ -0,0 +1,74 @@ +/* + * Abstract Data Type Library by Parra Studios + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * A abstract data type library providing generic containers. + * + */ + +#ifndef ADT_BUCKET_H +#define ADT_BUCKET_H 1 + +/* -- Headers -- */ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Forward Declarations -- */ + +struct pair_type; +struct bucket_type; + +/* -- Type Definitions -- */ + +typedef struct pair_type *pair; +typedef struct bucket_type *bucket; + +/* -- Member Data -- */ + +struct pair_type +{ + void *key; + void *value; +}; + +struct bucket_type +{ + size_t count; + size_t capacity; + pair pairs; +}; + +/* -- Methods -- */ + +ADT_API size_t bucket_capacity(size_t prime); + +ADT_API bucket bucket_create(size_t size); + +ADT_API int bucket_alloc_pairs(bucket b, size_t capacity); + +ADT_API int bucket_realloc_pairs(bucket b, size_t new_capacity); + +ADT_API pair bucket_get_pair(bucket b, comparable_callback compare_cb, void *key); + +ADT_API vector bucket_get_pairs_value(bucket b, comparable_callback compare_cb, void *key); + +ADT_API int bucket_insert(bucket b, void *key, void *value); + +ADT_API int bucket_remove(bucket b, comparable_callback compare_cb, void *key, void **value); + +#ifdef __cplusplus +} +#endif + +#endif /* ADT_BUCKET_H */ diff --git a/source/adt/include/adt/adt_comparable.h b/source/adt/include/adt/adt_comparable.h index 79ad0a44d5..54fc2d2b84 100644 --- a/source/adt/include/adt/adt_comparable.h +++ b/source/adt/include/adt/adt_comparable.h @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ extern "C" { #endif -typedef void * comparable; +typedef void *comparable; typedef int (*comparable_callback)(const comparable, const comparable); diff --git a/source/adt/include/adt/adt_hash.h b/source/adt/include/adt/adt_hash.h index a7972040b4..3601d4676c 100644 --- a/source/adt/include/adt/adt_hash.h +++ b/source/adt/include/adt/adt_hash.h @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,11 +27,15 @@ extern "C" { #endif +/* -- Headers -- */ + +#include + /* -- Type Definitions -- */ -typedef unsigned int hash; +typedef uintptr_t hash; -typedef void * hash_key; +typedef void *hash_key; typedef hash (*hash_callback)(const hash_key); @@ -39,6 +43,8 @@ typedef hash (*hash_callback)(const hash_key); ADT_API hash hash_callback_str(const hash_key key); +ADT_API hash hash_callback_ptr(const hash_key key); + #ifdef __cplusplus } #endif diff --git a/source/adt/include/adt/adt_map.h b/source/adt/include/adt/adt_map.h new file mode 100644 index 0000000000..2a256463ed --- /dev/null +++ b/source/adt/include/adt/adt_map.h @@ -0,0 +1,105 @@ +/* + * Abstract Data Type Library by Parra Studios + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * A abstract data type library providing generic containers. + * + */ + +#ifndef ADT_MAP_H +#define ADT_MAP_H 1 + +/* -- Headers -- */ + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Forward Declarations -- */ + +struct map_type; + +struct map_iterator_type; + +/* -- Type Definitions -- */ + +typedef struct map_type *map; + +typedef hash map_hash; + +typedef comparable map_key; + +typedef void *map_value; + +typedef void *map_cb_iterate_args; + +typedef hash_callback map_cb_hash; + +typedef comparable_callback map_cb_compare; + +typedef int (*map_cb_iterate)(map, map_key, map_value, map_cb_iterate_args); + +typedef struct map_iterator_type *map_iterator; + +/* -- Member Data -- */ + +struct map_iterator_type +{ + map m; + size_t current_bucket; + size_t current_pair; +}; + +/* -- Methods -- */ + +ADT_API map map_create(map_cb_hash hash_cb, map_cb_compare compare_cb); + +ADT_API size_t map_size(map m); + +ADT_API int map_insert(map m, map_key key, map_value value); + +ADT_API int map_insert_array(map m, map_key keys[], map_value values[], size_t size); + +ADT_API vector map_get(map m, map_key key); + +ADT_API int map_contains(map m, map_key key); + +ADT_API int map_contains_any(map dest, map src); + +ADT_API map_value map_remove(map m, map_key key); + +ADT_API vector map_remove_all(map m, map_key key); + +ADT_API void map_iterate(map m, map_cb_iterate iterate_cb, map_cb_iterate_args args); + +ADT_API int map_append(map dest, map src); + +ADT_API int map_clear(map m); + +ADT_API void map_destroy(map m); + +ADT_API void map_iterator_begin(map_iterator it, map m); + +ADT_API map_key map_iterator_key(map_iterator it); + +ADT_API map_value map_iterator_value(map_iterator it); + +ADT_API void map_iterator_next(map_iterator it); + +ADT_API int map_iterator_end(map_iterator it); + +#ifdef __cplusplus +} +#endif + +#endif /* ADT_MAP_H */ diff --git a/source/adt/include/adt/adt_set.h b/source/adt/include/adt/adt_set.h index d86201da2c..a662a90b8b 100644 --- a/source/adt/include/adt/adt_set.h +++ b/source/adt/include/adt/adt_set.h @@ -1,6 +1,6 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A abstract data type library providing generic containers. * @@ -13,8 +13,8 @@ #include -#include #include +#include #ifdef __cplusplus extern "C" { @@ -32,15 +32,15 @@ struct set_iterator_type; /* -- Type Definitions -- */ -typedef struct set_type * set; +typedef struct set_type *set; typedef hash set_hash; typedef comparable set_key; -typedef void * set_value; +typedef void *set_value; -typedef void * set_cb_iterate_args; +typedef void *set_cb_iterate_args; typedef hash_callback set_cb_hash; @@ -48,7 +48,16 @@ typedef comparable_callback set_cb_compare; typedef int (*set_cb_iterate)(set, set_key, set_value, set_cb_iterate_args); -typedef struct set_iterator_type * set_iterator; +typedef struct set_iterator_type *set_iterator; + +/* -- Member Data -- */ + +struct set_iterator_type +{ + set s; + size_t current_bucket; + size_t current_pair; +}; /* -- Methods -- */ @@ -66,6 +75,8 @@ ADT_API int set_contains(set s, set_key key); ADT_API int set_contains_any(set dest, set src); +ADT_API int set_contains_which(set dest, set src, set_key *key); + ADT_API set_value set_remove(set s, set_key key); ADT_API void set_iterate(set s, set_cb_iterate iterate_cb, set_cb_iterate_args args); @@ -78,15 +89,15 @@ ADT_API int set_clear(set s); ADT_API void set_destroy(set s); -ADT_API set_iterator set_iterator_begin(set s); +ADT_API void set_iterator_begin(set_iterator it, set s); -ADT_API set_key set_iterator_get_key(set_iterator it); +ADT_API set_key set_iterator_key(set_iterator it); -ADT_API set_value set_iterator_get_value(set_iterator it); +ADT_API set_value set_iterator_value(set_iterator it); ADT_API void set_iterator_next(set_iterator it); -ADT_API int set_iterator_end(set_iterator * it); +ADT_API int set_iterator_end(set_iterator it); #ifdef __cplusplus } diff --git a/source/adt/include/adt/adt_set_bucket.h b/source/adt/include/adt/adt_set_bucket.h deleted file mode 100644 index 480e175d01..0000000000 --- a/source/adt/include/adt/adt_set_bucket.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A abstract data type library providing generic containers. - * - */ - -#ifndef ADT_SET_BUCKET_H -#define ADT_SET_BUCKET_H 1 - -/* -- Headers -- */ - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Headers -- */ - -#include - -/* -- Forward Declarations -- */ - -struct set_pair_type; -struct set_bucket_type; - -/* -- Type Definitions -- */ - -typedef struct set_pair_type * set_pair; -typedef struct set_bucket_type * set_bucket; - -/* -- Member Data -- */ - -struct set_pair_type -{ - void * key; - void * value; -}; - -struct set_bucket_type -{ - size_t count; - size_t capacity; - set_pair pairs; -}; - -/* -- Methods -- */ - -ADT_API size_t set_bucket_capacity(size_t prime); - -ADT_API set_bucket set_bucket_create(size_t size); - -ADT_API int set_bucket_alloc_pairs(set_bucket bucket, size_t capacity); - -ADT_API int set_bucket_realloc_pairs(set_bucket bucket, size_t new_capacity); - -ADT_API set_pair set_bucket_get_pair(set_bucket bucket, comparable_callback compare_cb, void * key); - -ADT_API int set_bucket_insert(set_bucket bucket, void * key, void * value); - -ADT_API int set_bucket_remove(set_bucket bucket, comparable_callback compare_cb, void * key, void ** value); - -#ifdef __cplusplus -} -#endif - -#endif /* ADT_SET_BUCKET_H */ diff --git a/source/adt/include/adt/adt_string.h b/source/adt/include/adt/adt_string.h new file mode 100644 index 0000000000..1b50369a8d --- /dev/null +++ b/source/adt/include/adt/adt_string.h @@ -0,0 +1,50 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef ADT_STRING_H +#define ADT_STRING_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include +#include + +/* -- Macros -- */ + +/* This is a temporary solution for safe strings, it can be improved in the future */ +#define string_copy(dest, src, dest_size) \ + do \ + { \ + size_t __string_copy_length = strnlen(src, dest_size - 1); \ + memcpy(dest, src, __string_copy_length); \ + dest[__string_copy_length] = '\0'; \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* ADT_STRING_H */ diff --git a/source/adt/include/adt/adt_trie.h b/source/adt/include/adt/adt_trie.h index c6f2e07801..899e1f9296 100644 --- a/source/adt/include/adt/adt_trie.h +++ b/source/adt/include/adt/adt_trie.h @@ -1,6 +1,6 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A abstract data type library providing generic containers. * @@ -13,8 +13,8 @@ #include -#include #include +#include #include #ifdef __cplusplus @@ -31,15 +31,15 @@ struct trie_type; /* -- Type Definitions -- */ -typedef struct trie_type * trie; +typedef struct trie_type *trie; typedef hash trie_hash; typedef comparable trie_key; -typedef void * trie_value; +typedef void *trie_value; -typedef void * trie_cb_iterate_args; +typedef void *trie_cb_iterate_args; typedef hash_callback trie_cb_hash; diff --git a/source/adt/include/adt/adt_vector.h b/source/adt/include/adt/adt_vector.h index 55ff97635f..d727171687 100644 --- a/source/adt/include/adt/adt_vector.h +++ b/source/adt/include/adt/adt_vector.h @@ -1,6 +1,6 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A abstract data type library providing generic containers. * @@ -27,7 +27,7 @@ struct vector_type; /* -- Type Definitions -- */ -typedef struct vector_type * vector; +typedef struct vector_type *vector; /* -- Macros -- */ @@ -74,7 +74,7 @@ typedef struct vector_type * vector; * Pointer to the first element */ #define vector_front_type(v, type_name) \ - (*((type_name*)vector_front(v))) + (*((type_name *)vector_front(v))) /** * @brief @@ -90,7 +90,7 @@ typedef struct vector_type * vector; * Pointer to the last element */ #define vector_back_type(v, type_name) \ - (*((type_name*)vector_back(v))) + (*((type_name *)vector_back(v))) /** * @brief @@ -209,12 +209,9 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_set(v, position, ¯o_vector_type_const_to_var); \ - \ } while (0) - /** * @brief * Adds a constant object at the end of vector @@ -233,9 +230,7 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_push_back(v, ¯o_vector_type_const_to_var); \ - \ } while (0) /** @@ -256,9 +251,7 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_push_front(v, ¯o_vector_type_const_to_var); \ - \ } while (0) /** @@ -284,9 +277,7 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_insert(v, position, ¯o_vector_type_const_to_var); \ - \ } while (0) /* -- Methods -- */ @@ -407,7 +398,7 @@ ADT_API size_t vector_type_size(vector v); * @return * Pointer to the first element */ -ADT_API void * vector_front(vector v); +ADT_API void *vector_front(vector v); /** * @brief @@ -419,7 +410,7 @@ ADT_API void * vector_front(vector v); * @return * Pointer to the last element */ -ADT_API void * vector_back(vector v); +ADT_API void *vector_back(vector v); /** * @brief @@ -435,7 +426,7 @@ ADT_API void * vector_back(vector v); * @return * Pointer to the element at @position */ -ADT_API void * vector_at(vector v, size_t position); +ADT_API void *vector_at(vector v, size_t position); /** * @brief @@ -451,7 +442,7 @@ ADT_API void * vector_at(vector v, size_t position); * @param[in] element * Element to be copied at @position */ -ADT_API void vector_set(vector v, size_t position, void * element); +ADT_API void vector_set(vector v, size_t position, void *element); /** * @brief @@ -475,7 +466,7 @@ ADT_API void vector_push_back_empty(vector v); * @param[in] element * Element to be inserted */ -ADT_API void vector_push_back(vector v, void * element); +ADT_API void vector_push_back(vector v, void *element); /** * @brief @@ -509,7 +500,7 @@ ADT_API void vector_push_front_empty(vector v); * @param[in] element * Element to be inserted */ -ADT_API void vector_push_front(vector v, void * element); +ADT_API void vector_push_front(vector v, void *element); /** * @brief @@ -554,7 +545,7 @@ ADT_API void vector_insert_empty(vector v, size_t position); * @param[in] element * Reference to the element to be inserted */ -ADT_API void vector_insert(vector v, size_t position, void * element); +ADT_API void vector_insert(vector v, size_t position, void *element); /** * @brief diff --git a/source/adt/source/adt.c b/source/adt/source/adt.c index 347789731b..eeaf243223 100644 --- a/source/adt/source/adt.c +++ b/source/adt/source/adt.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,17 +22,17 @@ #include -const char * adt_print_info() +const char *adt_print_info(void) { static const char adt_info[] = "Abstract Data Type Library " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef ADT_STATIC_DEFINE - "Compiled as static library type" - #else - "Compiled as shared library type" - #endif +#ifdef ADT_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif "\n"; diff --git a/source/adt/source/adt_bucket.c b/source/adt/source/adt_bucket.c new file mode 100644 index 0000000000..98f7398751 --- /dev/null +++ b/source/adt/source/adt_bucket.c @@ -0,0 +1,322 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include + +#include + +/* -- Definitions -- */ + +#define BUCKET_PAIRS_DEFAULT ((size_t)0x04) + +/* -- Methods -- */ + +size_t bucket_capacity(size_t prime) +{ + static const size_t capacity_primes[] = { + 5UL, + 11UL, + 23UL, + 47UL, + 97UL, + 199UL, + 409UL, + 823UL, + 1741UL, + 3469UL, + 6949UL, + 14033UL, + 28411UL, + 57557UL, + 116731UL, + 236897UL, + 480881UL, + 976369UL, + 1982627UL, + 4026031UL, + 8175383UL, + 16601593UL, + 33712729UL, + 68460391UL, + 139022417UL, + 282312799UL, + 573292817UL, + 1164186217UL, + 2364114217UL, + 4294967291UL, + +#if (SIZE_MAX > 0xFFFFFFFF) + (size_t)8589934583ULL, + (size_t)17179869143ULL, + (size_t)34359738337ULL, + (size_t)68719476731ULL, + (size_t)137438953447ULL, + (size_t)274877906899ULL, + (size_t)549755813881ULL, + (size_t)1099511627689ULL, + (size_t)2199023255531ULL, + (size_t)4398046511093ULL, + (size_t)8796093022151ULL, + (size_t)17592186044399ULL, + (size_t)35184372088777ULL, + (size_t)70368744177643ULL, + (size_t)140737488355213ULL, + (size_t)281474976710597ULL, + (size_t)562949953421231ULL, + (size_t)1125899906842597ULL, + (size_t)2251799813685119ULL, + (size_t)4503599627370449ULL, + (size_t)9007199254740881ULL, + (size_t)18014398509481951ULL, + (size_t)36028797018963913ULL, + (size_t)72057594037927931ULL, + (size_t)144115188075855859ULL, + (size_t)288230376151711717ULL, + (size_t)576460752303423433ULL, + (size_t)1152921504606846883ULL, + (size_t)2305843009213693951ULL, + (size_t)4611686018427387847ULL, + (size_t)9223372036854775783ULL, + (size_t)18446744073709551557ULL +#endif + }; + + static const size_t capacity_primes_size = sizeof(capacity_primes) / sizeof(capacity_primes[0]); + + if (prime < capacity_primes_size) + { + return capacity_primes[prime]; + } + + return 0; +} + +bucket bucket_create(size_t size) +{ + bucket buckets; + + size_t iterator; + + if (size == 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket size: %" PRIuS, size); + return NULL; + } + + buckets = malloc(sizeof(struct bucket_type) * size); + + if (buckets == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad allocation for bucket"); + return NULL; + } + + for (iterator = 0; iterator < size; ++iterator) + { + buckets[iterator].count = 0; + buckets[iterator].capacity = 0; + buckets[iterator].pairs = NULL; + } + + return buckets; +} + +int bucket_alloc_pairs(bucket b, size_t capacity) +{ + if (b == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket"); + return 1; + } + + if (b->pairs != NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Set bucket pairs already allocated"); + return 1; + } + + b->pairs = malloc(sizeof(struct pair_type) * capacity); + + if (b->pairs == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad pairs allocation"); + return 1; + } + + b->count = 0; + b->capacity = capacity; + + return 0; +} + +int bucket_realloc_pairs(bucket b, size_t new_capacity) +{ + pair pairs = realloc(b->pairs, sizeof(struct pair_type) * new_capacity); + + if (pairs == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad pairs reallocation"); + return 1; + } + + b->pairs = pairs; + b->capacity = new_capacity; + + return 0; +} + +pair bucket_get_pair(bucket b, comparable_callback compare_cb, void *key) +{ + size_t iterator; + + if (b->pairs == NULL || b->count == 0) + { + return NULL; + } + + for (iterator = 0; iterator < b->count; ++iterator) + { + pair p = &b->pairs[iterator]; + + if (compare_cb(key, p->key) == 0) + { + return p; + } + } + + return NULL; +} + +vector bucket_get_pairs_value(bucket b, comparable_callback compare_cb, void *key) +{ + size_t iterator; + vector v; + + if (b->pairs == NULL) + { + return NULL; + } + + v = vector_create(sizeof(void *)); + + for (iterator = 0; iterator < b->count; ++iterator) + { + pair p = &b->pairs[iterator]; + + if (compare_cb(key, p->key) == 0) + { + vector_push_back(v, &p->value); + } + } + + return v; +} + +int bucket_insert(bucket b, void *key, void *value) +{ + pair p; + + if (b == NULL || key == NULL || value == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket insert parameters"); + return 1; + } + + if (b->pairs == NULL && bucket_alloc_pairs(b, BUCKET_PAIRS_DEFAULT) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket insertion pairs allocation"); + return 1; + } + + if ((b->count + 1) >= b->capacity) + { + if (bucket_realloc_pairs(b, b->capacity << 1) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket insertion pairs reallocation"); + return 1; + } + } + + p = &b->pairs[b->count]; + + p->key = key; + p->value = value; + + ++b->count; + + return 0; +} + +int bucket_remove(bucket b, comparable_callback compare_cb, void *key, void **value) +{ + size_t iterator; + + if (b == NULL || compare_cb == NULL || key == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket remove parameters"); + return 1; + } + + if (b->pairs == NULL || b->count == 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket remove pairs"); + return 1; + } + + for (iterator = 0; iterator < b->count; ++iterator) + { + pair p = &b->pairs[iterator]; + + if (compare_cb(key, p->key) == 0) + { + void *deleted_value = p->value; + + size_t next = iterator + 1; + + size_t new_capacity = b->capacity >> 1; + + memmove(p, &b->pairs[next], sizeof(struct pair_type) * (b->count - next)); + + --b->count; + + if (b->count <= new_capacity && new_capacity > BUCKET_PAIRS_DEFAULT) + { + if (bucket_realloc_pairs(b, new_capacity) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket remove pairs reallocation"); + return 1; + } + } + + if (value != NULL) + { + *value = deleted_value; + } + + return 0; + } + } + + return 1; +} diff --git a/source/adt/source/adt_comparable.c b/source/adt/source/adt_comparable.c index c5ea89f4f1..037be1f9be 100644 --- a/source/adt/source/adt_comparable.c +++ b/source/adt/source/adt_comparable.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,16 +24,16 @@ int comparable_callback_str(const comparable a, const comparable b) { - const char * str_a = a; - const char * str_b = b; + const char *str_a = a; + const char *str_b = b; return strcmp(str_a, str_b); } int comparable_callback_int(const comparable a, const comparable b) { - const int * int_ptr_a = a; - const int * int_ptr_b = b; + const int *int_ptr_a = a; + const int *int_ptr_b = b; const int int_var_a = *int_ptr_a; const int int_var_b = *int_ptr_b; @@ -54,8 +54,8 @@ int comparable_callback_int(const comparable a, const comparable b) int comparable_callback_ptr(const comparable a, const comparable b) { - const void * void_ptr_a = a; - const void * void_ptr_b = b; + const void *void_ptr_a = a; + const void *void_ptr_b = b; if (void_ptr_a < void_ptr_b) { diff --git a/source/adt/source/adt_hash.c b/source/adt/source/adt_hash.c index e94c48f635..2800af3b60 100644 --- a/source/adt/source/adt_hash.c +++ b/source/adt/source/adt_hash.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,8 @@ hash hash_callback_str(const hash_key key) { - const char * str = (const char *)key; + /* djb2 */ + const char *str = (const char *)key; hash h = 0x1505; @@ -35,3 +36,25 @@ hash hash_callback_str(const hash_key key) return h; } + +hash hash_callback_ptr(const hash_key key) +{ + /* https://stackoverflow.com/a/12996028 */ + uintptr_t x = (uintptr_t)key; + +#if UINTPTR_MAX == 0xFFFFFFFF + /* 32-bit */ + x = ((x >> 16) ^ x) * 0x45D9F3B; + x = ((x >> 16) ^ x) * 0x45D9F3B; + x = (x >> 16) ^ x; +#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFF + /* 64-bit */ + x = (x ^ (x >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); + x = (x ^ (x >> 27)) * UINT64_C(0x94D049BB133111EB); + x = x ^ (x >> 31); +#else + #error "Architecture not implemented for hash function" +#endif + + return x; +} diff --git a/source/adt/source/adt_map.c b/source/adt/source/adt_map.c new file mode 100644 index 0000000000..eb1768d0d9 --- /dev/null +++ b/source/adt/source/adt_map.c @@ -0,0 +1,583 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include +#include + +#include + +/* -- Definitions -- */ + +#define MAP_BUCKET_RATIO_MIN 0.1f +#define MAP_BUCKET_RATIO_MAX 0.77f + +/* -- Member Data -- */ + +struct map_type +{ + size_t count; + size_t capacity; + size_t prime; + bucket buckets; + map_cb_hash hash_cb; + map_cb_compare compare_cb; +}; + +/* -- Methods -- */ + +map map_create(map_cb_hash hash_cb, map_cb_compare compare_cb) +{ + map m; + + if (hash_cb == NULL || compare_cb == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map creation parameters"); + return NULL; + } + + m = malloc(sizeof(struct map_type)); + + if (m == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad map allocation"); + return NULL; + } + + m->hash_cb = hash_cb; + m->compare_cb = compare_cb; + m->count = 0; + m->prime = 0; + m->capacity = bucket_capacity(m->prime); + m->buckets = bucket_create(m->capacity); + + if (m->buckets == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad map bucket creation"); + free(m); + return NULL; + } + + return m; +} + +size_t map_size(map m) +{ + if (m != NULL) + { + return m->count; + } + + return 0; +} + +static int map_bucket_rehash(map m, map new_map) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < m->capacity; ++bucket_iterator) + { + bucket b = &m->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + map_hash h = new_map->hash_cb(p->key); + + size_t index = h % new_map->capacity; + + bucket new_bucket = &new_map->buckets[index]; + + if (bucket_insert(new_bucket, p->key, p->value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket realloc insertion"); + return 1; + } + + ++new_map->count; + } + } + } + + return 0; +} + +static int map_bucket_realloc(map m) +{ + struct map_type new_map; + size_t prime = m->prime; + float ratio = (float)((float)m->count / (float)m->capacity); + + if (prime > 0 && ratio <= MAP_BUCKET_RATIO_MIN) + { + --prime; + } + else if (ratio >= MAP_BUCKET_RATIO_MAX) + { + ++prime; + } + else + { + return 0; + } + + new_map.hash_cb = m->hash_cb; + new_map.compare_cb = m->compare_cb; + new_map.count = 0; + new_map.prime = prime; + new_map.capacity = bucket_capacity(prime); + new_map.buckets = bucket_create(new_map.capacity); + + if (new_map.buckets != NULL) + { + size_t iterator; + + /* Rehash all the elements into the new map */ + map_bucket_rehash(m, &new_map); + + /* Destroy all pairs from old map */ + for (iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL) + { + free(b->pairs); + } + } + + /* Destroy all buckets from old map */ + free(m->buckets); + + m->capacity = new_map.capacity; + m->prime = new_map.prime; + m->buckets = new_map.buckets; + + return 0; + } + + return 1; +} + +int map_insert(map m, map_key key, map_value value) +{ + map_hash h; + size_t index; + bucket b; + + if (m == NULL || key == NULL || value == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map insertion parameters"); + return 1; + } + + h = m->hash_cb(key); + + index = h % m->capacity; + + b = &m->buckets[index]; + + if (bucket_insert(b, key, value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket insertion"); + return 1; + } + + ++m->count; + + return map_bucket_realloc(m); +} + +int map_insert_array(map m, map_key keys[], map_value values[], size_t size) +{ + size_t iterator; + + if (m == NULL || keys == NULL || values == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map insertion parameters"); + return 1; + } + + for (iterator = 0; iterator < size; ++iterator) + { + if (map_insert(m, keys[iterator], values[iterator]) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map array insertion"); + return 1; + } + } + + return 0; +} + +vector map_get(map m, map_key key) +{ + if (m != NULL && key != NULL) + { + map_hash h = m->hash_cb(key); + + size_t index = h % m->capacity; + + bucket b = &m->buckets[index]; + + return bucket_get_pairs_value(b, m->compare_cb, key); + } + + return NULL; +} + +int map_contains(map m, map_key key) +{ + if (m != NULL && key != NULL) + { + map_hash h = m->hash_cb(key); + + size_t index = h % m->capacity; + + bucket b = &m->buckets[index]; + + pair p = bucket_get_pair(b, m->compare_cb, key); + + if (p != NULL) + { + return 0; + } + } + + return 1; +} + +int map_contains_any(map dest, map src) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + if (map_contains(dest, p->key) == 0) + { + return 0; + } + } + } + } + + return 1; +} + +map_value map_remove(map m, map_key key) +{ + map_hash h; + size_t index; + bucket b; + map_value value = NULL; + + if (m == NULL || key == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map remove parameters"); + return NULL; + } + + h = m->hash_cb(key); + + index = h % m->capacity; + + b = &m->buckets[index]; + + if (bucket_remove(b, m->compare_cb, key, &value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove: %p", key); + return NULL; + } + + --m->count; + + if (map_bucket_realloc(m) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove reallocation"); + return NULL; + } + + return value; +} + +vector map_remove_all(map m, map_key key) +{ + map_hash h; + size_t index, iterator, size; + bucket b; + vector v = NULL; + + if (m == NULL || key == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map remove parameters"); + return NULL; + } + + h = m->hash_cb(key); + + index = h % m->capacity; + + b = &m->buckets[index]; + + v = map_get(m, key); + + size = vector_size(v); + + if (size == 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove: %p", key); + vector_destroy(v); + return NULL; + } + + for (iterator = 0; iterator < size; ++iterator) + { + if (bucket_remove(b, m->compare_cb, key, NULL) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove: %p", key); + vector_destroy(v); + return NULL; + } + + --m->count; + } + + if (map_bucket_realloc(m) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove reallocation"); + vector_destroy(v); + return NULL; + } + + return v; +} + +void map_iterate(map m, map_cb_iterate iterate_cb, map_cb_iterate_args args) +{ + if (m != NULL && m->buckets != NULL && iterate_cb != NULL) + { + size_t bucket_iterator; + + for (bucket_iterator = 0; bucket_iterator < m->capacity; ++bucket_iterator) + { + bucket b = &m->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + size_t pair_iterator; + + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + if (iterate_cb(m, p->key, p->value, args) != 0) + { + return; + } + } + } + } + } +} + +int map_append(map dest, map src) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + if (map_insert(dest, p->key, p->value) != 0) + { + return 1; + } + } + } + } + + return 0; +} + +int map_clear(map m) +{ + if (m == NULL) + { + return 1; + } + + if (m->buckets != NULL) + { + size_t iterator; + + for (iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL) + { + free(b->pairs); + } + } + + free(m->buckets); + } + + m->count = 0; + m->prime = 0; + m->capacity = bucket_capacity(m->prime); + m->buckets = bucket_create(m->capacity); + + if (m->buckets == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad map clear bucket creation"); + return 1; + } + + return 0; +} + +void map_destroy(map m) +{ + if (m == NULL) + { + return; + } + + if (m->buckets != NULL) + { + size_t iterator; + + for (iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL) + { + free(b->pairs); + } + } + + free(m->buckets); + } + + free(m); +} + +void map_iterator_begin(map_iterator it, map m) +{ + if (it != NULL) + { + it->current_bucket = 0; + it->current_pair = 0; + + if (m != NULL && m->buckets != NULL && map_size(m) > 0) + { + it->m = m; + + map_iterator_next(it); + } + else + { + it->m = NULL; + } + } +} + +map_key map_iterator_key(map_iterator it) +{ + if (it != NULL && it->m != NULL && it->current_bucket < it->m->capacity && it->current_pair > 0) + { + return it->m->buckets[it->current_bucket].pairs[it->current_pair - 1].key; + } + + return NULL; +} + +map_value map_iterator_value(map_iterator it) +{ + if (it != NULL && it->m != NULL && it->current_bucket < it->m->capacity && it->current_pair > 0) + { + return it->m->buckets[it->current_bucket].pairs[it->current_pair - 1].value; + } + + return NULL; +} + +void map_iterator_next(map_iterator it) +{ + if (it != NULL && it->m != NULL) + { + for (; it->current_bucket < it->m->capacity; ++it->current_bucket) + { + bucket b = &it->m->buckets[it->current_bucket]; + + if (b->pairs != NULL && b->count > 0) + { + for (; it->current_pair < b->count; ++it->current_pair) + { + pair p = &b->pairs[it->current_pair]; + + if (p != NULL) + { + ++it->current_pair; + + return; + } + } + } + + it->current_pair = 0; + } + } +} + +int map_iterator_end(map_iterator it) +{ + if (it != NULL && it->m != NULL) + { + if (it->current_bucket >= it->m->capacity) + { + return 0; + } + + return 1; + } + + return 0; +} diff --git a/source/adt/source/adt_set.c b/source/adt/source/adt_set.c index 334b2a7828..2ffd457172 100644 --- a/source/adt/source/adt_set.c +++ b/source/adt/source/adt_set.c @@ -1,22 +1,34 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * * A abstract data type library providing generic containers. * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ /* -- Headers -- */ +#include #include -#include #include /* -- Definitions -- */ -#define SET_BUCKET_RATIO_MIN 0.1f -#define SET_BUCKET_RATIO_MAX 0.77f +#define SET_BUCKET_RATIO_MIN 0.1f +#define SET_BUCKET_RATIO_MAX 0.77f /* -- Member Data -- */ @@ -25,26 +37,11 @@ struct set_type size_t count; size_t capacity; size_t prime; - set_bucket buckets; + bucket buckets; set_cb_hash hash_cb; set_cb_compare compare_cb; }; -struct set_iterator_type -{ - set s; - size_t bucket; - size_t pair; -}; - -struct set_contains_any_cb_iterator_type -{ - set s; - int result; -}; - -typedef struct set_contains_any_cb_iterator_type * set_contains_any_cb_iterator; - /* -- Methods -- */ set set_create(set_cb_hash hash_cb, set_cb_compare compare_cb) @@ -54,7 +51,6 @@ set set_create(set_cb_hash hash_cb, set_cb_compare compare_cb) if (hash_cb == NULL || compare_cb == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set creation parameters"); - return NULL; } @@ -63,21 +59,20 @@ set set_create(set_cb_hash hash_cb, set_cb_compare compare_cb) if (s == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Bad set allocation"); + return NULL; } s->hash_cb = hash_cb; s->compare_cb = compare_cb; s->count = 0; s->prime = 0; - s->capacity = set_bucket_capacity(s->prime); - s->buckets = set_bucket_create(s->capacity); + s->capacity = bucket_capacity(s->prime); + s->buckets = bucket_create(s->capacity); if (s->buckets == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Bad set bucket creation"); - free(s); - return NULL; } @@ -94,6 +89,8 @@ size_t set_size(set s) return 0; } +/* + static int set_bucket_realloc_iterator(set s, set_key key, set_value value, set_cb_iterate_args args) { set new_set = (set)args; @@ -104,12 +101,11 @@ static int set_bucket_realloc_iterator(set s, set_key key, set_value value, set_ size_t index = h % new_set->capacity; - set_bucket bucket = &new_set->buckets[index]; + bucket b = &new_set->buckets[index]; - if (set_bucket_insert(bucket, key, value) != 0) + if (bucket_insert(b, key, value) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket realloc insertion"); - return 1; } @@ -120,13 +116,46 @@ static int set_bucket_realloc_iterator(set s, set_key key, set_value value, set_ return 1; } +*/ + +static int set_bucket_rehash(set s, set new_set) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < s->capacity; ++bucket_iterator) + { + bucket b = &s->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + set_hash h = new_set->hash_cb(p->key); + + size_t index = h % new_set->capacity; + + bucket new_bucket = &new_set->buckets[index]; + + if (bucket_insert(new_bucket, p->key, p->value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket realloc insertion"); + return 1; + } + + ++new_set->count; + } + } + } + + return 0; +} static int set_bucket_realloc(set s) { struct set_type new_set; - size_t prime = s->prime; - float ratio = (float)((float)s->count / (float)s->capacity); if (prime > 0 && ratio <= SET_BUCKET_RATIO_MIN) @@ -146,25 +175,28 @@ static int set_bucket_realloc(set s) new_set.compare_cb = s->compare_cb; new_set.count = 0; new_set.prime = prime; - new_set.capacity = set_bucket_capacity(prime); - new_set.buckets = set_bucket_create(new_set.capacity); + new_set.capacity = bucket_capacity(prime); + new_set.buckets = bucket_create(new_set.capacity); if (new_set.buckets != NULL) { size_t iterator; - set_iterate(s, &set_bucket_realloc_iterator, &new_set); + /* Rehash all the elements into the new set */ + set_bucket_rehash(s, &new_set); + /* Destroy all pairs from old set */ for (iterator = 0; iterator < s->capacity; ++iterator) { - set_bucket bucket = &s->buckets[iterator]; + bucket b = &s->buckets[iterator]; - if (bucket->pairs != NULL) + if (b->pairs != NULL) { - free(bucket->pairs); + free(b->pairs); } } + /* Destroy all buckets from old set */ free(s->buckets); s->capacity = new_set.capacity; @@ -180,17 +212,13 @@ static int set_bucket_realloc(set s) int set_insert(set s, set_key key, set_value value) { set_hash h; - size_t index; - - set_bucket bucket; - - set_pair pair; + bucket b; + pair p; if (s == NULL || key == NULL || value == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set insertion parameters"); - return 1; } @@ -198,21 +226,19 @@ int set_insert(set s, set_key key, set_value value) index = h % s->capacity; - bucket = &s->buckets[index]; + b = &s->buckets[index]; - pair = set_bucket_get_pair(bucket, s->compare_cb, key); + p = bucket_get_pair(b, s->compare_cb, key); - if (pair != NULL) + if (p != NULL) { - pair->value = value; - + p->value = value; return 0; } - if (set_bucket_insert(bucket, key, value) != 0) + if (bucket_insert(b, key, value) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insertion"); - return 1; } @@ -227,8 +253,7 @@ int set_insert_array(set s, set_key keys[], set_value values[], size_t size) if (s == NULL || keys == NULL || values == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set insertion parameters"); - + log_write("metacall", LOG_LEVEL_ERROR, "Invalid set array insertion parameters"); return 1; } @@ -237,7 +262,6 @@ int set_insert_array(set s, set_key keys[], set_value values[], size_t size) if (set_insert(s, keys[iterator], values[iterator]) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set array insertion"); - return 1; } } @@ -249,17 +273,17 @@ set_value set_get(set s, set_key key) { if (s != NULL && key != NULL) { - set_hash hash = s->hash_cb(key); + set_hash h = s->hash_cb(key); - size_t index = hash % s->capacity; + size_t index = h % s->capacity; - set_bucket bucket = &s->buckets[index]; + bucket b = &s->buckets[index]; - set_pair pair = set_bucket_get_pair(bucket, s->compare_cb, key); + pair p = bucket_get_pair(b, s->compare_cb, key); - if (pair != NULL) + if (p != NULL) { - return pair->value; + return p->value; } } @@ -270,15 +294,15 @@ int set_contains(set s, set_key key) { if (s != NULL && key != NULL) { - set_hash hash = s->hash_cb(key); + set_hash h = s->hash_cb(key); - size_t index = hash % s->capacity; + size_t index = h % s->capacity; - set_bucket bucket = &s->buckets[index]; + bucket b = &s->buckets[index]; - set_pair pair = set_bucket_get_pair(bucket, s->compare_cb, key); + pair p = bucket_get_pair(b, s->compare_cb, key); - if (pair != NULL) + if (p != NULL) { return 0; } @@ -287,45 +311,67 @@ int set_contains(set s, set_key key) return 1; } -static int set_contains_any_cb_iterate(set s, set_key key, set_value value, set_cb_iterate_args args) +int set_contains_any(set dest, set src) { - set_contains_any_cb_iterator iterator = (set_contains_any_cb_iterator)args; + size_t bucket_iterator, pair_iterator; - (void)s; - (void)value; + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; - iterator->result = set_contains(iterator->s, key); + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; - return iterator->result; + if (set_contains(dest, p->key) == 0) + { + return 0; + } + } + } + } + + return 1; } -int set_contains_any(set dest, set src) +int set_contains_which(set dest, set src, set_key *key) { - struct set_contains_any_cb_iterator_type args = + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) { - dest, - 1 - }; + bucket b = &src->buckets[bucket_iterator]; - set_iterate(src, &set_contains_any_cb_iterate, (set_cb_iterate_args)&args); + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; - return args.result; + if (set_contains(dest, p->key) == 0) + { + *key = p->key; + return 0; + } + } + } + } + + return 1; } set_value set_remove(set s, set_key key) { set_hash h; - size_t index; - - set_bucket bucket; - + bucket b; set_value value = NULL; if (s == NULL || key == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set remove parameters"); - return NULL; } @@ -333,12 +379,11 @@ set_value set_remove(set s, set_key key) index = h % s->capacity; - bucket = &s->buckets[index]; + b = &s->buckets[index]; - if (set_bucket_remove(bucket, s->compare_cb, key, &value) != 0) + if (bucket_remove(b, s->compare_cb, key, &value) != 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove"); - + log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove: %p", key); return NULL; } @@ -347,7 +392,6 @@ set_value set_remove(set s, set_key key) if (set_bucket_realloc(s) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove reallocation"); - return NULL; } @@ -362,17 +406,17 @@ void set_iterate(set s, set_cb_iterate iterate_cb, set_cb_iterate_args args) for (bucket_iterator = 0; bucket_iterator < s->capacity; ++bucket_iterator) { - set_bucket bucket = &s->buckets[bucket_iterator]; + bucket b = &s->buckets[bucket_iterator]; - if (bucket->pairs != NULL && bucket->count > 0) + if (b->pairs != NULL && b->count > 0) { size_t pair_iterator; - for (pair_iterator = 0; pair_iterator < bucket->count; ++pair_iterator) + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) { - set_pair pair = &bucket->pairs[pair_iterator]; + pair p = &b->pairs[pair_iterator]; - if (iterate_cb(s, pair->key, pair->value, args) != 0) + if (iterate_cb(s, p->key, p->value, args) != 0) { return; } @@ -382,40 +426,54 @@ void set_iterate(set s, set_cb_iterate iterate_cb, set_cb_iterate_args args) } } -static int set_append_cb_iterate(set s, set_key key, set_value value, set_cb_iterate_args args) +int set_append(set dest, set src) { - set dest = (set)args; + size_t bucket_iterator, pair_iterator; - (void)s; - - return set_insert(dest, key, value); -} + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; -int set_append(set dest, set src) -{ - set_cb_iterate_args args = (set_cb_iterate_args)dest; + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; - set_iterate(src, &set_append_cb_iterate, args); + if (set_insert(dest, p->key, p->value) != 0) + { + return 1; + } + } + } + } return 0; } -static int set_disjoint_cb_iterate(set s, set_key key, set_value value, set_cb_iterate_args args) +int set_disjoint(set dest, set src) { - set dest = (set)args; + size_t bucket_iterator, pair_iterator; - set_value deleted = set_remove(dest, key); - - (void)s; + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; - return !(deleted == value); -} + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; -int set_disjoint(set dest, set src) -{ - set_cb_iterate_args args = (set_cb_iterate_args)dest; + set_value deleted = set_remove(dest, p->key); - set_iterate(src, &set_disjoint_cb_iterate, args); + if (deleted != p->value) + { + return 1; + } + } + } + } return 0; } @@ -433,11 +491,11 @@ int set_clear(set s) for (iterator = 0; iterator < s->capacity; ++iterator) { - set_bucket bucket = &s->buckets[iterator]; + bucket b = &s->buckets[iterator]; - if (bucket->pairs != NULL) + if (b->pairs != NULL) { - free(bucket->pairs); + free(b->pairs); } } @@ -446,13 +504,12 @@ int set_clear(set s) s->count = 0; s->prime = 0; - s->capacity = set_bucket_capacity(s->prime); - s->buckets = set_bucket_create(s->capacity); + s->capacity = bucket_capacity(s->prime); + s->buckets = bucket_create(s->capacity); if (s->buckets == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Bad set clear bucket creation"); - return 1; } @@ -472,11 +529,11 @@ void set_destroy(set s) for (iterator = 0; iterator < s->capacity; ++iterator) { - set_bucket bucket = &s->buckets[iterator]; + bucket b = &s->buckets[iterator]; - if (bucket->pairs != NULL) + if (b->pairs != NULL) { - free(bucket->pairs); + free(b->pairs); } } @@ -486,42 +543,41 @@ void set_destroy(set s) free(s); } -set_iterator set_iterator_begin(set s) +void set_iterator_begin(set_iterator it, set s) { - if (s != NULL && s->buckets != NULL && set_size(s) > 0) + if (it != NULL) { - set_iterator it = malloc(sizeof(struct set_iterator_type)); + it->current_bucket = 0; + it->current_pair = 0; - if (it != NULL) + if (s != NULL && s->buckets != NULL && set_size(s) > 0) { it->s = s; - it->bucket = 0; - it->pair = 0; set_iterator_next(it); - - return it; + } + else + { + it->s = NULL; } } - - return NULL; } -set_key set_iterator_get_key(set_iterator it) +set_key set_iterator_key(set_iterator it) { - if (it != NULL && it->bucket < it->s->capacity && it->pair > 0) + if (it != NULL && it->s != NULL && it->current_bucket < it->s->capacity && it->current_pair > 0) { - return it->s->buckets[it->bucket].pairs[it->pair - 1].key; + return it->s->buckets[it->current_bucket].pairs[it->current_pair - 1].key; } return NULL; } -set_value set_iterator_get_value(set_iterator it) +set_value set_iterator_value(set_iterator it) { - if (it != NULL && it->bucket < it->s->capacity && it->pair > 0) + if (it != NULL && it->s != NULL && it->current_bucket < it->s->capacity && it->current_pair > 0) { - return it->s->buckets[it->bucket].pairs[it->pair - 1].value; + return it->s->buckets[it->current_bucket].pairs[it->current_pair - 1].value; } return NULL; @@ -529,40 +585,38 @@ set_value set_iterator_get_value(set_iterator it) void set_iterator_next(set_iterator it) { - if (it != NULL) + if (it != NULL && it->s != NULL) { - for ( ; it->bucket < it->s->capacity; ++it->bucket) + for (; it->current_bucket < it->s->capacity; ++it->current_bucket) { - set_bucket bucket = &it->s->buckets[it->bucket]; + bucket b = &it->s->buckets[it->current_bucket]; - if (bucket->pairs != NULL && bucket->count > 0) + if (b->pairs != NULL && b->count > 0) { - for ( ; it->pair < bucket->count; ++it->pair) + for (; it->current_pair < b->count; ++it->current_pair) { - set_pair pair = &bucket->pairs[it->pair]; + pair p = &b->pairs[it->current_pair]; - if (pair != NULL) + if (p != NULL) { - ++it->pair; + ++it->current_pair; return; } } + + it->current_pair = 0; } } } } -int set_iterator_end(set_iterator * it) +int set_iterator_end(set_iterator it) { - if (it != NULL && *it != NULL) + if (it != NULL && it->s != NULL) { - if ((*it)->bucket >= (*it)->s->capacity) + if (it->current_bucket >= it->s->capacity) { - free(*it); - - *it = NULL; - return 0; } diff --git a/source/adt/source/adt_set_bucket.c b/source/adt/source/adt_set_bucket.c deleted file mode 100644 index 4514cc0e59..0000000000 --- a/source/adt/source/adt_set_bucket.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A abstract data type library providing generic containers. - * - */ - -/* -- Headers -- */ - -#include - -#include - -#include - -/* -- Definitions -- */ - -#define SET_BUCKET_PAIRS_DEFAULT ((size_t)0x04) - -/* -- Methods -- */ - -size_t set_bucket_capacity(size_t prime) -{ - static const size_t capacity_primes[] = - { - 5UL, - 11UL, - 23UL, - 47UL, - 97UL, - 199UL, - 409UL, - 823UL, - 1741UL, - 3469UL, - 6949UL, - 14033UL, - 28411UL, - 57557UL, - 116731UL, - 236897UL, - 480881UL, - 976369UL, - 1982627UL, - 4026031UL, - 8175383UL, - 16601593UL, - 33712729UL, - 68460391UL, - 139022417UL, - 282312799UL, - 573292817UL, - 1164186217UL, - 2364114217UL, - 4294967291UL, - - #if (SIZE_MAX > 0xFFFFFFFF) - (size_t)8589934583ULL, - (size_t)17179869143ULL, - (size_t)34359738337ULL, - (size_t)68719476731ULL, - (size_t)137438953447ULL, - (size_t)274877906899ULL, - (size_t)549755813881ULL, - (size_t)1099511627689ULL, - (size_t)2199023255531ULL, - (size_t)4398046511093ULL, - (size_t)8796093022151ULL, - (size_t)17592186044399ULL, - (size_t)35184372088777ULL, - (size_t)70368744177643ULL, - (size_t)140737488355213ULL, - (size_t)281474976710597ULL, - (size_t)562949953421231ULL, - (size_t)1125899906842597ULL, - (size_t)2251799813685119ULL, - (size_t)4503599627370449ULL, - (size_t)9007199254740881ULL, - (size_t)18014398509481951ULL, - (size_t)36028797018963913ULL, - (size_t)72057594037927931ULL, - (size_t)144115188075855859ULL, - (size_t)288230376151711717ULL, - (size_t)576460752303423433ULL, - (size_t)1152921504606846883ULL, - (size_t)2305843009213693951ULL, - (size_t)4611686018427387847ULL, - (size_t)9223372036854775783ULL, - (size_t)18446744073709551557ULL - #endif - }; - - static const size_t capacity_primes_size = sizeof(capacity_primes) / sizeof(capacity_primes[0]); - - if (prime < capacity_primes_size) - { - return capacity_primes[prime]; - } - - return 0; -} - -set_bucket set_bucket_create(size_t size) -{ - set_bucket buckets; - - size_t iterator; - - if (size == 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket size: %" PRIuS, size); - - return NULL; - } - - buckets = malloc(sizeof(struct set_bucket_type) * size); - - if (buckets == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Bad allocation for set bucket"); - - return NULL; - } - - for (iterator = 0; iterator < size; ++iterator) - { - buckets[iterator].count = 0; - buckets[iterator].capacity = 0; - buckets[iterator].pairs = NULL; - } - - return buckets; -} - -int set_bucket_alloc_pairs(set_bucket bucket, size_t capacity) -{ - if (bucket == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket"); - - return 1; - } - - if (bucket->pairs != NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Set bucket pairs already allocated"); - - return 1; - } - - bucket->pairs = malloc(sizeof(struct set_pair_type) * capacity); - - if (bucket->pairs == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Bad set pairs allocation"); - - return 1; - } - - bucket->count = 0; - bucket->capacity = capacity; - - return 0; -} - -int set_bucket_realloc_pairs(set_bucket bucket, size_t new_capacity) -{ - set_pair pairs = realloc(bucket->pairs, sizeof(struct set_pair_type) * new_capacity); - - if (pairs == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Bad set pairs reallocation"); - - return 1; - } - - bucket->pairs = pairs; - bucket->capacity = new_capacity; - - return 0; -} - -set_pair set_bucket_get_pair(set_bucket bucket, comparable_callback compare_cb, void * key) -{ - size_t iterator; - - if (bucket->pairs == NULL || bucket->count == 0) - { - return NULL; - } - - for (iterator = 0; iterator < bucket->count; ++iterator) - { - set_pair pair = &bucket->pairs[iterator]; - - if (compare_cb(key, pair->key) == 0) - { - return pair; - } - } - - return NULL; -} - -int set_bucket_insert(set_bucket bucket, void * key, void * value) -{ - set_pair pair; - - if (bucket == NULL || key == NULL || value == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insert parameters"); - - return 1; - } - - if (bucket->pairs == NULL && set_bucket_alloc_pairs(bucket, SET_BUCKET_PAIRS_DEFAULT) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insertion pairs allocation"); - - return 1; - } - - if ((bucket->count + 1) >= bucket->capacity) - { - if (set_bucket_realloc_pairs(bucket, bucket->capacity << 1) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insertion pairs reallocation"); - - return 1; - } - } - - pair = &bucket->pairs[bucket->count]; - - pair->key = key; - pair->value = value; - - ++bucket->count; - - return 0; -} - -int set_bucket_remove(set_bucket bucket, comparable_callback compare_cb, void * key, void ** value) -{ - size_t iterator; - - if (bucket == NULL || compare_cb == NULL || key == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove parameters"); - - return 1; - } - - if (bucket->pairs == NULL || bucket->count == 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove pairs"); - - return 1; - } - - for (iterator = 0; iterator < bucket->count; ++iterator) - { - set_pair pair = &bucket->pairs[iterator]; - - if (compare_cb(key, pair->key) == 0) - { - void * deleted_value = pair->value; - - size_t next = iterator + 1; - - size_t new_capacity = bucket->capacity >> 1; - - memmove(pair, &bucket->pairs[next], sizeof(struct set_pair_type) * (bucket->count - next)); - - --bucket->count; - - if (bucket->count <= new_capacity && new_capacity > SET_BUCKET_PAIRS_DEFAULT) - { - if (set_bucket_realloc_pairs(bucket, new_capacity) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove pairs reallocation"); - - return 1; - } - } - - *value = deleted_value; - - return 0; - } - } - - return 1; -} diff --git a/source/adt/source/adt_trie.c b/source/adt/source/adt_trie.c index 1bc17b2b15..047c20a698 100644 --- a/source/adt/source/adt_trie.c +++ b/source/adt/source/adt_trie.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,14 @@ /* -- Headers -- */ -#include #include +#include #include /* -- Definitions -- */ -#define TRIE_CAPACITY_MIN ((size_t)0x10) +#define TRIE_CAPACITY_MIN ((size_t)0x10) /* -- Forward Declarations -- */ @@ -45,71 +45,71 @@ struct trie_node_suffixes_iterator_args_type; /* -- Type Definitions -- */ -typedef struct trie_node_ref_type * trie_node_ref; +typedef struct trie_node_ref_type *trie_node_ref; -typedef struct trie_node_free_type * trie_node_free; +typedef struct trie_node_free_type *trie_node_free; -typedef struct trie_node_type * trie_node; +typedef struct trie_node_type *trie_node; -typedef struct trie_node_set_iterator_args_type * trie_node_set_iterator_args; +typedef struct trie_node_set_iterator_args_type *trie_node_set_iterator_args; -typedef struct trie_node_append_iterator_args_type * trie_node_append_iterator_args; +typedef struct trie_node_append_iterator_args_type *trie_node_append_iterator_args; -typedef struct trie_node_suffixes_iterator_args_type * trie_node_suffixes_iterator_args; +typedef struct trie_node_suffixes_iterator_args_type *trie_node_suffixes_iterator_args; /* -- Member Data -- */ struct trie_node_ref_type { - trie_key key; /**< Pointer to the key of the node */ - size_t index; /**< Reference to node in trie set */ + trie_key key; /**< Pointer to the key of the node */ + size_t index; /**< Reference to node in trie set */ }; struct trie_node_free_type { - trie_node_free next; /**< Reference to the next node in the list */ - size_t index; /**< Reference to node in trie set */ + trie_node_free next; /**< Reference to the next node in the list */ + size_t index; /**< Reference to node in trie set */ }; struct trie_node_type { - size_t parent_index; /**< Reference to parent trie node */ - size_t self_index; /**< Reference to itself inside trie node list */ - trie_key key; /**< Pointer to key of the node */ - trie_value value; /**< Pointer to data of the node */ - set childs; /**< Set with references to child trie nodes (trie_key -> trie_node_ref) */ + size_t parent_index; /**< Reference to parent trie node */ + size_t self_index; /**< Reference to itself inside trie node list */ + trie_key key; /**< Pointer to key of the node */ + trie_value value; /**< Pointer to data of the node */ + set childs; /**< Set with references to child trie nodes (trie_key -> trie_node_ref) */ }; struct trie_type { - trie_node root; /**< Trie root node */ - trie_node node_list; /**< Array of trie nodes */ - size_t size; /**< Size of current nodes inside node list */ - size_t capacity; /**< Size of allocated nodes in memory */ - trie_node_free free_node_list; /**< List of free nodes in array (size_t) */ - size_t key_limit; /**< Maximum number of childs per trie node (0 == disabled) */ - size_t depth_limit; /**< Maximum number of depth levels in a trie (0 == disabled) */ - trie_cb_hash hash_cb; /**< Hash callback for node insertion */ - trie_cb_compare compare_cb; /**< Compare callback for value comparison */ + trie_node root; /**< Trie root node */ + trie_node node_list; /**< Array of trie nodes */ + size_t size; /**< Size of current nodes inside node list */ + size_t capacity; /**< Size of allocated nodes in memory */ + trie_node_free free_node_list; /**< List of free nodes in array (size_t) */ + size_t key_limit; /**< Maximum number of childs per trie node (0 == disabled) */ + size_t depth_limit; /**< Maximum number of depth levels in a trie (0 == disabled) */ + trie_cb_hash hash_cb; /**< Hash callback for node insertion */ + trie_cb_compare compare_cb; /**< Compare callback for value comparison */ }; struct trie_node_set_iterator_args_type { - trie t; /**< Pointer to trie */ - trie_cb_iterate iterate_cb; /**< Pointer to iterator callback */ + trie t; /**< Pointer to trie */ + trie_cb_iterate iterate_cb; /**< Pointer to iterator callback */ trie_cb_iterate_args args; /**< Pointer to iterator arguments */ }; struct trie_node_append_iterator_args_type { - trie dest; /**< Pointer to destination trie */ - vector prefixes; /**< Vector containing prefixes for each iteration */ + trie dest; /**< Pointer to destination trie */ + vector prefixes; /**< Vector containing prefixes for each iteration */ }; struct trie_node_suffixes_iterator_args_type { - trie suffix_trie; /**< Pointer to new suffix trie */ - vector prefixes; /**< Vector containing prefixes for each iteration */ + trie suffix_trie; /**< Pointer to new suffix trie */ + vector prefixes; /**< Vector containing prefixes for each iteration */ }; /* -- Private Methods -- */ @@ -272,7 +272,7 @@ trie_node trie_node_insert(trie t, trie_node parent, trie_key key, trie_value va { if ((t->size + 1) >= t->capacity) { - register void * node_list; + register void *node_list; size_t capacity = t->capacity << 1; @@ -349,7 +349,7 @@ int trie_insert(trie t, vector keys, trie_value value) for (iterator = 0; current_node != NULL && iterator < size; ++iterator) { - trie_key * key_ptr = vector_at(keys, iterator); + trie_key *key_ptr = vector_at(keys, iterator); trie_node next_node = NULL; @@ -401,7 +401,7 @@ trie_node trie_node_get(trie t, vector keys) for (iterator = 0; current_node != NULL && iterator < size; ++iterator) { - trie_key * key_ptr = vector_at(keys, iterator); + trie_key *key_ptr = vector_at(keys, iterator); trie_node next_node = NULL; @@ -514,7 +514,7 @@ void trie_node_iterate(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_ while (vector_size(node_stack) > 0) { - trie_node * back_ptr = vector_back(node_stack); + trie_node *back_ptr = vector_back(node_stack); vector_pop_back(node_stack); @@ -524,10 +524,11 @@ void trie_node_iterate(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_ if (back->childs != NULL) { - set_iterator it; - for (it = set_iterator_begin(back->childs); set_iterator_end(&it) > 0; set_iterator_next(it)) + struct set_iterator_type it; + + for (set_iterator_begin(&it, back->childs); set_iterator_end(&it) > 0; set_iterator_next(&it)) { - trie_node_ref ref_node = set_iterator_get_value(it); + trie_node_ref ref_node = set_iterator_value(&it); trie_node current_node = &t->node_list[ref_node->index]; @@ -597,7 +598,7 @@ int trie_node_clear(trie t, trie_node n) while (vector_size(node_stack) > 0) { - trie_node * back_ptr = vector_back(node_stack); + trie_node *back_ptr = vector_back(node_stack); vector_pop_back(node_stack); @@ -609,11 +610,11 @@ int trie_node_clear(trie t, trie_node n) if (back->childs != NULL) { - set_iterator it; + struct set_iterator_type it; - for (it = set_iterator_begin(back->childs); set_iterator_end(&it) > 0; set_iterator_next(it)) + for (set_iterator_begin(&it, back->childs); set_iterator_end(&it) > 0; set_iterator_next(&it)) { - trie_node_ref ref_node = set_iterator_get_value(it); + trie_node_ref ref_node = set_iterator_value(&it); trie_node current_node = &t->node_list[ref_node->index]; @@ -674,21 +675,20 @@ trie_node trie_node_find(trie t, trie_key key) while (vector_size(node_stack) > 0) { - trie_node * back_ptr = vector_back(node_stack); + trie_node *back_ptr = vector_back(node_stack); vector_pop_back(node_stack); if (back_ptr != NULL && *back_ptr != NULL) { trie_node back = *back_ptr; - - set_iterator it = NULL; + struct set_iterator_type it; if (back->childs != NULL) { - for (it = set_iterator_begin(back->childs); set_iterator_end(&it) > 0; set_iterator_next(it)) + for (set_iterator_begin(&it, back->childs); set_iterator_end(&it) > 0; set_iterator_next(&it)) { - trie_node_ref ref_node = set_iterator_get_value(it); + trie_node_ref ref_node = set_iterator_value(&it); trie_node current_node = &t->node_list[ref_node->index]; @@ -698,9 +698,10 @@ trie_node trie_node_find(trie t, trie_key key) if (back->key != NULL && t->compare_cb(back->key, key) == 0) { + /* TODO: it may be un-initialized here */ while (set_iterator_end(&it) > 0) { - set_iterator_next(it); + set_iterator_next(&it); } vector_destroy(node_stack); diff --git a/source/adt/source/adt_vector.c b/source/adt/source/adt_vector.c index bf72ed632a..0047cc1185 100644 --- a/source/adt/source/adt_vector.c +++ b/source/adt/source/adt_vector.c @@ -1,9 +1,21 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * * A abstract data type library providing generic containers. * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ /* -- Headers -- */ @@ -16,18 +28,18 @@ /* -- Definitions -- */ -#define VECTOR_CAPACITY_INCREMENT 2 /**< Capacity increment */ -#define VECTOR_CAPACITY_MIN 16 /**< Minimum capacity */ -#define VECTOR_CAPACITY_MIN_USED 8 /**< Max unused capacity since free memory */ +#define VECTOR_CAPACITY_INCREMENT 2 /**< Capacity increment */ +#define VECTOR_CAPACITY_MIN 16 /**< Minimum capacity */ +#define VECTOR_CAPACITY_MIN_USED 8 /**< Max unused capacity since free memory */ /* -- Member Data -- */ struct vector_type { - size_t type_size; /**< Size of element type */ - size_t capacity; /**< Allocated capacity of the vector */ - size_t size; /**< Amount of actual elements cointained in vector */ - void * data; /**< Pointer to memory block */ + size_t type_size; /**< Size of element type */ + size_t capacity; /**< Allocated capacity of the vector */ + size_t size; /**< Amount of actual elements cointained in vector */ + void *data; /**< Pointer to memory block */ }; /* -- Private Methods -- */ @@ -45,15 +57,15 @@ struct vector_type * @return * A pointer to data vector memory block with offset @bytes */ -static void * vector_data_offset_bytes(vector v, size_t bytes); +static void *vector_data_offset_bytes(vector v, size_t bytes); /* -- Methods -- */ -void * vector_data_offset_bytes(vector v, size_t bytes) +void *vector_data_offset_bytes(vector v, size_t bytes) { if (v != NULL && v->data != NULL && bytes < v->capacity * v->type_size) { - return ((void*)(((char*)v->data) + (bytes))); + return ((void *)(((char *)v->data) + (bytes))); } return NULL; @@ -139,7 +151,7 @@ int vector_reserve(vector v, size_t capacity) { if (v != NULL && capacity != v->capacity) { - register void * data; + register void *data; if (capacity < VECTOR_CAPACITY_MIN) { @@ -227,7 +239,7 @@ size_t vector_type_size(vector v) return 0; } -void * vector_front(vector v) +void *vector_front(vector v) { if (v != NULL) { @@ -237,7 +249,7 @@ void * vector_front(vector v) return NULL; } -void * vector_back(vector v) +void *vector_back(vector v) { if (v != NULL) { @@ -247,7 +259,7 @@ void * vector_back(vector v) return NULL; } -void * vector_at(vector v, size_t position) +void *vector_at(vector v, size_t position) { if (v != NULL) { @@ -257,7 +269,7 @@ void * vector_at(vector v, size_t position) return NULL; } -void vector_set(vector v, size_t position, void * element) +void vector_set(vector v, size_t position, void *element) { if (v != NULL && position < v->capacity && element != NULL) { @@ -283,7 +295,7 @@ void vector_push_back_empty(vector v) } } -void vector_push_back(vector v, void * element) +void vector_push_back(vector v, void *element) { if (v != NULL) { @@ -337,7 +349,7 @@ void vector_push_front_empty(vector v) } } -void vector_push_front(vector v, void * element) +void vector_push_front(vector v, void *element) { if (v != NULL && element != NULL) { @@ -398,7 +410,7 @@ void vector_insert_empty(vector v, size_t position) } } -void vector_insert(vector v, size_t position, void * element) +void vector_insert(vector v, size_t position, void *element) { if (v != NULL) { diff --git a/source/benchmarks/CMakeLists.txt b/source/benchmarks/CMakeLists.txt index 50d7e71eea..107a348e81 100644 --- a/source/benchmarks/CMakeLists.txt +++ b/source/benchmarks/CMakeLists.txt @@ -18,7 +18,7 @@ if(NOT GBENCH_FOUND) include(InstallGBench) if(NOT GBENCH_FOUND) - message(STATUS "GBench libraries not found") + message(SEND_ERROR "GBench libraries not found") return() endif() @@ -49,12 +49,17 @@ if(GBENCH_INSTALL) add_dependencies(GBench google-bench-depends) endif() +# Create output directory +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/benchmarks) + # # Benchmarks # include(CTest) +add_subdirectory(set_bench) +add_subdirectory(log_bench) add_subdirectory(metacall_py_c_api_bench) add_subdirectory(metacall_py_call_bench) add_subdirectory(metacall_py_init_bench) diff --git a/source/benchmarks/log_bench/CMakeLists.txt b/source/benchmarks/log_bench/CMakeLists.txt new file mode 100644 index 0000000000..f1ba5575ea --- /dev/null +++ b/source/benchmarks/log_bench/CMakeLists.txt @@ -0,0 +1,146 @@ +# +# Executable name and options +# + +# Target name +set(target log-bench) +message(STATUS "Benchmark ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/log_bench.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GBench + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + log +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/benchmarks/log_bench/source/log_bench.cpp b/source/benchmarks/log_bench/source/log_bench.cpp new file mode 100644 index 0000000000..75bdb547bd --- /dev/null +++ b/source/benchmarks/log_bench/source/log_bench.cpp @@ -0,0 +1,101 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +static int stream_write(void *, const char *, const size_t) +{ + // Disable stream write so we do not count stdout on the benchmark + return 0; +} + +static int stream_flush(void *) +{ + // Disable stream flush so we do not count stdout on the benchmark + return 0; +} + +class log_bench : public benchmark::Fixture +{ +public: + void SetUp(benchmark::State &state) + { + if (log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_custom(NULL, &stream_write, &stream_flush)) != 0) + { + state.SkipWithError("Error creating the log"); + } + } + + void TearDown(benchmark::State &) + { + } +}; + +BENCHMARK_DEFINE_F(log_bench, call_macro) +(benchmark::State &state) +{ + const int64_t call_count = 10000; + + for (auto _ : state) + { + for (int64_t it = 0; it < call_count; ++it) + { + log_write("metacall", LOG_LEVEL_ERROR, "Message"); + } + } + + state.SetLabel("Log Benchmark - Call Macro"); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(log_bench, call_macro) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(3); + +BENCHMARK_DEFINE_F(log_bench, call_va) +(benchmark::State &state) +{ + const int64_t call_count = 10000; + + for (auto _ : state) + { + for (int64_t it = 0; it < call_count; ++it) + { + log_write_impl_va("metacall", LOG_PREPROCESSOR_LINE, log_record_function(), __FILE__, LOG_LEVEL_ERROR, "Message"); + } + } + + state.SetLabel("Log Benchmark - Call Variadic"); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(log_bench, call_va) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(3); + +BENCHMARK_MAIN(); diff --git a/source/benchmarks/metacall_cs_call_bench/CMakeLists.txt b/source/benchmarks/metacall_cs_call_bench/CMakeLists.txt index e28998d0eb..80db3ce393 100644 --- a/source/benchmarks/metacall_cs_call_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_cs_call_bench/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS) + return() +endif() + # # Executable name and options # @@ -79,20 +84,6 @@ target_link_libraries(${target} GBench - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -118,7 +109,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -127,8 +118,28 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with sanitizers: + # Tracer caught signal 11: addr=0x5000002b0 pc=0x7f614774a0f0 sp=0x7f609fb40d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader ) # diff --git a/source/benchmarks/metacall_cs_call_bench/source/metacall_cs_call_bench.cpp b/source/benchmarks/metacall_cs_call_bench/source/metacall_cs_call_bench.cpp index 05cff1a8a3..179dfaf6c0 100644 --- a/source/benchmarks/metacall_cs_call_bench/source/metacall_cs_call_bench.cpp +++ b/source/benchmarks/metacall_cs_call_bench/source/metacall_cs_call_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,21 +28,20 @@ class metacall_cs_call_bench : public benchmark::Fixture public: }; -BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_va_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_va_args) +(benchmark::State &state) { const int64_t call_count = 500000; const int64_t call_size = sizeof(int) * 3; // (int, int) -> int for (auto _ : state) { - /* CSharp */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* CSharp */ +#if defined(OPTION_BUILD_LOADERS_CS) { - void * ret; - for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacall("sum", 0, 0)); + void *ret = metacall("sum", 0, 0); state.PauseTiming(); @@ -61,7 +60,7 @@ BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_va_args)(benchmark::State & stat state.ResumeTiming(); } } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ } state.SetLabel("MetaCall CSharp Call Benchmark - Variadic Argument Call"); @@ -70,27 +69,24 @@ BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_va_args)(benchmark::State & stat } BENCHMARK_REGISTER_F(metacall_cs_call_bench, call_va_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_array_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_array_args) +(benchmark::State &state) { const int64_t call_count = 500000; const int64_t call_size = sizeof(int) * 3; // (int, int) -> int for (auto _ : state) { - /* CSharp */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* CSharp */ +#if defined(OPTION_BUILD_LOADERS_CS) { - void * ret; - state.PauseTiming(); - void * args[2] = - { + void *args[2] = { metacall_value_create_int(0), metacall_value_create_int(0) }; @@ -99,7 +95,7 @@ BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_array_args)(benchmark::State & s for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacallv("sum", args)); + void *ret = metacallv("sum", args); state.PauseTiming(); @@ -127,7 +123,7 @@ BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_array_args)(benchmark::State & s state.ResumeTiming(); } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ } state.SetLabel("MetaCall CSharp Call Benchmark - Array Argument Call"); @@ -136,7 +132,6 @@ BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_array_args)(benchmark::State & s } BENCHMARK_REGISTER_F(metacall_cs_call_bench, call_array_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); @@ -144,7 +139,7 @@ BENCHMARK_REGISTER_F(metacall_cs_call_bench, call_array_args) /* TODO: NetCore re-initialization */ /* BENCHMARK_MAIN(); */ -int main(int argc, char ** argv) +int main(int argc, char **argv) { ::benchmark::Initialize(&argc, argv); @@ -166,8 +161,8 @@ int main(int argc, char ** argv) return 1; } - /* CSharp */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* CSharp */ +#if defined(OPTION_BUILD_LOADERS_CS) { static const char tag[] = "cs"; @@ -187,9 +182,11 @@ int main(int argc, char ** argv) return 1; } } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ ::benchmark::RunSpecifiedBenchmarks(); - return metacall_destroy(); + metacall_destroy(); + + return 0; } diff --git a/source/benchmarks/metacall_node_call_bench/CMakeLists.txt b/source/benchmarks/metacall_node_call_bench/CMakeLists.txt index f2b62cd730..16ae8e7684 100644 --- a/source/benchmarks/metacall_node_call_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_node_call_bench/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + # # Executable name and options # @@ -79,20 +84,6 @@ target_link_libraries(${target} GBench - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -116,9 +107,9 @@ target_compile_options(${target} # # Linker options -# +# -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -129,6 +120,15 @@ target_link_libraries(${target} add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader ) # diff --git a/source/benchmarks/metacall_node_call_bench/source/metacall_node_call_bench.cpp b/source/benchmarks/metacall_node_call_bench/source/metacall_node_call_bench.cpp index dec8d8586a..16d2c987b5 100644 --- a/source/benchmarks/metacall_node_call_bench/source/metacall_node_call_bench.cpp +++ b/source/benchmarks/metacall_node_call_bench/source/metacall_node_call_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,25 +28,26 @@ class metacall_node_call_bench : public benchmark::Fixture public: }; -BENCHMARK_DEFINE_F(metacall_node_call_bench, call_va_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_node_call_bench, call_va_args) +(benchmark::State &state) { const int64_t call_count = 100000; const int64_t call_size = sizeof(double) * 3; // (double, double) -> double - const enum metacall_value_id int_mem_type_ids[] = - { + const enum metacall_value_id int_mem_type_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; + // Print memory usage + // metacall_value_destroy(metacall("mem_check")); + for (auto _ : state) { - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - void * ret; - for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacallt("int_mem_type", int_mem_type_ids, 0.0, 0.0)); + void *ret = metacallt("int_mem_type", int_mem_type_ids, 0.0, 0.0); state.PauseTiming(); @@ -65,7 +66,7 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_va_args)(benchmark::State & st state.ResumeTiming(); } } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ } state.SetLabel("MetaCall NodeJS Call Benchmark - Variadic Argument Call"); @@ -74,27 +75,27 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_va_args)(benchmark::State & st } BENCHMARK_REGISTER_F(metacall_node_call_bench, call_va_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(3); -BENCHMARK_DEFINE_F(metacall_node_call_bench, call_array_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_node_call_bench, call_array_args) +(benchmark::State &state) { const int64_t call_count = 100000; const int64_t call_size = sizeof(double) * 3; // (double, double) -> double + // Print memory usage + // metacall_value_destroy(metacall("mem_check")); + for (auto _ : state) { - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - void * ret; - state.PauseTiming(); - void * args[2] = - { + void *args[2] = { metacall_value_create_double(0.0), metacall_value_create_double(0.0) }; @@ -103,7 +104,7 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_array_args)(benchmark::State & for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacallv("int_mem_type", args)); + void *ret = metacallv("int_mem_type", args); state.PauseTiming(); @@ -131,7 +132,7 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_array_args)(benchmark::State & state.ResumeTiming(); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ } state.SetLabel("MetaCall NodeJS Call Benchmark - Array Argument Call"); @@ -140,27 +141,27 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_array_args)(benchmark::State & } BENCHMARK_REGISTER_F(metacall_node_call_bench, call_array_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(3); -BENCHMARK_DEFINE_F(metacall_node_call_bench, call_async)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_node_call_bench, call_async) +(benchmark::State &state) { const int64_t call_count = 100000; const int64_t call_size = sizeof(double) * 3; // (double, double) -> double + // Print memory usage + // metacall_value_destroy(metacall("mem_check")); + for (auto _ : state) { - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - void * ret; - state.PauseTiming(); - void * args[2] = - { + void *args[2] = { metacall_value_create_double(0.0), metacall_value_create_double(0.0) }; @@ -169,18 +170,20 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_async)(benchmark::State & stat for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacall_await("int_mem_async_type", args, [](void * result, void * data) -> void * { - benchmark::State * state = static_cast(data); + void *ret = metacall_await( + "int_mem_async_type", args, [](void *result, void *data) -> void * { + benchmark::State *state = static_cast(data); - if (metacall_value_to_double(result) != 0.0) - { - state->SkipWithError("Invalid return value from int_mem_async_type"); - } + if (metacall_value_to_double(result) != 0.0) + { + state->SkipWithError("Invalid return value from int_mem_async_type"); + } - state->PauseTiming(); + state->PauseTiming(); - return NULL; - }, NULL, static_cast(&state))); + return NULL; + }, + NULL, static_cast(&state)); if (ret == NULL) { @@ -206,7 +209,7 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_async)(benchmark::State & stat state.ResumeTiming(); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ } state.SetLabel("MetaCall NodeJS Call Benchmark - Async Call"); @@ -215,7 +218,6 @@ BENCHMARK_DEFINE_F(metacall_node_call_bench, call_async)(benchmark::State & stat } BENCHMARK_REGISTER_F(metacall_node_call_bench, call_async) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(3); @@ -223,7 +225,7 @@ BENCHMARK_REGISTER_F(metacall_node_call_bench, call_async) /* TODO: NodeJS re-initialization */ /* BENCHMARK_MAIN(); */ -int main(int argc, char ** argv) +int main(int argc, char **argv) { ::benchmark::Initialize(&argc, argv); @@ -245,14 +247,26 @@ int main(int argc, char ** argv) return 1; } - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { static const char tag[] = "node"; static const char int_mem_type[] = "#!/usr/bin/env node\n" + "function mem_check() {\n" + " const formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;\n" + " const memoryData = process.memoryUsage();\n" + " const memoryUsage = {\n" + " rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated for the process execution`,\n" + " heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`,\n" + " heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`,\n" + " external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`,\n" + " };\n" + " console.log(memoryUsage);\n" + "}\n" "module.exports = {\n" + " mem_check,\n" " int_mem_type: (left, right) => 0,\n" " int_mem_async_type: async (left, right) => new Promise(resolve => 0),\n" "};\n"; @@ -262,10 +276,23 @@ int main(int argc, char ** argv) metacall_destroy(); return 1; } + + // Print memory usage + metacall_value_destroy(metacall("mem_check")); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ ::benchmark::RunSpecifiedBenchmarks(); - return metacall_destroy(); +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + // Print memory usage + metacall_value_destroy(metacall("mem_check")); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + + return 0; } diff --git a/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt b/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt index 6880e4f15a..08605fb08d 100644 --- a/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt @@ -1,11 +1,15 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # External dependencies # -find_package(PythonLibs 3 REQUIRED) +find_package(Python3 COMPONENTS Development) -if(NOT PYTHONLIBS_FOUND) - message(STATUS "Python libraries not found") +if(NOT Python3_Development_FOUND) return() endif() @@ -79,7 +83,7 @@ target_include_directories(${target} ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include - ${PYTHON_INCLUDE_DIRS} # Python includes + ${Python3_INCLUDE_DIRS} # Python includes ) # @@ -92,7 +96,7 @@ target_link_libraries(${target} GBench - ${PYTHON_LIBRARIES} # Python libraries + ${Python3_LIBRARIES} # Python libraries ) # @@ -117,7 +121,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -126,8 +130,27 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # ERROR: LeakSanitizer: detected memory leaks + # + # Direct leak of 449717 byte(s) in 224 object(s) allocated from: + # #0 0x7fd0798519cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fd078cf29c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # Indirect leak of 108494 byte(s) in 110 object(s) allocated from: + # #0 0x7fd0798519cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fd078cf29c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # SUMMARY: AddressSanitizer: 558211 byte(s) leaked in 334 allocation(s). + # + # For solving this, we should enable Python support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json ) # diff --git a/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp b/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp index ef64971e8a..e1db6bb14b 100644 --- a/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp +++ b/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,26 +25,26 @@ class metacall_py_c_api_bench : public benchmark::Fixture { public: - void SetUp(benchmark::State & state) + void SetUp(benchmark::State &state) { static const char buffer[] = "#!/usr/bin/env python3\n" "def int_mem_type(left: int, right: int) -> int:\n" "\treturn 0;"; - - static const char name[] = "int_mem_type"; - PyObject * dict; + static const char name[] = "int_mem_type"; if (Py_IsInitialized() == 0) { Py_InitializeEx(0); } +#if !(PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9) if (PyEval_ThreadsInitialized() == 0) { PyEval_InitThreads(); } +#endif compiled = Py_CompileString(buffer, name, Py_file_input); @@ -52,21 +52,24 @@ class metacall_py_c_api_bench : public benchmark::Fixture dict = PyModule_GetDict(instance); + Py_INCREF(dict); + func = PyDict_GetItemString(dict, "int_mem_type"); + Py_INCREF(func); + if (!PyCallable_Check(func)) { state.SkipWithError("An error ocurred during script loading"); } - - Py_DECREF(dict); } - void TearDown(benchmark::State & state) + void TearDown(benchmark::State &state) { - Py_DECREF(compiled); - Py_DECREF(instance); Py_DECREF(func); + Py_DECREF(dict); + Py_DECREF(instance); + Py_DECREF(compiled); if (Py_IsInitialized() != 0) { @@ -80,26 +83,25 @@ class metacall_py_c_api_bench : public benchmark::Fixture } protected: - PyObject * compiled; - PyObject * instance; - PyObject * func; + PyObject *compiled; + PyObject *instance; + PyObject *dict; + PyObject *func; }; -BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(long) * 3; // (long, long) -> long for (auto _ : state) { - PyObject * ret; - state.PauseTiming(); - PyObject * tuple_args = PyTuple_New(2); + PyObject *tuple_args = PyTuple_New(2); - PyObject * args[2] = - { + PyObject *args[2] = { PyLong_FromLong(0L), PyLong_FromLong(0L) }; @@ -111,7 +113,7 @@ BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & stat for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = PyObject_CallObject(func, tuple_args)); + PyObject *ret = PyObject_CallObject(func, tuple_args); state.PauseTiming(); @@ -132,8 +134,6 @@ BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & stat state.PauseTiming(); - Py_DECREF(args[0]); - Py_DECREF(args[1]); Py_DECREF(tuple_args); state.ResumeTiming(); @@ -145,9 +145,8 @@ BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & stat } BENCHMARK_REGISTER_F(metacall_py_c_api_bench, call_object) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_MAIN(); \ No newline at end of file +BENCHMARK_MAIN(); diff --git a/source/benchmarks/metacall_py_call_bench/CMakeLists.txt b/source/benchmarks/metacall_py_call_bench/CMakeLists.txt index 3896abe0ab..9ed6744229 100644 --- a/source/benchmarks/metacall_py_call_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_py_call_bench/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # Executable name and options # @@ -79,20 +84,6 @@ target_link_libraries(${target} GBench - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -116,9 +107,9 @@ target_compile_options(${target} # # Linker options -# +# -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -129,6 +120,15 @@ target_link_libraries(${target} add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader ) # diff --git a/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp b/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp index d26fff6eae..68b0bf5890 100644 --- a/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp +++ b/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,59 +26,22 @@ class metacall_py_call_bench : public benchmark::Fixture { public: - void SetUp(benchmark::State & state) - { - metacall_print_info(); - - metacall_log_null(); - - if (metacall_initialize() != 0) - { - state.SkipWithError("Error initializing MetaCall"); - } - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) - { - static const char tag[] = "py"; - - static const char int_mem_type[] = - "#!/usr/bin/env python3\n" - "def int_mem_type(left: int, right: int) -> int:\n" - "\treturn 0;"; - - if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) - { - state.SkipWithError("Error loading int_mem_type function"); - } - } - #endif /* OPTION_BUILD_LOADERS_PY */ - } - - void TearDown(benchmark::State & state) - { - if (metacall_destroy() != 0) - { - state.SkipWithError("Error destroying MetaCall"); - } - } }; -BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(long) * 3; // (long, long) -> long for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - void * ret; - for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacall("int_mem_type", 0L, 0L)); + void *ret = metacall("int_mem_type", 0L, 0L); state.PauseTiming(); @@ -97,7 +60,7 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args)(benchmark::State & stat state.ResumeTiming(); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Call Benchmark - Variadic Argument Call"); @@ -106,27 +69,24 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args)(benchmark::State & stat } BENCHMARK_REGISTER_F(metacall_py_call_bench, call_va_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(long) * 3; // (long, long) -> long for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - void * ret; - state.PauseTiming(); - void * args[2] = - { + void *args[2] = { metacall_value_create_long(0L), metacall_value_create_long(0L) }; @@ -135,7 +95,7 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & s for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacallv("int_mem_type", args)); + void *ret = metacallv("int_mem_type", args); state.PauseTiming(); @@ -163,7 +123,7 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & s state.ResumeTiming(); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Call Benchmark - Array Argument Call"); @@ -172,9 +132,53 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & s } BENCHMARK_REGISTER_F(metacall_py_call_bench, call_array_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_MAIN(); +/* Use main for initializing MetaCall once. There's a bug in Python async which prevents reinitialization */ +/* https://github.com/python/cpython/issues/89425 */ +/* https://bugs.python.org/issue45262 */ +/* metacall-py-call-benchd: ./Modules/_asynciomodule.c:261: get_running_loop: Assertion `Py_IS_TYPE(rl, &PyRunningLoopHolder_Type)' failed. */ +int main(int argc, char *argv[]) +{ + metacall_print_info(); + + metacall_log_null(); + + if (metacall_initialize() != 0) + { + return 1; + } + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char tag[] = "py"; + + static const char int_mem_type[] = + "#!/usr/bin/env python3\n" + "def int_mem_type(left: int, right: int) -> int:\n" + "\treturn 0;"; + + if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) + { + return 2; + } + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + ::benchmark::Initialize(&argc, argv); + + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) + { + return 3; + } + + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + + metacall_destroy(); + + return 0; +} diff --git a/source/benchmarks/metacall_py_init_bench/CMakeLists.txt b/source/benchmarks/metacall_py_init_bench/CMakeLists.txt index bd4173d03a..eb9ecb2c79 100644 --- a/source/benchmarks/metacall_py_init_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_py_init_bench/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # Executable name and options # @@ -79,20 +84,6 @@ target_link_libraries(${target} GBench - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -118,7 +109,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -129,6 +120,15 @@ target_link_libraries(${target} add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader ) # diff --git a/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp b/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp index 18ed375975..9a1ef4a886 100644 --- a/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp +++ b/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,52 +26,47 @@ class metacall_py_init_bench : public benchmark::Fixture { public: - void SetUp(benchmark::State &) - { - metacall_print_info(); - - metacall_log_null(); - } - - void TearDown(benchmark::State & state) - { - if (metacall_destroy() != 0) - { - state.SkipWithError("Error destroying MetaCall"); - } - } }; -BENCHMARK_DEFINE_F(metacall_py_init_bench, init)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_init_bench, init) +(benchmark::State &state) { for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { + state.PauseTiming(); + + metacall_print_info(); + + metacall_log_null(); + + state.ResumeTiming(); + if (metacall_initialize() != 0) { state.SkipWithError("Error initializing MetaCall"); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Init Benchmark - Init"); } BENCHMARK_REGISTER_F(metacall_py_init_bench, init) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(1); -BENCHMARK_DEFINE_F(metacall_py_init_bench, load)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_init_bench, load) +(benchmark::State &state) { for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char tag[] = "py"; @@ -80,38 +75,29 @@ BENCHMARK_DEFINE_F(metacall_py_init_bench, load)(benchmark::State & state) "def int_mem_type(left: int, right: int) -> int:\n" "\treturn 0;"; - state.PauseTiming(); - - if (metacall_initialize() != 0) - { - state.SkipWithError("Error initializing MetaCall"); - } - - state.ResumeTiming(); - if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) { state.SkipWithError("Error loading int_mem_type function"); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Init Benchmark - Load Runtime"); } BENCHMARK_REGISTER_F(metacall_py_init_bench, load) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(1); -BENCHMARK_DEFINE_F(metacall_py_init_bench, load_warm)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_init_bench, load_warm) +(benchmark::State &state) { for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char tag[] = "py"; @@ -119,40 +105,42 @@ BENCHMARK_DEFINE_F(metacall_py_init_bench, load_warm)(benchmark::State & state) "#!/usr/bin/env python3\n" "def int_a_type(left: int, right: int) -> int:\n" "\treturn 0;"; - static const char int_b_type[] = - "#!/usr/bin/env python3\n" - "def int_b_type(left: int, right: int) -> int:\n" - "\treturn 0;"; - - state.PauseTiming(); - - if (metacall_initialize() != 0) - { - state.SkipWithError("Error initializing MetaCall"); - } if (metacall_load_from_memory(tag, int_a_type, sizeof(int_a_type), NULL) != 0) { state.SkipWithError("Error loading int_a_type function"); } - - state.ResumeTiming(); - - if (metacall_load_from_memory(tag, int_b_type, sizeof(int_b_type), NULL) != 0) - { - state.SkipWithError("Error loading int_b_type function"); - } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Init Benchmark - Load Warm"); } BENCHMARK_REGISTER_F(metacall_py_init_bench, load_warm) - ->Threads(1) ->Unit(benchmark::kMicrosecond) ->Iterations(1) ->Repetitions(1); +BENCHMARK_DEFINE_F(metacall_py_init_bench, destroy) +(benchmark::State &state) +{ + for (auto _ : state) + { +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + metacall_destroy(); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + } + + state.SetLabel("MetaCall Python Init Benchmark - Destroy"); +} + +BENCHMARK_REGISTER_F(metacall_py_init_bench, destroy) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(1); + BENCHMARK_MAIN(); diff --git a/source/benchmarks/metacall_rb_call_bench/CMakeLists.txt b/source/benchmarks/metacall_rb_call_bench/CMakeLists.txt index ef4ccef1ca..4833bad0de 100644 --- a/source/benchmarks/metacall_rb_call_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_rb_call_bench/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB) + return() +endif() + # # Executable name and options # @@ -79,20 +84,6 @@ target_link_libraries(${target} GBench - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -118,7 +109,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -127,8 +118,43 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with sanitizers: + # + # Address Sanitizer: + # ERROR: AddressSanitizer: heap-use-after-free on address 0x629000003a50 at pc 0x7fb5059be061 bp 0x6290000032d0 sp 0x629000002a80 + # WRITE of size 22 at 0x629000003a50 thread T0 + # + # Thread Sanitizer: + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14308) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #4 loader_impl_load_from_memory /usr/local/metacall/source/loader/source/loader_impl.c:945 (libmetacalld.so+0x30df1) + # #5 loader_load_from_memory /usr/local/metacall/source/loader/source/loader.c:327 (libmetacalld.so+0x2e1d1) + # #6 metacall_load_from_memory /usr/local/metacall/source/metacall/source/metacall.c:357 (libmetacalld.so+0x32c00) + # #7 metacall_rb_call_bench::SetUp(benchmark::State&) /usr/local/metacall/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp:51 (metacall-rb-call-benchd+0x1a420) + # #8 benchmark::internal::BenchmarkInstance::Run(unsigned long, int, benchmark::internal::ThreadTimer*, benchmark::internal::ThreadManager*, benchmark::internal::PerfCountersMeasurement*) const (metacall-rb-call-benchd+0x5f41f) + # #9 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable Ruby support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader ) # diff --git a/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp b/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp index 162e228ac8..4dc75e9424 100644 --- a/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp +++ b/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,60 +26,22 @@ class metacall_rb_call_bench : public benchmark::Fixture { public: - void SetUp(benchmark::State & state) - { - metacall_print_info(); - - metacall_log_null(); - - if (metacall_initialize() != 0) - { - state.SkipWithError("Error initializing MetaCall"); - } - - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) - { - static const char tag[] = "rb"; - - static const char int_mem_type[] = - "#!/usr/bin/env ruby\n" - "def int_mem_type(left: Fixnum, right: Fixnum)\n" - "\treturn 0\n" - "end\n"; - - if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) - { - state.SkipWithError("Error loading int_mem_type function"); - } - } - #endif /* OPTION_BUILD_LOADERS_RB */ - } - - void TearDown(benchmark::State & state) - { - if (metacall_destroy() != 0) - { - state.SkipWithError("Error destroying MetaCall"); - } - } }; -BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_va_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_va_args) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(int) * 3; // (int, int) -> int for (auto _ : state) { - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - void * ret; - for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacall("int_mem_type", 0, 0)); + void *ret = metacall("int_mem_type", 0, 0); state.PauseTiming(); @@ -98,7 +60,7 @@ BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_va_args)(benchmark::State & stat state.ResumeTiming(); } } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ } state.SetLabel("MetaCall Ruby Call Benchmark - Variadic Argument Call"); @@ -107,27 +69,24 @@ BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_va_args)(benchmark::State & stat } BENCHMARK_REGISTER_F(metacall_rb_call_bench, call_va_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_array_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_array_args) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(int) * 3; // (int, int) -> int for (auto _ : state) { - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - void * ret; - state.PauseTiming(); - void * args[2] = - { + void *args[2] = { metacall_value_create_int(0), metacall_value_create_int(0) }; @@ -136,7 +95,7 @@ BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_array_args)(benchmark::State & s for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacallv("int_mem_type", args)); + void *ret = metacallv("int_mem_type", args); state.PauseTiming(); @@ -164,7 +123,7 @@ BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_array_args)(benchmark::State & s state.ResumeTiming(); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ } state.SetLabel("MetaCall Ruby Call Benchmark - Array Argument Call"); @@ -173,9 +132,73 @@ BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_array_args)(benchmark::State & s } BENCHMARK_REGISTER_F(metacall_rb_call_bench, call_array_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_MAIN(); +/* Use main for initializing MetaCall once. There's a bug in Ruby 3.2 on MacOS which prevents reinitialization */ +/* + Stack trace (most recent call last): + #18 Object "metacall-rb-call-bench", at 0x100363520, in main + 48 + #17 Object "metacall-rb-call-bench", at 0x1003647f8, in benchmark::RunSpecifiedBenchmarks() + 40 + #16 Object "metacall-rb-call-bench", at 0x10036561f, in benchmark::RunSpecifiedBenchmarks(benchmark::BenchmarkReporter*, benchmark::BenchmarkReporter*, std::__1::basic_string, std::__1::allocator >) + 3567 + #15 Object "metacall-rb-call-bench", at 0x10037e408, in benchmark::internal::BenchmarkRunner::DoOneRepetition() + 136 + #14 Object "metacall-rb-call-bench", at 0x10037d97a, in benchmark::internal::BenchmarkRunner::DoNIterations() + 890 + #13 Object "metacall-rb-call-bench", at 0x10037dea7, in benchmark::internal::(anonymous namespace)::RunInThread(benchmark::internal::BenchmarkInstance const*, unsigned long long, int, benchmark::internal::ThreadManager*, benchmark::internal::PerfCountersMeasurement*) + 87 + #12 Object "metacall-rb-call-bench", at 0x10036af5f, in benchmark::internal::BenchmarkInstance::Run(unsigned long long, int, benchmark::internal::ThreadTimer*, benchmark::internal::ThreadManager*, benchmark::internal::PerfCountersMeasurement*) const + 79 + #11 Object "metacall-rb-call-bench", at 0x100363573, in benchmark::Fixture::Run(benchmark::State&) + 19 + #10 Object "metacall-rb-call-bench", at 0x100363605, in metacall_rb_call_bench::SetUp(benchmark::State&) + 69 + #9 Object "libmetacall.dylib", at 0x10050a077, in loader_impl_load_from_memory + 135 + #8 Object "libmetacall.dylib", at 0x10050955f, in loader_impl_initialize + 287 + #7 Object "librb_loader.so", at 0x10058b978, in rb_loader_impl_initialize + 56 + #6 Object "libruby.3.2.dylib", at 0x100c683d7, in ruby_init + 13 + #5 Object "libruby.3.2.dylib", at 0x100c6837b, in ruby_setup + 304 + #4 Object "libruby.3.2.dylib", at 0x100c90ff1, in rb_call_inits + 19 + #3 Object "libruby.3.2.dylib", at 0x100da121e, in Init_TransientHeap + 79 + #2 Object "libsystem_platform.dylib", at 0x7ff8088fbdfc, in _sigtramp + 28 + #1 Object "libbacktrace_plugin.so", at 0x100571b5d, in backward::SignalHandling::sig_handler(int, __siginfo*, void*) + 13 + #0 Object "libbacktrace_plugin.so", at 0x100571bc6, in backward::SignalHandling::handleSignal(int, __siginfo*, void*) + 70 +*/ +int main(int argc, char *argv[]) +{ + metacall_print_info(); + + metacall_log_null(); + + if (metacall_initialize() != 0) + { + return 1; + } + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + static const char tag[] = "rb"; + + static const char int_mem_type[] = + "#!/usr/bin/env ruby\n" + "def int_mem_type(left: Fixnum, right: Fixnum)\n" + "\treturn 0\n" + "end\n"; + + if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) + { + return 2; + } + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + ::benchmark::Initialize(&argc, argv); + + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) + { + return 3; + } + + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + + metacall_destroy(); + + return 0; +} diff --git a/source/benchmarks/set_bench/CMakeLists.txt b/source/benchmarks/set_bench/CMakeLists.txt new file mode 100644 index 0000000000..69630fada5 --- /dev/null +++ b/source/benchmarks/set_bench/CMakeLists.txt @@ -0,0 +1,147 @@ +# +# Executable name and options +# + +# Target name +set(target set-bench) +message(STATUS "Benchmark ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/set_bench.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GBench + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::adt +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + adt +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/benchmarks/set_bench/source/set_bench.cpp b/source/benchmarks/set_bench/source/set_bench.cpp new file mode 100644 index 0000000000..2129c98b54 --- /dev/null +++ b/source/benchmarks/set_bench/source/set_bench.cpp @@ -0,0 +1,147 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#define SET_SIZE 1000 +#define ITERATIONS 1000 + +class set_bench : public benchmark::Fixture +{ +public: + void SetUp(benchmark::State &) + { + s = set_create(&hash_callback_ptr, &comparable_callback_ptr); + + keys.reserve(SET_SIZE); + values.reserve(SET_SIZE); + + for (int i = 0; i < SET_SIZE; ++i) + { + keys.push_back(std::to_string(i)); + values.push_back(i); + set_insert(s, (set_key)keys[i].c_str(), &values[i]); + } + } + + void TearDown(benchmark::State &) + { + set_destroy(s); + } + + set s; + std::vector keys; + std::vector values; +}; + +int set_cb_iterate_sum(set s, set_key key, set_value value, set_cb_iterate_args args) +{ + int *i = (int *)value; + uint64_t *sum = (uint64_t *)args; + + (void)s; + (void)key; + + *sum = ((*sum) + (uint64_t)(*i)); + + return 0; +} + +BENCHMARK_DEFINE_F(set_bench, set_iterate) +(benchmark::State &state) +{ + uint64_t sum = 0; + + for (auto _ : state) + { + set_iterate(s, &set_cb_iterate_sum, &sum); + } + + state.SetLabel("Set Benchmark - Iterate Callback"); + state.SetItemsProcessed(SET_SIZE); +} + +BENCHMARK_REGISTER_F(set_bench, set_iterate) + ->Unit(benchmark::kMillisecond) + ->Iterations(ITERATIONS) + ->Repetitions(3); + +/* +BENCHMARK_DEFINE_F(set_bench, set_iterators) +(benchmark::State &state) +{ + uint64_t sum = 0; + + for (auto _ : state) + { + for (set_iterator it = set_iterator_begin(s); set_iterator_end(&it) > 0; set_iterator_next(it)) + { + int *i = (int *)set_iterator_value(it); + + sum += ((uint64_t)(*i)); + } + } + + (void)sum; + + state.SetLabel("Set Benchmark - Iterators"); + state.SetItemsProcessed(SET_SIZE); +} + +BENCHMARK_REGISTER_F(set_bench, set_iterators) + ->Unit(benchmark::kMillisecond) + ->Iterations(ITERATIONS) + ->Repetitions(3); +*/ + +BENCHMARK_DEFINE_F(set_bench, set_iterators_2) +(benchmark::State &state) +{ + uint64_t sum = 0; + + for (auto _ : state) + { + set_iterator_type it; + + for (set_iterator_begin(&it, s); set_iterator_end(&it) > 0; set_iterator_next(&it)) + { + int *i = (int *)set_iterator_value(&it); + + sum += ((uint64_t)(*i)); + } + } + + (void)sum; + + state.SetLabel("Set Benchmark - Iterators 2"); + state.SetItemsProcessed(SET_SIZE); +} + +BENCHMARK_REGISTER_F(set_bench, set_iterators_2) + ->Unit(benchmark::kMillisecond) + ->Iterations(ITERATIONS) + ->Repetitions(3); + +BENCHMARK_MAIN(); diff --git a/source/cli/CMakeLists.txt b/source/cli/CMakeLists.txt index 872f10b77e..d6b10bf0dc 100644 --- a/source/cli/CMakeLists.txt +++ b/source/cli/CMakeLists.txt @@ -4,5 +4,12 @@ if(NOT OPTION_BUILD_CLI) return() endif() +# Check if the dependency loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE) + message(WARNING "The Extension and NodeJS Loaders are a dependency of the CLI, in order to compile the CLI, enable them with -DOPTION_BUILD_LOADERS_EXT=ON -DOPTION_BUILD_LOADERS_NODE=ON") + return() +endif() + # CLI applications add_subdirectory(metacallcli) +add_subdirectory(plugins) diff --git a/source/cli/metacallcli/CMakeLists.txt b/source/cli/metacallcli/CMakeLists.txt index 81a928a10d..8067b8e620 100644 --- a/source/cli/metacallcli/CMakeLists.txt +++ b/source/cli/metacallcli/CMakeLists.txt @@ -1,9 +1,3 @@ -# -# External dependencies -# - -# find_package(THIRDPARTY REQUIRED) - # # Executable name and options # @@ -31,22 +25,13 @@ include(SecurityFlags) # set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(inline_path "${CMAKE_CURRENT_SOURCE_DIR}/inline/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(headers - ${include_path}/tokenizer.hpp - ${include_path}/parser.hpp ${include_path}/application.hpp ) -set(inline - ${inline_path}/parser.inl -) - set(sources - ${source_path}/tokenizer.cpp - ${source_path}/parser.cpp ${source_path}/application.cpp ${source_path}/main.cpp ) @@ -57,7 +42,6 @@ set(sources # Build executable add_executable(${target} - MACOSX_BUNDLE ${sources} ) @@ -72,11 +56,11 @@ set(PROJECT_METACALL_PORTS_DIRECTORY "${CMAKE_SOURCE_DIR}/source/ports/py_port") add_custom_target(${target}-scripts-tests ALL WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${CMAKE_COMMAND} -E make_directory ${LOADER_SCRIPT_PATH} + COMMAND ${CMAKE_COMMAND} -E make_directory ${LOADER_SCRIPT_PATH} COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${LOADER_SCRIPT_PATH}/ COMMAND ${CMAKE_COMMAND} -DPROJECT_METACALL_PORTS_DIRECTORY=${PROJECT_METACALL_PORTS_DIRECTORY} -DLOADER_SCRIPT_PATH=${LOADER_SCRIPT_PATH} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/configure_tests.cmake -) - +) + set_target_properties(${target}-scripts-tests PROPERTIES ${DEFAULT_PROJECT_OPTIONS} @@ -104,9 +88,6 @@ target_include_directories(${target} ${PROJECT_BINARY_DIR}/source/include ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include - ${PROJECT_BINARY_DIR}/source/inline - ${CMAKE_CURRENT_SOURCE_DIR}/inline - ${CMAKE_CURRENT_BINARY_DIR}/inline ${DEFAULT_INCLUDE_DIRECTORIES} PUBLIC @@ -124,7 +105,7 @@ target_include_directories(${target} target_link_libraries(${target} PRIVATE ${DEFAULT_LIBRARIES} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -145,15 +126,56 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 # Required for filesystem +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + c_loader + cob_loader + cs_loader + ext_loader + file_loader + java_loader + mock_loader + py_loader + rb_loader + rs_loader + rpc_loader + ts_loader + wasm_loader +) + +add_dependencies(${target} + node_loader + cli_repl_plugin + cli_core_plugin +) + +if(TARGET cli_cmd_plugin) + add_dependencies(${target} + cli_cmd_plugin + ) +endif() + # # Deployment # @@ -168,96 +190,342 @@ install(TARGETS ${target} # Define test # -if(WIN32) - set(TEST_COMMAND cmd /c) - set(GREP_COMMAND findstr) -else() - set(TEST_COMMAND sh -c) - set(GREP_COMMAND grep) +# Check if tests are enabled +if(NOT OPTION_BUILD_TESTS) + return() endif() +# Define variables for testing the CLI interactively +set(TEST_COMMAND_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/test/commands/${target}") +set(TEST_COMMAND_RUNNER "${CMAKE_CURRENT_SOURCE_DIR}/test/commands/command_runner.cmake") + +include(TestEnvironmentVariables) + add_test(NAME ${target} - COMMAND ${TEST_COMMAND} "echo 'load mock a.mock\ninspect\nexit' | $ | ${GREP_COMMAND} \"function three_str(a_str, b_str, c_str)\"" + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}.txt" -P ${TEST_COMMAND_RUNNER} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) - -# -# Define test labels -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} +set_tests_properties(${target} PROPERTIES + LABELS ${target} + PASS_REGULAR_EXPRESSION "function three_str\\(a_str, b_str, c_str\\)" ) - -include(TestEnvironmentVariables) - test_environment_variables(${target} "" ${TESTS_ENVIRONMENT_VARIABLES} ) +add_test(NAME ${target}-inspect-leak + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-inspect-leak.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +set_property(TEST ${target}-inspect-leak + PROPERTY LABELS ${target}-inspect-leak +) +test_environment_variables(${target}-inspect-leak + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) + if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_NODE AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_NODE) add_test(NAME ${target}-node - COMMAND ${TEST_COMMAND} "echo 'load node nod.js\ninspect\ncall hello_boy(300, 400)\nexit' | $ | ${GREP_COMMAND} \"700.0\"" + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node.txt" -P ${TEST_COMMAND_RUNNER} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + set_tests_properties(${target}-node PROPERTIES + LABELS ${target}-node + PASS_REGULAR_EXPRESSION "4001534" + ) test_environment_variables(${target}-node "" ${TESTS_ENVIRONMENT_VARIABLES} ) - if(OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_NODE AND OPTION_BUILD_LOADERS_PY) add_test(NAME ${target}-node-port-py - COMMAND ${TEST_COMMAND} "echo 'load node cli-test.js\ninspect\nexit' | $ | ${GREP_COMMAND} \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"" + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-port-py.txt" -P ${TEST_COMMAND_RUNNER} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + set_tests_properties(${target}-node-port-py PROPERTIES + LABELS ${target}-node-port-py + PASS_REGULAR_EXPRESSION "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ) test_environment_variables(${target}-node-port-py "" ${TESTS_ENVIRONMENT_VARIABLES} ) + + if(OPTION_BUILD_LOADERS_RB) + add_test(NAME ${target}-node-port-py-rb + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-port-py-rb.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_property(TEST ${target}-node-port-py-rb + PROPERTY LABELS ${target}-node-port-py-rb + ) + test_environment_variables(${target}-node-port-py-rb + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() endif() - if(OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_PY) + add_test(NAME ${target}-node-null + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-null.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node-null PROPERTIES + LABELS ${target}-node-null + PASS_REGULAR_EXPRESSION "Hello 342521512461246!" + ) + test_environment_variables(${target}-node-null + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + add_test(NAME ${target}-node-null-empty + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-null-empty.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node-null-empty PROPERTIES + LABELS ${target}-node-null-empty + PASS_REGULAR_EXPRESSION "Hello 342521512461246!" + ) + test_environment_variables(${target}-node-null-empty + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + add_test(NAME ${target}-node-null-undefined + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-null-undefined.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node-null-undefined PROPERTIES + LABELS ${target}-node-null-undefined + PASS_REGULAR_EXPRESSION "(null)" + ) + test_environment_variables(${target}-node-null-undefined + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + if(OPTION_BUILD_LOADERS_PY AND OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_PY) add_test(NAME ${target}-py-port - COMMAND ${TEST_COMMAND} "echo 'load py cli-test.py\ninspect\ncall test()\nexit' | $ | ${GREP_COMMAND} \"1234\"" + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-port.txt" -P ${TEST_COMMAND_RUNNER} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + set_tests_properties(${target}-py-port PROPERTIES + LABELS ${target}-py-port + PASS_REGULAR_EXPRESSION "1234" + ) test_environment_variables(${target}-py-port "" ${TESTS_ENVIRONMENT_VARIABLES} ) - endif() - if(OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_PY AND OPTION_BUILD_LOADERS_RB) - add_test(NAME ${target}-py-port-rb - COMMAND ${TEST_COMMAND} "echo 'load py cli-test-rb.py\ninspect\ncall test()\nexit' | $ | ${GREP_COMMAND} \"0123456789ABCDEFasd\"" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - test_environment_variables(${target}-py-port-rb - "" - ${TESTS_ENVIRONMENT_VARIABLES} - ) + if(OPTION_BUILD_LOADERS_RB) + add_test(NAME ${target}-py-port-rb + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-port-rb.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-port-rb PROPERTIES + LABELS ${target}-py-port-rb + PASS_REGULAR_EXPRESSION "0123456789ABCDEFasd" + ) + test_environment_variables(${target}-py-port-rb + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() endif() endif() if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_FILE AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_FILE) add_test(NAME ${target}-file - COMMAND ${TEST_COMMAND} "echo 'load file template.html\ninspect\ncall template.html()\nexit' | $ | ${GREP_COMMAND} \"${LOADER_SCRIPT_PATH}/template.html\"" + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-file.txt" -P ${TEST_COMMAND_RUNNER} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + set_tests_properties(${target}-file PROPERTIES + LABELS ${target}-file + PASS_REGULAR_EXPRESSION "${LOADER_SCRIPT_PATH}/template.html" + ) test_environment_variables(${target}-file "" ${TESTS_ENVIRONMENT_VARIABLES} ) + add_test(NAME ${target}-file-fail + COMMAND $ this-does-not-exist + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-file-fail PROPERTIES + LABELS ${target}-file-fail + PASS_REGULAR_EXPRESSION "Error: Failed to load script 'this-does-not-exist' with loader 'file'" + ) + test_environment_variables(${target}-file-fail + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_PY) + add_test(NAME ${target}-py-naming + COMMAND $ test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-naming PROPERTIES + LABELS ${target}-py-naming + PASS_REGULAR_EXPRESSION "Test: 66673332" + ) + test_environment_variables(${target}-py-naming + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-py-argv + COMMAND $ cli-test-argv.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-argv PROPERTIES + LABELS ${target}-py-argv + PASS_REGULAR_EXPRESSION "Test: cli-test-argv.py" + ) + test_environment_variables(${target}-py-argv + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-py-main + COMMAND $ cli-test-main.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-main PROPERTIES + LABELS ${target}-py-main + PASS_REGULAR_EXPRESSION "Test: 1234567890abcd" + ) + test_environment_variables(${target}-py-main + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-py-exception # https://github.com/metacall/core/issues/261 + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-exception.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-exception PROPERTIES + LABELS ${target}-py-exception + PASS_REGULAR_EXPRESSION "66" + ) + test_environment_variables(${target}-py-exception + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_RB) + add_test(NAME ${target}-rb-simplest + COMMAND $ simplest.rb + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-rb-simplest PROPERTIES + LABELS ${target}-rb-simplest + PASS_REGULAR_EXPRESSION "Hello from Ruby" + ) + test_environment_variables(${target}-rb-simplest + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) endif() if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_TS AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_TS) add_test(NAME ${target}-ts - COMMAND ${TEST_COMMAND} "echo 'load ts typedfunc.ts\ninspect\ncall typed_sum(4, 5)\nexit' | $ | ${GREP_COMMAND} \"9.0\"" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-ts.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc + ) + set_tests_properties(${target}-ts PROPERTIES + LABELS ${target}-ts + PASS_REGULAR_EXPRESSION "51354" ) test_environment_variables(${target}-ts "" ${TESTS_ENVIRONMENT_VARIABLES} ) + add_test(NAME ${target}-tsx-templating + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-tsx-templating.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/templating + ) + set_tests_properties(${target}-tsx-templating PROPERTIES + LABELS ${target}-tsx-templating + PASS_REGULAR_EXPRESSION "Hello metaprogrammer" + ) + test_environment_variables(${target}-tsx-templating + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + if(NOT (OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS)) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14459) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #9 node_loader_port_load_from_file_export(napi_env__*, napi_callback_info__*) /usr/local/metacall/source/loaders/node_loader/source/node_loader_port.cpp:395 (libnode_loaderd.so+0x113c5) + # #10 (libnode.so.72+0x7b6344) + # #11 node_loader_impl_async_func_call_safe /usr/local/metacall/source/loaders/node_loader/source/node_loader_impl.cpp:2040 (libnode_loaderd.so+0xe2e8) + # #12 (libnode.so.72+0x7b6344) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + add_test(NAME ${target}-tsx-loop-fail + COMMAND $ loopfail.tsx + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/loopfail + ) + set_tests_properties(${target}-tsx-loop-fail PROPERTIES + LABELS ${target}-tsx-loop-fail + PASS_REGULAR_EXPRESSION "Error: Cannot find module 'yeet-oof/whatever'" + ) + test_environment_variables(${target}-tsx-loop-fail + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() + + if(OPTION_BUILD_LOADERS_PY) + add_test(NAME ${target}-py-tsx + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-tsx.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/templating + ) + set_tests_properties(${target}-py-tsx PROPERTIES + LABELS ${target}-py-tsx + PASS_REGULAR_EXPRESSION "Hello World" + ) + test_environment_variables(${target}-py-tsx + "" + ${TESTS_ENVIRONMENT_VARIABLES} + PROJECT_METACALL_PORTS_DIRECTORY=${CMAKE_SOURCE_DIR}/source/ports/py_port + # Note: Here we have to set up the LOADER_SCRIPT_PATH to the CWD as a difference to ${target}-tsx-templating test + # which does not require this apparently (due to NodeJS resolve system), probably this has to be reviewed + LOADER_SCRIPT_PATH=${LOADER_SCRIPT_PATH}/templating + ) + endif() +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_EXT AND OPTION_BUILD_EXTENSIONS AND OPTION_BUILD_LOADERS_NODE AND OPTION_BUILD_LOADERS_PY AND OPTION_BUILD_PLUGINS_SANDBOX AND PROJECT_OS_FAMILY STREQUAL unix AND TARGET cli_sandbox_plugin) + if(NOT OPTION_BUILD_THREAD_SANITIZER AND NOT OPTION_BUILD_ADDRESS_SANITIZER) + add_test(NAME ${target}-cmd-sandboxing + COMMAND ${CMAKE_COMMAND} -E env $ --sandboxing --disable_time time.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-cmd-sandboxing PROPERTIES + LABELS ${target}-cmd-sandboxing + WILL_FAIL TRUE + ) + test_environment_variables(${target}-cmd-sandboxing + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() endif() diff --git a/source/cli/metacallcli/README.md b/source/cli/metacallcli/README.md new file mode 100644 index 0000000000..8febe19212 --- /dev/null +++ b/source/cli/metacallcli/README.md @@ -0,0 +1,17 @@ +# MetaCall CLI + +## About + +MetaCall provides a Command Line Interface which supports all MetaCall functionalities such as loading, calling, awaiting scripts. + +Use the `metacall` command in your shell after [installing](https://github.com/metacall/install) MetaCall to launch the CLI. + +## Currently Available Commands + +- `help`: Show help for MetaCall CLI (**Use this command to see the usage of a particular command**) +- `load`: Load a script from file into MetaCall +- `inspect`: Show all runtimes, modules and functions (with their signature) loaded into MetaCall +- `call`: Call a function previously loaded in MetaCall (Note: For `js` and `py` functions that do not return anything, the default return value is a `null`) +- `await`: Await an async function previously loaded in MetaCall +- `clear`: Delete a script previously loaded in MetaCall +- `exit`: Exit from MetaCall CLI diff --git a/source/cli/metacallcli/include/metacallcli/application.hpp b/source/cli/metacallcli/include/metacallcli/application.hpp index f419b9b1db..c2e7863a8e 100644 --- a/source/cli/metacallcli/include/metacallcli/application.hpp +++ b/source/cli/metacallcli/include/metacallcli/application.hpp @@ -1,6 +1,6 @@ /* * MetaCall Command Line Interface by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A command line interface example as metacall wrapper. * @@ -13,25 +13,16 @@ #include +#include +#include #include -#include #include -#include -#include +#include /* -- Namespace -- */ -namespace metacallcli { - -/* -- Forward Declarations -- */ - -class tokenizer; - -class parser; -class parser_parameter; - -class application; - +namespace metacallcli +{ /* -- Class Definition -- */ /** @@ -40,12 +31,7 @@ class application; */ class application { - public: - - /* -- Public Type Definitions -- */ - - typedef bool (*command_callback)(application &, tokenizer &); - +public: /* -- Public Methods -- */ /** @@ -58,7 +44,7 @@ class application * @param[in] argv * Array of strings from program parameters */ - application(int argc, char * argv[]); + application(int argc, char *argv[]); /** * @brief @@ -66,237 +52,87 @@ class application */ ~application(void); - /** - * @brief - * Application script loader - * - * @param[in] tag - * Loader tag reference - * - * @param[in] script - * Reference to script name - * - * @return - * Return true on success, false otherwhise - */ - bool load(const std::string & tag, const std::string & script); - - /** - * @brief - * Application script clearer - * - * @param[in] tag - * Loader tag reference - * - * @param[in] script - * Reference to script name - * - * @return - * Return true on success, false otherwhise - */ - bool clear(const std::string & tag, const std::string & script); - /** * @brief * Application main entry point */ void run(void); - /** - * @brief - * Shutdown main application loop - */ - void shutdown(void); - - /** - * @brief - * Debug command line string - * - * @param[in] key - * Name of the command line option - * - * @param[in] t - * Tokenizer wrapper of input command - */ - void command_debug(const std::string & key, const tokenizer & t); +protected: + /* -- Protected Methods -- */ /** * @brief - * Show inspect information - * - * @param[in] str - * Serialized inspect data - * - * @param[in] size - * Size in bytes of str string - * - * @param[in] size - * Size in bytes of str string - * - * @param[in] allocator - * Pointer to the allocator to be used in deserialization + * Initialize the REPL */ - void command_inspect(const char * str, size_t size, void * allocator); + void repl(); /** * @brief - * Create a new value from arguments with parser @p + * Initialize the CMD * - * @param[in] p - * Parser which points to the current iterator of the string + * @param[in] arguments + * Vector of strings containing all the arguments from argv * * @return - * Return a new value instanced if argument was correct + * Return true if the load was successful, false otherwise */ - void * argument_parse(parser_parameter & p); + bool cmd(std::vector &arguments); /** * @brief - * Adapts metacallv from string @name and vector @args + * Fallback argument parser * - * @param[in] name - * String object of function name + * @param[in] arguments + * Vector of strings containing all the arguments from argv * - * @param[in] args - * Vector pointing to arguments - * - * @return - * Return a new value instanced if argument was correct with the result of the call */ - void * metacallv_adaptor(const std::string & name, const std::vector & args); + void arguments_parse(std::vector &arguments); /** * @brief - * Adapts metacallfs from string @name and array string @args - * - * @param[in] name - * String object of function name + * Load all plugins from a subfolder @path * - * @param[in] args - * String representing an array to be deserialized + * @param[in] path + * Subpath where the plugins are located * - * @param[in] allocator - * Pointer to the allocator to be used in deserialization + * @param[out] handle + * Pointer to the handle containing of the loaded scripts * * @return - * Return a new value instanced if argument was correct with the result of the call - */ - void * metacallfs_adaptor(const std::string & name, const std::string & args, void * allocator); - - /** - * @brief - * Adapts metacallfs_await from string @name and array string @args - * - * @param[in] name - * String object of function name - * - * @param[in] args - * String representing an array to be deserialized - * - * @param[in] allocator - * Pointer to the allocator to be used in deserialization + * Return true if the load was successful, false otherwise * - * @return - * Return a new value instanced if argument was correct with the result of the call */ - void * metacallfs_await_adaptor(const std::string & name, const std::string & args, void * allocator); - - protected: - - /* -- Protected Definitions -- */ - - static const size_t arguments_str_size; - - /* -- Protected Methods -- */ + bool load_path(const char *path, void **handle); /** * @brief * Execute a command with string parameters * - * @param[in out] t - * Tokenizer wrapper of input command + * @param[inout] tokens + * Value of type array containing all the tokens of the input command + * + * @return + * Return result of the command execution */ - void execute(tokenizer & t); + void *execute(void *tokens); /** * @brief - * Defines a new command with a callback handler + * Check if a value is an exception or throwable, then prints it. + * The method always destroys the value @v * - * @param[in] key - * Name of the command line option - * - * @param[in] command_cb - * Handler will be raised on @key command entered + * @param[inout] v + * Value to be checked against and destroyed */ - void define(const char * key, command_callback command_cb); - - private: - - /* -- Private Type Definitions -- */ - - typedef std::vector arg_list; - - typedef std::vector script_list; - - typedef std::unordered_map command_table; - - /* -- Private Class Definition -- */ - - class parameter_iterator - { - public: - - /* -- Public Methods -- */ - - /** - * @brief - * Initialize parameter iterator - * - * @param[in] app - * Reference to the application - */ - parameter_iterator(application & app); - - /** - * @brief - * Parameter iterator class destructor - */ - ~parameter_iterator(); - - /** - * @brief - * Operator callback for iteration - * - * @param[in] parameter - * Current parameter being iterated - */ - void operator()(const char * parameter); - - /** - * @brief - * Assignement operator for parameter iterator - * - * @return - * Returns a reference to itself - */ - parameter_iterator & operator=(const parameter_iterator &) = delete; - - private: - - /* -- Private Member Data -- */ - - application & app; /**< Reference to the application */ - }; + void check_for_exception(void *v); +private: /* -- Private Member Data -- */ - bool exit_condition; /**< Condition for main loop */ - arg_list arguments; /**< Vector containing a list of arguments */ - script_list scripts; /**< Vector containing a list of script names */ - command_table commands; /**< Hash table from command strings to command handlers */ - std::string log_path; /**< Path where logs are located */ - std::mutex await_mutex; /**< Mutex for blocking the REPL until await is resolved */ - std::condition_variable await_cond; /**< Condition to be fired once await method is resolved or rejected */ + void *plugin_cli_handle; /**< Handle containing all loaded plugins for CLI */ + void *plugin_repl_handle; /**< Handle containing all loaded plugins for REPL */ + void *plugin_cmd_handle; /**< Handle containing all loaded plugins for CMD */ }; } /* namespace metacallcli */ diff --git a/source/cli/metacallcli/include/metacallcli/parser.hpp b/source/cli/metacallcli/include/metacallcli/parser.hpp deleted file mode 100644 index 37b556f090..0000000000 --- a/source/cli/metacallcli/include/metacallcli/parser.hpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - * MetaCall Command Line Interface by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A command line interface example as metacall wrapper. - * - */ - -#ifndef METACALL_CLI_PARSER_HPP -#define METACALL_CLI_PARSER_HPP 1 - -/* -- Headers -- */ - -#include - -#include "tokenizer.hpp" - -#include - -/* -- Namespace -- */ - -namespace metacallcli { - -/* -- Forward Declarations -- */ - -class tokenizer; - -class parser; - -/* -- Class Definition -- */ - -/** -* @brief -* Simple parser utility -*/ -class parser -{ - public: - - /* -- Public Methods -- */ - - /** - * @brief - * Parser class constructor - * - * @param[in] it - * Tokenizer iterator reference - */ - parser(const tokenizer::iterator & it); - - /** - * @brief - * Parser class destructor - */ - ~parser(void); - - /** - * @brief - * Check if current token is a defined type @T - * - * @param[template] T - * Type of value to check against token - * - * @return - * True if type @T is equivalent to current token - */ - template bool is(void); - - /** - * @brief - * Return current token transformed to type @T - * - * @param[template] T - * Type of value to check against token - * - * @return - * A copy of the value transformed to given type @T - */ - template T to(void); - - /** - * @brief - * Assignement operator for parser - * - * @return - * Returns a reference to itself - */ - parser & operator=(const parser &) = delete; - - protected: - - /* -- Private Member Data -- */ - - const tokenizer::iterator & it; /**< Tokenizer iterator reference */ -}; - -/** -* @brief -* Custom parser utility for parameters -*/ -class parser_parameter : public parser -{ - public: - - /* -- Public Methods -- */ - - /** - * @brief - * Parser class constructor - * - * @param[in] it - * Tokenizer iterator reference - */ - parser_parameter(const tokenizer::iterator & it); - - /** - * @brief - * Parser class destructor - */ - ~parser_parameter(void); - - /** - * @brief - * Check if current token is a defined type @T - * - * @param[template] T - * Type of value to check against token - * - * @return - * True if type @T is equivalent to current token - */ - template bool is(void); - - /** - * @brief - * Return current token transformed to type @T - * - * @param[template] T - * Type of value to check against token - * - * @return - * A copy of the value transformed to given type @T - */ - template T to(void); - - /** - * @brief - * Assignement operator for parser - * - * @return - * Returns a reference to itself - */ - parser_parameter & operator=(const parser_parameter &) = delete; -}; - -/** -* @brief -* Check if current token is a custom quoted char ('') -* -* @return -* True if current token is a custom quoted char -*/ -template <> bool parser_parameter::is(void); - -/** -* @brief -* Check if current token is a custom long ending with (L) -* -* @return -* True if current token is a custom long -*/ -template <> bool parser_parameter::is(void); - -/** -* @brief -* Check if current token is a custom float ending with (f) -* -* @return -* True if current token is a custom float -*/ -template <> bool parser_parameter::is(void); - -/** -* @brief -* Check if current token is a custom quoted string ("") -* -* @return -* True if current token is a custom quoted string -*/ -template <> bool parser_parameter::is(void); - -/** -* @brief -* Return current token transformed from custom quoted char ('') -* -* @return -* A copy of the value transformed to char -*/ -template <> char parser_parameter::to(void); - -/** -* @brief -* Return current token transformed from custom long ending with (L) -* -* @return -* A copy of the value transformed to long -*/ -template <> long parser_parameter::to(void); - -/** -* @brief -* Return current token transformed from custom float ending with (f) -* -* @return -* A copy of the value transformed to float -*/ -template <> float parser_parameter::to(void); - -/** -* @brief -* Return current token transformed from custom quoted string ('') -* -* @return -* A copy of the value transformed to string -*/ -template <> std::string parser_parameter::to(void); - -} /* namespace metacallcli */ - -/* -- Template Implementation -- */ - -#include - -#endif /* METACALL_CLI_PARSER_HPP */ diff --git a/source/cli/metacallcli/include/metacallcli/tokenizer.hpp b/source/cli/metacallcli/include/metacallcli/tokenizer.hpp deleted file mode 100644 index 2a4c1eaf85..0000000000 --- a/source/cli/metacallcli/include/metacallcli/tokenizer.hpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - * MetaCall Command Line Interface by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A command line interface example as metacall wrapper. - * - */ - -#ifndef METACALL_CLI_TOKENIZER_HPP -#define METACALL_CLI_TOKENIZER_HPP 1 - -/* -- Headers -- */ - -#include - -#include -#include -#include - -/* -- Namespace -- */ - -namespace metacallcli { - -/* -- Forward Declarations -- */ - -class tokenizer; - -/* -- Class Definition -- */ - -/** -* @brief -* String tokenizer utility -*/ -class tokenizer -{ - public: - - /* -- Public Methods -- */ - - /** - * @brief - * Tokenizer constructor with default delimiters - * - * @param[in] str - * String to be tokenized - */ - tokenizer(const std::string & str); - - /** - * @brief - * Tokenizer constructor with custom @delimiters - * - * @param[in] str - * String to be tokenized - * - * @param[in] delimiters - * Delimiters will be used to tokenize the string @str - */ - tokenizer(const std::string & str, const std::string & delimiters); - - /** - * @brief - * Tokenizer class destructor - */ - ~tokenizer(void); - - /** - * @brief - * Set custom tokenizer delimiters - * - * @param[in] del - * Delimiters will be used to tokenize the string - */ - void delimit(const std::string & del); - - /* -- Public Class Definition -- */ - - /** - * @brief - * String tokenizer iterator - */ - class iterator : public std::iterator< - std::input_iterator_tag, - size_t, - size_t, - const size_t *, - const std::string &> - { - public: - - /* -- Public Methods -- */ - - /** - * @brief - * Tokenizer iterator constructor - * - * @param[in] t - * Reference to tokenizer - * - * @param[in] begin - * Initial iterator position - */ - explicit iterator(const tokenizer & t, size_t begin = 0); - - /** - * @brief - * Tokenizer iterator increment operator - * - * @return - * Return a reference to itself - */ - iterator & operator++(void); - - /** - * @brief - * Tokenizer iterator increment operator - * - * @return - * Return a copy of itself - */ - iterator operator++(int); - - /** - * @brief - * Tokenizer iterator equality operator - * - * @param[in] other - * Reference to iterator to be compared against - * - * @return - * Return true if @other iterator points to the same @str and @offset - */ - bool operator==(iterator other) const; - - /** - * @brief - * Tokenizer iterator inequality operator - * - * @param[in] other - * Reference to iterator to be compared against - * - * @return - * Return true if @other iterator points to different @str or @offset - */ - bool operator!=(iterator other) const; - - /** - * @brief - * Tokenizer iterator dereferencing operator - * - * @return - * Return a constant string reference to the current token - */ - reference operator*(void) const; - - /** - * @brief - * Tokenizer iterator current position - * - * @return - * Return current value of @offset - */ - size_t position(void) const; - - /** - * @brief - * Tokenizer iterator escape trailing characters - * - * @param[in] characters - * Reference to list of characters to be escaped - * - * @return - * Return a constant string reference to the current token - */ - reference escape(const std::string & characters); - - /** - * @brief - * Assignment operator for tokenizer iterator - * - * @return - * Returns a reference to itself - */ - iterator & operator=(const iterator &) = delete; - - private: - - /* -- Private Member Data -- */ - - const tokenizer & t; /**< Reference to tokenizer */ - - size_t offset; /**< Current position over iteration */ - - std::string token; /**< Current token */ - }; - - /** - * @brief - * Begin iterator operation - * - * @return - * Returns a iterator pointing to the beginning of @str - */ - iterator begin(void) const; - - /** - * @brief - * End iterator operation - * - * @return - * Returns a iterator pointing to the end of @str - */ - iterator end(void) const; - - /** - * @brief - * Assignement operator for tokenizer - * - * @return - * Returns a reference to itself - */ - tokenizer & operator=(const tokenizer &) = delete; - - protected: - - /* -- Protected Definitions -- */ - - static const std::string default_delimiters; - - private: - - /* -- Private Member Data -- */ - - const std::string str; /**< String to be tokenized */ - - std::string delimiters; /**< Current string token delimiters */ -}; - -} /* namespace metacallcli */ - -#endif /* METACALL_CLI_TOKENIZER_HPP */ diff --git a/source/cli/metacallcli/inline/metacallcli/parser.inl b/source/cli/metacallcli/inline/metacallcli/parser.inl deleted file mode 100644 index 6665d9c318..0000000000 --- a/source/cli/metacallcli/inline/metacallcli/parser.inl +++ /dev/null @@ -1,60 +0,0 @@ -/* - * MetaCall Command Line Interface by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A command line interface example as metacall wrapper. - * - */ - -/* -- Headers -- */ - -#include -#include - -/* -- Namespace -- */ - -namespace metacallcli { - -/* -- Methods -- */ - -template bool parser::is() -{ - std::stringstream str_stream(*it); - - T t; - - return (str_stream >> t && !str_stream.ignore()); -} - -template T parser::to() -{ - std::stringstream str_stream(*it); - - T t; - - str_stream >> t; - - return t; -} - -template bool parser_parameter::is() -{ - std::stringstream str_stream(*it); - - T t; - - return (str_stream >> std::noskipws >> t && !str_stream.ignore()); -} - -template T parser_parameter::to() -{ - std::stringstream str_stream(*it); - - T t; - - str_stream >> std::noskipws >> t; - - return t; -} - -} /* namespace metacallcli */ diff --git a/source/cli/metacallcli/source/application.cpp b/source/cli/metacallcli/source/application.cpp index e2a724c384..098f639006 100644 --- a/source/cli/metacallcli/source/application.cpp +++ b/source/cli/metacallcli/source/application.cpp @@ -1,6 +1,6 @@ /* * MetaCall Command Line Interface by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A command line interface example as metacall wrapper. * @@ -9,436 +9,309 @@ /* -- Headers -- */ #include -#include -#include -#include -#include -#include - -/* Includes for home path */ -#if defined(unix) || defined(__unix__) || defined(__unix) || \ - defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) || \ - defined(__CYGWIN__) || defined(__CYGWIN32__) || \ - (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) -# include -# include -# include -#endif - -/* TODO: Windows special characters not working properly */ -/* Set UTF-16 mode for stdout in Windows for the lambda character */ -/* -#if defined(WIN32) || defined(_WIN32) -# include -# include -# include +#if defined __has_include + #if __has_include() + #include +namespace fs = std::filesystem; + #elif __has_include() + #include +namespace fs = std::experimental::filesystem; + #else + #error "Missing the header." + #endif +#else + #error "C++ standard too old for compiling this file." #endif -*/ -#include +#include +#include +#include /* -- Namespace Declarations -- */ using namespace metacallcli; -/* -- Private Methods -- */ +/* -- Private Data -- */ -bool command_cb_help(application & /*app*/, tokenizer & /*t*/) -{ - std::cout << "MetaCall Command Line Interface by Parra Studios" << std::endl; - std::cout << "Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia " << std::endl; - std::cout << std::endl << "A command line interface for MetaCall Core" << std::endl; - - /* Command list */ - std::cout << std::endl << "Command list:" << std::endl << std::endl; - - /* Load command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Load a script from file into MetaCall │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ load ... │" << std::endl; - std::cout << "\t│ : identifier to the type of script │" << std::endl; - std::cout << "\t│ options : │" << std::endl; - std::cout << "\t│ mock - Mock (for testing purposes) │" << std::endl; - std::cout << "\t│ py - Python │" << std::endl; - std::cout << "\t│ node - NodeJS │" << std::endl; - std::cout << "\t│ rb - Ruby │" << std::endl; - std::cout << "\t│ cs - C# NetCore │" << std::endl; - std::cout << "\t│ cob - Cobol │" << std::endl; - std::cout << "\t│ ts - TypeScript │" << std::endl; - std::cout << "\t│ js - V8 JavaScript Engine │" << std::endl; - std::cout << "\t│ file - Files (for handling file systems) │" << std::endl; - std::cout << "\t│ ... : relative or absolute path to the script(s) │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Example: │" << std::endl; - std::cout << "\t│ load node concat.js │" << std::endl; - std::cout << "\t│ load py example.py │" << std::endl; - std::cout << "\t│ load rb hello.rb │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Result: │" << std::endl; - std::cout << "\t│ Script (concat.js) loaded correctly │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl << std::endl; - - /* Inspect command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Show all runtimes, modules and functions (with their signature) loaded into MetaCall │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ inspect │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Example: │" << std::endl; - std::cout << "\t│ inspect │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Result: │" << std::endl; - std::cout << "\t│ runtime node { │" << std::endl; - std::cout << "\t│ module concat { │" << std::endl; - std::cout << "\t│ function concat(left, right) │" << std::endl; - std::cout << "\t│ } │" << std::endl; - std::cout << "\t│ } │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl << std::endl; - - /* Call command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Call a function previously loaded in MetaCall │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ call (, , ... , ) │" << std::endl; - std::cout << "\t│ : alphanumeric string beginning by letter (method1, method2, hello) │" << std::endl; - std::cout << "\t│ , , ... , : arguments to be passed to the function in JSON │" << std::endl; - std::cout << "\t│ types : │" << std::endl; - std::cout << "\t│ bool - true, false │" << std::endl; - std::cout << "\t│ number - 5, 4.34 │" << std::endl; - std::cout << "\t│ string - \"hello world\" │" << std::endl; - std::cout << "\t│ array - [2, true, \"abc\"] │" << std::endl; - std::cout << "\t│ object - { \"one\": 1, \"two\": 2 } │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Example: │" << std::endl; - std::cout << "\t│ call concat(\"hello\", \"world\") │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Result: │" << std::endl; - std::cout << "\t│ \"hello world\" │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl << std::endl; - - /* Await command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Await an async function previously loaded in MetaCall │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ await (, , ... , ) │" << std::endl; - std::cout << "\t│ : alphanumeric string beginning by letter (method1, method2, hello) │" << std::endl; - std::cout << "\t│ , , ... , : arguments to be passed to the function in JSON │" << std::endl; - std::cout << "\t│ types : │" << std::endl; - std::cout << "\t│ bool - true, false │" << std::endl; - std::cout << "\t│ number - 5, 4.34 │" << std::endl; - std::cout << "\t│ string - \"hello world\" │" << std::endl; - std::cout << "\t│ array - [2, true, \"abc\"] │" << std::endl; - std::cout << "\t│ object - { \"one\": 1, \"two\": 2 } │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Example: │" << std::endl; - std::cout << "\t│ await concat(\"hello\", \"world\") │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Result: │" << std::endl; - std::cout << "\t│ \"hello world\" │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl << std::endl; - - /* Clear command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Delete a script previously loaded in MetaCall │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ clear ... │" << std::endl; - std::cout << "\t│ : identifier to the type of script │" << std::endl; - std::cout << "\t│ options : │" << std::endl; - std::cout << "\t│ mock - Mock (for testing purposes) │" << std::endl; - std::cout << "\t│ py - Python │" << std::endl; - std::cout << "\t│ node - NodeJS │" << std::endl; - std::cout << "\t│ rb - Ruby │" << std::endl; - std::cout << "\t│ cs - C# NetCore │" << std::endl; - std::cout << "\t│ cob - Cobol │" << std::endl; - std::cout << "\t│ ts - TypeScript │" << std::endl; - std::cout << "\t│ js - V8 JavaScript Engine │" << std::endl; - std::cout << "\t│ file - Files (for handling file systems) │" << std::endl; - std::cout << "\t│ ... : id of the script (file name without extension) │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Example: │" << std::endl; - std::cout << "\t│ clear node concat │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Result: │" << std::endl; - std::cout << "\t│ Script (concat) removed correctly │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl << std::endl; - - /* Exit command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Exit from MetaCall CLI │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ exit │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl << std::endl; - - /* Help command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Show help for MetaCall CLI │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ help │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl << std::endl; +static bool exit_condition = true; - return true; -} +/* -- Methods -- */ -bool command_cb_debug(application & app, tokenizer & t) +void application::repl() { - std::cout << "[DEBUG]" << std::endl; + /* Initialize REPL plugins */ + if (!load_path("repl", &plugin_repl_handle)) + { + /* Do not enter into the main loop */ + exit_condition = true; + return; + } - app.command_debug(*t.begin(), t); + /* Register exit function */ + auto exit_command = [](size_t argc, void *args[], void *data) -> void * { + (void)args; + (void)data; - return true; -} + /* Validate function parameters */ + if (argc != 0) + { + std::cout << "Invalid number of arguments passed to exit, expected 0, received: " << argc << std::endl; + } -bool command_cb_inspect(application & app, tokenizer &) -{ - size_t size = 0; + std::cout << "Exiting ..." << std::endl; - struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + /* Exit from main loop */ + exit_condition = true; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + return NULL; + }; - char * inspect_str = metacall_inspect(&size, allocator); + int result = metacall_register_loaderv(metacall_loader("ext"), plugin_repl_handle, "exit", exit_command, METACALL_INVALID, 0, NULL); - if (inspect_str == NULL || size == 0) + if (result != 0) { - std::cout << "(null)" << std::endl; + std::cout << "Exit function was not registered properly, return code: " << result << std::endl; + exit(1); } else { - app.command_inspect(inspect_str, size, allocator); + /* Start the main loop */ + exit_condition = false; } - if (inspect_str != NULL) - { - metacall_allocator_free(allocator, inspect_str); - } + /* Initialize REPL descriptors */ + std::string plugin_path(metacall_plugin_path()); - metacall_allocator_destroy(allocator); + void *args[] = { + metacall_value_create_string(plugin_path.c_str(), plugin_path.length()) + }; - return true; -} + void *ret = metacallhv_s(plugin_cli_handle, "repl_initialize", args, sizeof(args) / sizeof(args[0])); -bool command_cb_call(application & app, tokenizer & t) -{ - const std::string func_delimiters(" \n\t\r\v\f(,)"); + metacall_value_destroy(args[0]); - tokenizer::iterator it = t.begin(); + check_for_exception(ret); +} - /* Set custom function delimiters */ - t.delimit(func_delimiters); +bool application::cmd(std::vector &arguments) +{ + /* Get the command parsing function */ + void *command_parse_func = metacall_handle_function(plugin_cli_handle, "command_parse"); - /* Skip command key */ - ++it; + /* By default, when executing the cmd, it will exit of the REPL */ + exit_condition = true; - /* Parse function call */ - if (it != t.end()) + if (command_parse_func == NULL) { - struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - - std::string func_name(*it); - - const std::string param_delimiters("()"); - - /* Convert arguments into an array */ - std::string args = "["; - - t.delimit(param_delimiters); - - ++it; + std::cout << "Warning: CLI Arguments Parser was not loaded, " + "using fallback argument parser with positional arguments only. " + << std::endl + << "Any command line option like '--help' will result into error. " + "Only files are allowed: $ metacall a.py b.js c.rb" + << std::endl; - if (it != t.end()) - { - args += *it; - } - - args += "]"; + /* Use fallback parser, it can execute files but does not support command line arguments as options (i.e: -h, --help) */ + /* Parse program arguments if any (e.g metacall (0) a.py (1) b.js (2) c.rb (3)) */ + arguments_parse(arguments); - /* - void * result = app.metacallv_adaptor(func_name, args); - */ + return true; + } - void * result = app.metacallfs_adaptor(func_name, args, allocator); + /* Check first if the command function is registered */ + void *command_function_func = metacall_handle_function(plugin_cli_handle, "command_function"); - if (result != NULL) - { - size_t size = 0; + if (command_function_func == NULL) + { + return false; + } - char * value_str = metacall_serialize(metacall_serial(), result, &size, allocator); + /* Initialize CMD plugins */ + if (!load_path("cmd", &plugin_cmd_handle)) + { + return false; + } - std::cout << value_str << std::endl; + /* Initialize CMD descriptors */ + std::string plugin_path(metacall_plugin_path()); - metacall_allocator_free(allocator, value_str); + void *command_initialize_args[] = { + metacall_value_create_string(plugin_path.c_str(), plugin_path.length()) + }; - metacall_value_destroy(result); - } - else - { - std::cout << "(null)" << std::endl; - } + void *command_initialize_ret = metacallhv_s(plugin_cli_handle, "command_initialize", command_initialize_args, sizeof(command_initialize_args) / sizeof(command_initialize_args[0])); - metacall_allocator_destroy(allocator); + check_for_exception(command_initialize_ret); - return true; + for (void *arg : command_initialize_args) + { + metacall_value_destroy(arg); } - return false; -} - -bool command_cb_await(application & app, tokenizer & t) -{ - const std::string func_delimiters(" \n\t\r\v\f(,)"); - - tokenizer::iterator it = t.begin(); + /* Convert all arguments into metacall value strings */ + std::vector arguments_values; + arguments_values.reserve(arguments.size()); - /* Set custom function delimiters */ - t.delimit(func_delimiters); + for (const std::string &str_argument : arguments) + { + arguments_values.push_back(metacall_value_create_string(str_argument.c_str(), str_argument.length())); + } - /* Skip command key */ - ++it; + /* Parse the arguments with the CMD plugin command parse function */ + void *ret = metacallfv_s(command_parse_func, arguments_values.data(), arguments_values.size()); - /* Parse function call */ - if (it != t.end()) + /* Destroy all the command parse values */ + for (void *value_argument : arguments_values) { - struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + metacall_value_destroy(value_argument); + } - std::string func_name(*it); + /* Check for correct result of command parse */ + if (metacall_value_id(ret) != METACALL_ARRAY) + { + check_for_exception(ret); + return false; + } - const std::string param_delimiters("()"); + /* Get the argument map and the positional array */ + void **ret_array = metacall_value_to_array(ret); + void **command_map = metacall_value_to_map(ret_array[0]); + size_t command_size = metacall_value_count(ret_array[0]); + void **positional_array = metacall_value_to_array(ret_array[1]); + size_t positional_size = metacall_value_count(ret_array[1]); - /* Convert arguments into an array */ - std::string args = "["; + /* Execute arguments */ + for (size_t iterator = 0; iterator < command_size; ++iterator) + { + void **command_pair = metacall_value_to_array(command_map[iterator]); - t.delimit(param_delimiters); + void *command_args[] = { + command_pair[0] + }; - ++it; + void *command_func = metacallfv_s(command_function_func, command_args, sizeof(command_args) / sizeof(command_args[0])); - if (it != t.end()) + if (metacall_value_id(command_func) == METACALL_FUNCTION) { - args += *it; + /* Execute the function */ + void *command_ret = metacallfv_s(command_func, &command_pair[1], 1); + metacall_value_destroy(command_func); + check_for_exception(command_ret); + continue; } - - args += "]"; - - /* - void * result = app.metacallfv_await_adaptor(func_name, args); - */ - - void * result = app.metacallfs_await_adaptor(func_name, args, allocator); - - if (result != NULL) + else if (metacall_value_id(command_func) == METACALL_DOUBLE) { - size_t size = 0; + static const double COMMAND_NOT_REGISTERED = 0.0; - char * value_str = metacall_serialize(metacall_serial(), result, &size, allocator); - - std::cout << value_str << std::endl; - - metacall_allocator_free(allocator, value_str); + /* The command is not registered, skip it */ + if (metacall_value_to_double(command_func) == COMMAND_NOT_REGISTERED) + { + metacall_value_destroy(command_func); + continue; + } - metacall_value_destroy(result); + /* If the function is undefined, try to match the command with a function in the handle scope */ + metacall_value_destroy(command_func); } else { - std::cout << "(null)" << std::endl; + check_for_exception(command_func); + return false; } - metacall_allocator_destroy(allocator); + /* Otherwise use the cmd handle scope for obtaining the function */ + const char *command_str = metacall_value_to_string(command_pair[0]); + command_func = metacall_handle_function(plugin_cmd_handle, command_str); - return true; - } - - return false; -} - -bool command_cb_load(application & app, tokenizer & t) -{ - tokenizer::iterator it = t.begin(); - - parser p(it); + if (command_func == NULL) + { + std::cout << "Error: Command line option '" << command_str << "' unrecognized" << std::endl; + return false; + } - std::string loader_tag; + void *command_ret = metacallfv_s(command_func, &command_pair[1], 1); - ++it; + check_for_exception(command_ret); + } - if (p.is()) + /* Note: If it has zero positional arguments, we should also run the repl, for example: + * $ metacall --some-option --another-option + */ + if (positional_size == 0) { - loader_tag = *it; + /* Initialize the REPL */ + repl(); + exit_condition = false; } - - do + else { - ++it; - - } while (it != t.end() && p.is() && app.load(loader_tag, *it)); - - return true; -} - -bool command_cb_clear(application & app, tokenizer & t) -{ - tokenizer::iterator it = t.begin(); - - parser p(it); - - std::string loader_tag; + /* Execute the positional arguments */ + std::vector positional_arguments; + positional_arguments.reserve(positional_size); - ++it; + for (size_t iterator = 0; iterator < positional_size; ++iterator) + { + positional_arguments.push_back(metacall_value_to_string(positional_array[iterator])); + } - if (p.is()) - { - loader_tag = *it; + arguments_parse(positional_arguments); } - do - { - ++it; - - } while (it != t.end() && p.is() && app.clear(loader_tag, *it)); + metacall_value_destroy(ret); return true; } -bool command_cb_exit(application & app, tokenizer & /*t*/) +application::application(int argc, char *argv[]) : + plugin_cli_handle(NULL), plugin_repl_handle(NULL), plugin_cmd_handle(NULL) { - std::cout << "Exiting ..." << std::endl; + /* Initialize MetaCall */ + if (metacall_initialize() != 0) + { + /* Exit from application */ + exit(1); + } - app.shutdown(); + /* Initialize MetaCall arguments */ + metacall_initialize_args(argc, argv); - return true; -} + /* Print MetaCall information */ + metacall_print_info(); -/* -- Methods -- */ + /* Initialize CLI internal plugins */ + if (!load_path("internal", &plugin_cli_handle)) + { + /* Do not enter into the main loop */ + exit_condition = true; + return; + } -application::parameter_iterator::parameter_iterator(application & app) : - app(app) -{ + if (argc == 1) + { + /* Launch the REPL */ + repl(); + } + else + { + std::vector arguments(argv + 1, argv + argc); + /* Launch the CMD (parse arguments) */ + if (!cmd(arguments)) + { + /* TODO: Report something? */ + } + } } -application::parameter_iterator::~parameter_iterator() +application::~application() { - + metacall_destroy(); } -void application::parameter_iterator::operator()(const char * parameter) +void application::arguments_parse(std::vector &arguments) { - std::string script(parameter); - /* List of file extensions mapped into loader tags */ - static std::unordered_map extension_to_tag = - { + static std::unordered_map extension_to_tag = { /* Mock Loader */ { "mock", "mock" }, /* Python Loader */ @@ -459,504 +332,242 @@ void application::parameter_iterator::operator()(const char * parameter) /* TypeScript Loader */ { "ts", "ts" }, { "jsx", "ts" }, - { "tsx", "ts" } + { "tsx", "ts" }, + /* WASM Loader */ + { "wasm", "wasm" }, + { "wat", "wasm" }, + /* Rust Loader */ + { "rs", "rs" }, + /* C Loader */ + { "c", "c" }, + { "h", "c" }, + /* Java Loader */ + { "java", "java" }, + { "jar", "java" }, + /* RPC Loader */ + { "rpc", "rpc" } + + // TODO: Implement handling of duplicated extensions, + // load the file with all loaders of each duplicated extension (trial and error) + + // /* Extension Loader */ + // { "so", "ext" }, + // { "dylib", "ext" }, + // { "dll", "ext" }, /* Note: By default js extension uses NodeJS loader instead of JavaScript V8 */ /* Probably in the future we can differenciate between them, but it is not trivial */ }; - const std::string tag = extension_to_tag[script.substr(script.find_last_of(".") + 1)]; - const std::string safeTag = tag != "" ? tag : "file"; /* Use File Loader if the tag is not found */ - - app.load(safeTag, script); - app.shutdown(); -} - -bool application::load(const std::string & tag, const std::string & script) -{ - const char * load_scripts[] = + for (std::string script : arguments) { - script.c_str() - }; + const std::string tag = extension_to_tag[script.substr(script.find_last_of(".") + 1)]; + const std::string safe_tag = tag != "" ? tag : "file"; /* Use File Loader if the tag is not found */ - if (metacall_load_from_file(tag.c_str(), load_scripts, sizeof(load_scripts) / sizeof(load_scripts[0]), NULL) != 0) - { - std::cout << "Script (" << script << ") load error in loader (" << tag << ")" << std::endl; + /* Load the script */ + const char *scripts[1] = { + script.c_str() + }; - return false; + if (metacall_load_from_file(safe_tag.c_str(), scripts, 1, NULL) != 0) + { + /* Stop loading more scripts */ + std::cout << "Error: Failed to load script '" << script << "' with loader '" << safe_tag << "'" << std::endl; + exit(1); + } } - - scripts.push_back(script); - - std::cout << "Script (" << script << ") loaded correctly" << std::endl; - - return true; } -bool application::clear(const std::string & tag, const std::string & script) +bool application::load_path(const char *path, void **handle) { - void * handle = metacall_handle(tag.c_str(), script.c_str()); - - if (handle == NULL) - { - std::cout << "Script (" << script << ") not found in loader (" << tag << ")" << std::endl; + /* Get core plugin path and handle in order to load cli plugins */ + const char *plugin_path = metacall_plugin_path(); + void *plugin_extension_handle = metacall_plugin_extension(); - return false; - } - - if (metacall_clear(handle) != 0) + if (plugin_path == NULL || plugin_extension_handle == NULL) { - std::cout << "Script (" << script << ") clear error in loader (" << tag << ")" << std::endl; - return false; } - script_list::iterator it = std::find(scripts.begin(), scripts.end(), script); - - if (it != scripts.end()) - { - scripts.erase(it); - } - - std::cout << "Script (" << script << ") removed correctly" << std::endl; - - return true; -} - -std::string user_directory() -{ -#if defined(WIN32) || defined(_WIN32) || \ - defined(__CYGWIN__) || defined(__CYGWIN32__) || \ - defined(__MINGW32__) || defined(__MINGW64__) - - const char * path = getenv("USERPROFILE"); + /* Define the cli plugin path as string (core plugin path plus the subpath) */ + fs::path plugin_cli_path(plugin_path); + plugin_cli_path /= "cli"; + plugin_cli_path /= path; + std::string plugin_cli_path_str(plugin_cli_path.string()); - return std::string(path) + std::string("\\"); - -#elif defined(unix) || defined(__unix__) || defined(__unix) || \ - defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) || \ - defined(__CYGWIN__) || defined(__CYGWIN32__) || \ - (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) - - const char * path = getenv("HOME"); - - if (path == NULL) - { - path = getpwuid(getuid())->pw_dir; - } - - return std::string(path) + std::string("/"); -#else - return std::string("~/"); -#endif -} - -application::application(int argc, char * argv[]) : exit_condition(false), log_path(user_directory() + std::string("metacall.log")) -{ - /* Set locale */ - setlocale(LC_CTYPE, "C"); - - /* TODO: Windows special characters not working properly */ - /* - #if defined(WIN32) || defined(_WIN32) - { - // SetConsoleOutputCP(CP_UTF16); - - *//* Set UTF-16 mode for stdout in Windows for the lambda character *//* - _setmode(_fileno(stdout), _O_U16TEXT); - - *//* Enable buffering to prevent VS from chopping up UTF-16 byte sequences *//* - setvbuf(stdout, nullptr, _IOFBF, 1000); - } - #endif - */ - - /* Initialize MetaCall logs */ - metacall_log_file_type log_file = - { - log_path.c_str(), "w" + /* Load cli plugins into plugin cli handle */ + void *args[] = { + metacall_value_create_string(plugin_cli_path_str.c_str(), plugin_cli_path_str.length()), + metacall_value_create_ptr(handle) }; - if (metacall_log(METACALL_LOG_FILE, (void *)&log_file) != 0) - { - std::cout << "Error initializing the logs..." << std::endl; + void *ret = metacallhv_s(plugin_extension_handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); - /* Exit from application */ - shutdown(); - } + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); - /* Initialize MetaCall */ - if (metacall_initialize() != 0) + if (ret == NULL) { - /* Exit from application */ - shutdown(); + std::cout << "Failed to load CLI plugins from folder: " << plugin_cli_path_str << std::endl; + return false; } - /* Print MetaCall information */ - metacall_print_info(); - - /* Parse program arguments if any (e.g metacall (0) a.py (1) b.js (2) c.rb (3)) */ - if (argc > 1) + if (metacall_value_id(ret) == METACALL_INT && metacall_value_to_int(ret) != 0) { - parameter_iterator param_it(*this); + std::cout << "Failed to load CLI plugins from folder '" + << plugin_cli_path_str << "' with result: " + << metacall_value_to_int(ret) << std::endl; - /* Parse program parameters */ - std::for_each(&argv[1], argv + argc, param_it); + metacall_value_destroy(ret); + return false; } - /* Define available commands */ - define("help", &command_cb_help); - - define("debug", &command_cb_debug); - - define("inspect", &command_cb_inspect); - - define("call", &command_cb_call); - - define("await", &command_cb_await); + metacall_value_destroy(ret); - define("load", &command_cb_load); - - define("clear", &command_cb_clear); - - define("exit", &command_cb_exit); -} - -application::~application() -{ - if (metacall_destroy() != 0) - { - std::cout << "Error while destroying MetaCall." << std::endl; - } + return true; } void application::run() { - /* Show welcome message */ - if (exit_condition != true) - { - std::cout << "Welcome to Tijuana, tequila, sexo & marijuana." << std::endl; - } + void *evaluate_func = metacall_handle_function(plugin_cli_handle, "repl_evaluate"); while (exit_condition != true) { - std::string input; - - /* Show prompt line */ - #if defined(WIN32) || defined(_WIN32) - /* TODO: Windows special characters not working properly */ - /* std::cout << L'\u03BB' << ' '; */ - std::cout << "> "; - #else - std::cout << "\u03BB "; - #endif - - /* Get whole line */ - std::getline(std::cin, input); - - if (std::cin.eof() || std::cin.fail() || std::cin.bad()) - { - shutdown(); - return; - } + std::mutex await_mutex; + std::condition_variable await_cond; + std::unique_lock lock(await_mutex); - /* Check for valid data */ - if (input.length() > 0) + struct await_data_type { - /* Create tokenizer from input string */ - tokenizer t(input); - - /* Execute the command */ - execute(t); - } - - } while (exit_condition != true); -} - -void application::shutdown() -{ - std::cout << "Log file written in: " << log_path << std::endl; - - exit_condition = true; -} - -void application::command_debug(const std::string & key, const tokenizer & t) -{ - std::cout << "{" << std::endl - << "\tkey : " << key << "," << std::endl - << "\targuments :" << std::endl; - - for (tokenizer::iterator it = t.begin(); it != t.end(); ++it) - { - std::cout << "\t\t[" << it.position() << "] " << *it << std::endl; - } - - std::cout << "}" << std::endl; -} - -void value_array_for_each(void * v, const std::function & lambda) -{ - void ** v_array = static_cast(metacall_value_to_array(v)); - size_t count = metacall_value_count(v); - - std::for_each(v_array, v_array + count, lambda); -} - -void value_map_for_each(void * v, const std::function & lambda) -{ - void ** v_map = static_cast(metacall_value_to_map(v)); - size_t count = metacall_value_count(v); - - std::for_each(v_map, v_map + count, [&lambda](void * element) - { - void ** v_element = metacall_value_to_array(element); - lambda(metacall_value_to_string(v_element[0]), v_element[1]); - }); -} - -void application::command_inspect(const char * str, size_t size, void * allocator) -{ - void * v = metacall_deserialize(metacall_serial(), str, size, allocator); - - if (v == NULL) - { - std::cout << "Invalid deserialization" << std::endl; - - return; - } - - /* Print run-times */ - value_map_for_each(v, [](const char * key, void * modules) - { - std::cout << "runtime " << key; - - if (metacall_value_count(modules) == 0) + void *v; + std::mutex &mutex; + std::condition_variable &cond; + bool exit_condition; + }; + + struct await_data_type await_data = { NULL, await_mutex, await_cond, false }; + + void *future = metacallfv_await_s( + evaluate_func, metacall_null_args, 0, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->cond.notify_one(); + return NULL; + }, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->exit_condition = true; + await_data->cond.notify_one(); + return NULL; + }, + static_cast(&await_data)); + + await_cond.wait(lock); + + /* Unused */ + metacall_value_destroy(future); + + /* Check if the loop was rejected */ + if (await_data.exit_condition) { - std::cout << std::endl; - return; + exit_condition = true; } - - std::cout << " {" << std::endl; - - /* Print scripts */ - value_array_for_each(modules, [](void * module) + else { - /* Get module name */ - void ** v_module_map = static_cast(metacall_value_to_map(module)); - void ** v_module_name_tuple = metacall_value_to_array(v_module_map[0]); - const char * name = metacall_value_to_string(v_module_name_tuple[1]); + void **results = metacall_value_to_array(await_data.v); + void *args[2]; - std::cout << "\tmodule " << name << " { " << std::endl; - - /* Get module functions */ - void ** v_module_scope_tuple = metacall_value_to_array(v_module_map[1]); - void ** v_scope_map = metacall_value_to_map(v_module_scope_tuple[1]); - void ** v_scope_funcs_tuple = metacall_value_to_array(v_scope_map[1]); - - if (metacall_value_count(v_scope_funcs_tuple[1]) != 0) + if (metacall_value_id(results[0]) == METACALL_EXCEPTION || metacall_value_id(results[0]) == METACALL_THROWABLE) { - value_array_for_each(v_scope_funcs_tuple[1], [](void * func) - { - /* Get function name */ - void ** v_func_map = static_cast(metacall_value_to_map(func)); - void ** v_func_tuple = metacall_value_to_array(v_func_map[0]); - const char * func_name = metacall_value_to_string(v_func_tuple[1]); - - std::cout << "\t\tfunction " << func_name << "("; - - /* Get function signature */ - void ** v_signature_tuple = metacall_value_to_array(v_func_map[1]); - void ** v_args_map = metacall_value_to_map(v_signature_tuple[1]); - void ** v_args_tuple = metacall_value_to_array(v_args_map[1]); - void ** v_args_array = metacall_value_to_array(v_args_tuple[1]); - - size_t iterator = 0, count = metacall_value_count(v_args_tuple[1]); - - value_array_for_each(v_args_array, [&iterator, &count](void * arg) - { - void ** v_arg_map = metacall_value_to_map(arg); - void ** v_arg_name_tupla = metacall_value_to_array(v_arg_map[0]); - - std::cout << metacall_value_to_string(v_arg_name_tupla[1]); - - if (iterator + 1 < count) - { - std::cout << ", "; - } + args[0] = metacall_value_copy(results[0]); + args[1] = metacall_value_create_null(); + } + else + { + args[0] = metacall_value_create_null(); - ++iterator; - }); + /* Execute command */ + if (metacall_value_id(results[0]) == METACALL_ARRAY) + { + args[1] = execute(results[0]); + } + else + { + args[1] = metacall_value_copy(results[0]); + } - std::cout << ")" << std::endl; - }); + if (metacall_value_id(args[1]) == METACALL_INVALID) + { + metacall_value_destroy(args[1]); + args[1] = metacall_value_create_null(); + } } - std::cout << "\t}" << std::endl; - }); + /* Invoke next iteration of the REPL */ + void *ret = metacallfv_s(metacall_value_to_function(results[1]), args, 2); - std::cout << "}" << std::endl; - }); -} + check_for_exception(ret); -void application::execute(tokenizer & t) -{ - tokenizer::iterator it = t.begin(); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } - command_callback cb = commands[*it]; + metacall_value_destroy(await_data.v); + } - if (cb == nullptr) + if (plugin_cli_handle != NULL) { - std::cout << "[WARNING]: Invalid command" << std::endl; + /* Close REPL */ + void *ret = metacallhv_s(plugin_cli_handle, "repl_close", metacall_null_args, 0); - command_debug(*it, t); - - return; - } + check_for_exception(ret); - if (cb(*this, t) == false) - { - std::cout << "[WARNING]: Invalid command execution" << std::endl; + /* Get the command destroy function */ + void *command_destroy_func = metacall_handle_function(plugin_cli_handle, "command_destroy"); - command_debug(*it, t); + /* Destroy the commands */ + if (command_destroy_func != NULL) + { + /* Parse the arguments with the CMD plugin command parse function */ + ret = metacallfv_s(command_destroy_func, metacall_null_args, 0); - return; + check_for_exception(ret); + } } } -void application::define(const char * key, application::command_callback command_cb) +void *application::execute(void *tokens) { - std::string cmd(key); + size_t size = metacall_value_count(tokens); - commands[cmd] = command_cb; -} - -void * application::argument_parse(parser_parameter & p) -{ - if (p.is()) + if (size == 0) { - bool b = p.to(); - - boolean bo = static_cast(b); - - return metacall_value_create_bool(bo); + return metacall_value_create_null(); } - else if (p.is()) - { - char c = p.to(); - - return metacall_value_create_char(c); - } - else if (p.is()) - { - int i = p.to(); - - return metacall_value_create_int(i); - } - else if (p.is()) - { - long l = p.to(); - - return metacall_value_create_long(l); - } - else if (p.is()) - { - float f = p.to(); - - return metacall_value_create_float(f); - } - else if (p.is()) - { - double d = p.to(); - - return metacall_value_create_double(d); - } - else if (p.is()) - { - void * ptr = p.to(); - - return metacall_value_create_ptr(ptr); - } - else if (p.is()) + else { - std::string str = p.to(); + void **tokens_array = metacall_value_to_array(tokens); + void *key = tokens_array[0]; - return metacall_value_create_string(str.c_str(), str.length()); + return metacallhv_s(plugin_repl_handle, metacall_value_to_string(key), &tokens_array[1], size - 1); } - - return NULL; } -void * application::metacallv_adaptor(const std::string & name, const std::vector & args) +void application::check_for_exception(void *v) { - void ** args_ptr = new void * [args.size()]; + struct metacall_exception_type ex; - void * result = NULL; - - if (args_ptr != nullptr) + if (metacall_error_from_value(v, &ex) == 0) { - size_t iterator = 0; - - std::for_each(args.begin(), args.end(), [&iterator, args_ptr](void * v) - { - args_ptr[iterator] = v; - - ++iterator; - }); - - result = metacallv(name.c_str(), args_ptr); - - delete[] args_ptr; + std::cout << "Exception: " << ex.message << std::endl + << ex.stacktrace << std::endl; } - return result; -} - -void * application::metacallfs_adaptor(const std::string & name, const std::string & args, void * allocator) -{ - void * func = metacall_function(name.c_str()); - - return metacallfs(func, args.c_str(), args.length() + 1, allocator); -} - -void * application::metacallfs_await_adaptor(const std::string & name, const std::string & args, void * allocator) -{ - void * func = metacall_function(name.c_str()); - #if 0 - return metacallfs_await(func, args.c_str(), args.length() + 1, allocator, NULL, NULL, NULL); - #endif - - std::unique_lock lock(this->await_mutex); - - struct await_data_type - { - void * v; - application * app; - }; - - struct await_data_type data = { NULL, this }; - - void * future = metacallfs_await(func, args.c_str(), args.length() + 1, allocator, - [](void * result, void * ctx) -> void * { - struct await_data_type * await_data = static_cast(ctx); - std::unique_lock lock(await_data->app->await_mutex); - /* Value must be always copied, it gets deteled after the scope */ - await_data->v = metacall_value_copy(result); - await_data->app->await_cond.notify_one(); - return NULL; - }, - [](void * result, void * ctx) -> void * { - struct await_data_type * await_data = static_cast(ctx); - std::unique_lock lock(await_data->app->await_mutex); - /* Value must be always copied, it gets deteled after the scope */ - await_data->v = metacall_value_copy(result); - await_data->app->await_cond.notify_one(); - return NULL; - }, static_cast(&data)); - - this->await_cond.wait(lock); - - /* Unused */ - metacall_value_destroy(future); - - return data.v; + metacall_value_destroy(v); } diff --git a/source/cli/metacallcli/source/main.cpp b/source/cli/metacallcli/source/main.cpp index a855eaf6ad..8c41f19157 100644 --- a/source/cli/metacallcli/source/main.cpp +++ b/source/cli/metacallcli/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Command Line Interface by Parra Studios * A command line interface example as metacall wrapper. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ using namespace metacallcli; /* -- Methods -- */ -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { application app(argc, argv); diff --git a/source/cli/metacallcli/source/parser.cpp b/source/cli/metacallcli/source/parser.cpp deleted file mode 100644 index b43dc62017..0000000000 --- a/source/cli/metacallcli/source/parser.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * MetaCall Command Line Interface by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A command line interface example as metacall wrapper. - * - */ - -/* -- Headers -- */ - -#include - -#include - -/* -- Namespace Declarations -- */ - -using namespace metacallcli; - -/* -- Methods -- */ - -parser::parser(const tokenizer::iterator & it) : it(it) -{ - -} - -parser::~parser() -{ - -} - -parser_parameter::parser_parameter(const tokenizer::iterator & it) : parser(it) -{ - -} - -parser_parameter::~parser_parameter() -{ - -} - -template <> bool parser_parameter::is() -{ - const std::string & str = *it; - - size_t len = str.length(); - - char first = str[0]; - - char last = str[len - 1]; - - return (first == last) && (first == '\''); -} - -template <> bool parser_parameter::is() -{ - const std::string & str = *it; - - size_t length = str.length(); - - std::stringstream str_stream; - - long l; - - if (isdigit(str[0]) && str[length - 1] == 'L') - { - str_stream.str(str.substr(0, length - 1)); - } - else - { - str_stream.str(str); - } - - return (str_stream >> std::noskipws >> l && !str_stream.ignore()); -} - -template <> bool parser_parameter::is() -{ - const std::string & str = *it; - - size_t length = str.length(); - - std::stringstream str_stream; - - float f; - - if (str[length - 1] == 'f') - { - str_stream.str(str.substr(0, length - 1)); - } - else - { - str_stream.str(str); - } - - return (str_stream >> std::noskipws >> f && !str_stream.ignore()); -} - -template <> bool parser_parameter::is() -{ - const std::string & str = *it; - - size_t length = str.length(); - - char first = str[0]; - - char last = str[length - 1]; - - return (first == last) && (first == '"'); -} - -template <> char parser_parameter::to() -{ - const std::string & str = *it; - - size_t length = str.length(); - - char c = str[1]; - - if (length < 4) - { - return c; - } - - if (length < 5 && c == '\\') - { - char special = str[2]; - - switch (special) - { - case 't' : return '\t'; - case 'n' : return '\n'; - case 'r' : return '\r'; - case 'v' : return '\v'; - case 'f' : return '\f'; - case '0' : return '\0'; - case '\\' : return '\\'; - case '\'' : return '\''; - - default: - { - std::cout << "Invalid character (" << special << ")" << std::endl; - - return special; - } - } - } - - return '\0'; -} - -template <> long parser_parameter::to() -{ - const std::string & str = *it; - - size_t length = str.length(); - - std::stringstream str_stream; - - long l; - - if (isdigit(str[0]) && str[length - 1] == 'L') - { - str_stream.str(str.substr(0, length - 1)); - } - else - { - str_stream.str(str); - } - - str_stream >> std::noskipws >> l; - - return l; -} - -template <> float parser_parameter::to() -{ - const std::string & str = *it; - - size_t length = str.length(); - - std::stringstream str_stream; - - float f; - - if (str[length - 1] == 'f') - { - str_stream.str(str.substr(0, length - 1)); - } - else - { - str_stream.str(str); - } - - str_stream >> std::noskipws >> f; - - return f; -} - -template <> std::string parser_parameter::to() -{ - const std::string & str = *it; - - size_t length = str.length() - 2; - - return str.substr(1, length); -} diff --git a/source/cli/metacallcli/source/tokenizer.cpp b/source/cli/metacallcli/source/tokenizer.cpp deleted file mode 100644 index 97c24e432f..0000000000 --- a/source/cli/metacallcli/source/tokenizer.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * MetaCall Command Line Interface by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A command line interface example as metacall wrapper. - * - */ - -/* -- Headers -- */ - -#include - -/* -- Namespace Declarations -- */ - -using namespace metacallcli; - -/* -- Protected Definitions -- */ - -const std::string tokenizer::default_delimiters(" \n\t\r\v\f"); - -/* -- Methods -- */ - -tokenizer::tokenizer(const std::string & str) : str(str), delimiters(default_delimiters) -{ - -} - -tokenizer::tokenizer(const std::string & str, const std::string & delimiters) : - str(str), delimiters(delimiters) -{ - -} - -tokenizer::~tokenizer() -{ - -} - -tokenizer::iterator::iterator(const tokenizer & t, size_t begin) : - t(t), offset(begin), token("") -{ - ++(*this); -} - -void tokenizer::delimit(const std::string & del) -{ - delimiters = del; -} - -tokenizer::iterator & tokenizer::iterator::operator++() -{ - size_t token_position = t.str.find_first_not_of(t.delimiters, offset); - - size_t next_delimiter_position; - - if (std::string::npos == token_position) - { - offset = t.str.length() + 1; - - return *this; - } - - next_delimiter_position = t.str.find_first_of(t.delimiters, token_position); - - if (std::string::npos == next_delimiter_position) - { - token = t.str.substr(token_position); - - offset = t.str.length(); - - return *this; - } - - token = t.str.substr(token_position, next_delimiter_position - token_position); - - offset = next_delimiter_position; - - return *this; -} - -tokenizer::iterator tokenizer::iterator::operator++(int) -{ - iterator ret = *this; - - ++(*this); - - return ret; -} - -bool tokenizer::iterator::operator==(iterator other) const -{ - return offset == other.offset && t.str == other.t.str; -} - -bool tokenizer::iterator::operator!=(iterator other) const -{ - return !(*this == other); -} - -tokenizer::iterator::reference tokenizer::iterator::operator*() const -{ - return token; -} - -size_t tokenizer::iterator::position() const -{ - return offset; -} - -tokenizer::iterator::reference tokenizer::iterator::escape(const std::string & characters) -{ - token.erase(0, token.find_first_not_of(characters)); - - token.erase(token.find_last_not_of(characters) + 1); - - return token; -} - -tokenizer::iterator tokenizer::begin() const -{ - return tokenizer::iterator(*this); -} - -tokenizer::iterator tokenizer::end() const -{ - return tokenizer::iterator(*this, str.length() + 1); -} diff --git a/source/cli/metacallcli/test/cli-test-argv.py b/source/cli/metacallcli/test/cli-test-argv.py new file mode 100644 index 0000000000..e247d3e6a7 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-argv.py @@ -0,0 +1,4 @@ +# This test verifies that argv works properly in Python Loader +import sys + +print('Test: ' + sys.argv[0]) diff --git a/source/cli/metacallcli/test/cli-test-main.py b/source/cli/metacallcli/test/cli-test-main.py new file mode 100644 index 0000000000..6903efb5c6 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-main.py @@ -0,0 +1,4 @@ +# This test verifies that __name__ == "__main__" works properly in Python Loader + +if __name__ == "__main__": + print('Test: 1234567890abcd') diff --git a/source/cli/metacallcli/test/cli-test-null-undefined.js b/source/cli/metacallcli/test/cli-test-null-undefined.js new file mode 100644 index 0000000000..c452aefb28 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-null-undefined.js @@ -0,0 +1,9 @@ +function test_null_and_undefined(name) { + if (name) { + return `Hello, ${name}`; + } else { + return 'Hello 342521512461246!' + } +} + +module.exports = test_null_and_undefined; diff --git a/source/cli/metacallcli/test/cli-test-onload.js b/source/cli/metacallcli/test/cli-test-onload.js new file mode 100644 index 0000000000..1d15b9c625 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-onload.js @@ -0,0 +1,7 @@ +const { metacall_load_from_memory, metacall } = require('metacall'); + +metacall_load_from_memory('py', 'def sum(a, b):\n\treturn a + b'); +metacall_load_from_memory('rb', 'def mult(a, b)\n\ta * b\nend'); + +metacall('sum', 3, 4) !== 7 && process.exit(1); +metacall('mult', 3, 4) !== 12 && process.exit(1); diff --git a/source/cli/metacallcli/test/cli-test-rb.py.in b/source/cli/metacallcli/test/cli-test-rb.py.in index 65525eec23..572c11d970 100644 --- a/source/cli/metacallcli/test/cli-test-rb.py.in +++ b/source/cli/metacallcli/test/cli-test-rb.py.in @@ -1,22 +1,26 @@ #!/usr/bin/env python3 -import os -import sys -import json +def main(): + import os + import sys + import json -# Insert Python Port folder first in the system path list -sys.path.insert(0, '@PROJECT_METACALL_PORTS_DIRECTORY@') + # Insert Python Port folder first in the system path list + sys.path.insert(0, '@PROJECT_METACALL_PORTS_DIRECTORY@') -from metacall import metacall, metacall_load_from_file, metacall_load_from_memory + from metacall import metacall, metacall_load_from_file, metacall_load_from_memory -# Load ruby -metacall_load_from_file('rb', ['hello.rb']); + # Load ruby + metacall_load_from_file('rb', ['hello.rb']); -# Load test from memory -script = ''' + # Load test from memory + script = ''' from metacall import metacall def test(): return metacall('say_string_without_spaces', 'asd'); ''' -metacall_load_from_memory('py', script); + metacall_load_from_memory('py', script); + +# Run the main +main(); diff --git a/source/cli/metacallcli/test/cli-test.js b/source/cli/metacallcli/test/cli-test.js index 984a57b650..b7119e20c1 100644 --- a/source/cli/metacallcli/test/cli-test.js +++ b/source/cli/metacallcli/test/cli-test.js @@ -1,6 +1,3 @@ -// Require MetaCall directly from Node Port -require('../../source/ports/node_port/index.js'); - -const { a } = require('cli-test-target.py'); +const { a } = require('./cli-test-target.py'); console.log(a()); diff --git a/source/cli/metacallcli/test/cli-test.py.in b/source/cli/metacallcli/test/cli-test.py.in index e6e8a82907..c6f01d1e3c 100644 --- a/source/cli/metacallcli/test/cli-test.py.in +++ b/source/cli/metacallcli/test/cli-test.py.in @@ -1,29 +1,33 @@ #!/usr/bin/env python3 -import os -import sys -import json +def main(): + import os + import sys + import json -# Insert Python Port folder first in the system path list -sys.path.insert(0, '@PROJECT_METACALL_PORTS_DIRECTORY@') + # Insert Python Port folder first in the system path list + sys.path.insert(0, '@PROJECT_METACALL_PORTS_DIRECTORY@') -from metacall import metacall, metacall_load_from_file, metacall_load_from_memory, metacall_inspect + from metacall import metacall, metacall_load_from_file, metacall_load_from_memory, metacall_inspect -# Load mock -metacall_load_from_file('mock', ['test.mock']); + # Load mock + metacall_load_from_file('mock', ['test.mock']); -# Load test from memory -script = ''' + # Load test from memory + script = ''' from metacall import metacall def test(): # Execute a call to mock previously loaded return metacall('my_empty_func_int'); ''' -metacall_load_from_memory('py', script); + metacall_load_from_memory('py', script); -# Execute a call to mock -print(metacall('three_str', 'a', 'b', 'c')); + # Execute a call to mock + print(metacall('three_str', 'a', 'b', 'c')); -# Inspect contents -print(json.dumps(metacall_inspect(), indent = 2)); + # Inspect contents + print(json.dumps(metacall_inspect(), indent = 2)); + +# Run the main +main(); diff --git a/source/cli/metacallcli/test/commands/command_runner.cmake b/source/cli/metacallcli/test/commands/command_runner.cmake new file mode 100644 index 0000000000..297e94ef5a --- /dev/null +++ b/source/cli/metacallcli/test/commands/command_runner.cmake @@ -0,0 +1 @@ +execute_process(COMMAND ${EXECUTABLE} INPUT_FILE ${INPUT}) diff --git a/source/cli/metacallcli/test/commands/metacallcli-file.txt b/source/cli/metacallcli/test/commands/metacallcli-file.txt new file mode 100644 index 0000000000..ea4b5c3e1a --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-file.txt @@ -0,0 +1,4 @@ +load file template.html +inspect +call template.html() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-inspect-leak.txt b/source/cli/metacallcli/test/commands/metacallcli-inspect-leak.txt new file mode 100644 index 0000000000..f7fcfdcc9d --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-inspect-leak.txt @@ -0,0 +1,2 @@ +inspect +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-null-empty.txt b/source/cli/metacallcli/test/commands/metacallcli-node-null-empty.txt new file mode 100644 index 0000000000..37c69a2dd9 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-null-empty.txt @@ -0,0 +1,4 @@ +load node cli-test-null-undefined.js +inspect +call test_null_and_undefined() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-null-undefined.txt b/source/cli/metacallcli/test/commands/metacallcli-node-null-undefined.txt new file mode 100644 index 0000000000..e506b21805 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-null-undefined.txt @@ -0,0 +1,4 @@ +load node cli-test-null-undefined.js +inspect +call test_null_and_undefined(undefined) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-null.txt b/source/cli/metacallcli/test/commands/metacallcli-node-null.txt new file mode 100644 index 0000000000..7a1b7616de --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-null.txt @@ -0,0 +1,4 @@ +load node cli-test-null-undefined.js +inspect +call test_null_and_undefined(null) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-port-py-rb.txt b/source/cli/metacallcli/test/commands/metacallcli-node-port-py-rb.txt new file mode 100644 index 0000000000..d522558a6b --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-port-py-rb.txt @@ -0,0 +1,3 @@ +load node cli-test-onload.js +inspect +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-port-py.txt b/source/cli/metacallcli/test/commands/metacallcli-node-port-py.txt new file mode 100644 index 0000000000..e601228e4a --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-port-py.txt @@ -0,0 +1,3 @@ +load node cli-test.js +inspect +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node.txt b/source/cli/metacallcli/test/commands/metacallcli-node.txt new file mode 100644 index 0000000000..519b7b6c74 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node.txt @@ -0,0 +1,4 @@ +load node nod.js +inspect +call hello_boy(300, 4001234) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-exception.txt b/source/cli/metacallcli/test/commands/metacallcli-py-exception.txt new file mode 100644 index 0000000000..c120e3648b --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-exception.txt @@ -0,0 +1,5 @@ +load py ducktype.py +inspect +call sum(1, "a") +call sum(33, 33) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-port-rb.txt b/source/cli/metacallcli/test/commands/metacallcli-py-port-rb.txt new file mode 100644 index 0000000000..d6977a74d4 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-port-rb.txt @@ -0,0 +1,4 @@ +load py cli-test-rb.py +inspect +call test() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-port.txt b/source/cli/metacallcli/test/commands/metacallcli-py-port.txt new file mode 100644 index 0000000000..cf3f9744c8 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-port.txt @@ -0,0 +1,4 @@ +load py cli-test.py +inspect +call test() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-tsx.txt b/source/cli/metacallcli/test/commands/metacallcli-py-tsx.txt new file mode 100644 index 0000000000..5d8f83c660 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-tsx.txt @@ -0,0 +1,2 @@ +load py cli-test-tsx.py +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-ts.txt b/source/cli/metacallcli/test/commands/metacallcli-ts.txt new file mode 100644 index 0000000000..81067eee1b --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-ts.txt @@ -0,0 +1,4 @@ +load ts typedfunc.ts +inspect +call typed_sum(4, 51350) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-tsx-templating.txt b/source/cli/metacallcli/test/commands/metacallcli-tsx-templating.txt new file mode 100644 index 0000000000..90272f632e --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-tsx-templating.txt @@ -0,0 +1,4 @@ +load ts templating.tsx +inspect +call hello("metaprogrammer") +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli.txt b/source/cli/metacallcli/test/commands/metacallcli.txt new file mode 100644 index 0000000000..bc1fa23086 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli.txt @@ -0,0 +1,3 @@ +load mock a.mock +inspect +exit diff --git a/source/cli/metacallcli/test/test.py b/source/cli/metacallcli/test/test.py new file mode 100644 index 0000000000..063b1d7055 --- /dev/null +++ b/source/cli/metacallcli/test/test.py @@ -0,0 +1,9 @@ +# This test verifies that Python Loader loads local scripts before global ones +# test.py has the same file name (without extension) as the test module from Python stdlib +# so basically what happens is that Python is seeing an "import test", and loads first the +# stdlib module instead of the test.py, with the refactor done, the local file should be load first + +def multiply_type(a: int, b: int) -> int: + return a * b + +print('Test: ' + str(multiply_type(33336666, 2))) diff --git a/source/cli/metacallcli/test/time.py b/source/cli/metacallcli/test/time.py new file mode 100644 index 0000000000..d181d115dd --- /dev/null +++ b/source/cli/metacallcli/test/time.py @@ -0,0 +1,3 @@ +import time + +time.sleep(1) diff --git a/source/cli/plugins/CMakeLists.txt b/source/cli/plugins/CMakeLists.txt new file mode 100644 index 0000000000..cf76080cee --- /dev/null +++ b/source/cli/plugins/CMakeLists.txt @@ -0,0 +1,39 @@ +# +# NodeJS Dependency +# + +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(SEND_ERROR "NodeJS libraries not found") + return() +endif() + +# Generate output directories for CLI plugins +execute_process( + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli" + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/internal" + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/repl" + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/cmd" +) + +# +# REPL Plugins +# + +add_subdirectory(cli_repl_plugin) +add_subdirectory(cli_core_plugin) + +# +# CMD Plugins +# + +# NodeJS added util.parseArgs([config]) in versions v18.3.0, v16.17.0 +# Check for compatibility, otherwise use fallback command parser in the CLI +if(NOT (NodeJS_VERSION VERSION_GREATER_EQUAL "18.3.0" OR (NodeJS_VERSION_MAJOR LESS 18 AND NodeJS_VERSION VERSION_GREATER_EQUAL "16.17.0"))) + message(WARNING "NodeJS version ${NodeJS_VERSION} does not support CLI command line plugins, at least v18.3.0 or v16.17.0 are required, using fallback command parser") + return() +endif() + +add_subdirectory(cli_cmd_plugin) +add_subdirectory(cli_sandbox_plugin) diff --git a/source/cli/plugins/cli_cmd_plugin/CMakeLists.txt b/source/cli/plugins/cli_cmd_plugin/CMakeLists.txt new file mode 100644 index 0000000000..5e929d8965 --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/CMakeLists.txt @@ -0,0 +1,68 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Plugin name and options +# + +# Target name +set(target cli_cmd_plugin) + +# Exit here if required dependencies are not met +message(STATUS "Plugin ${target}") + +# +# Source +# + +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +# +# Destination +# + +set(PLUGIN_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/plugins/cli/internal/${target}") + +# +# Project Target +# + +add_custom_target(${target} ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/metacall.json ${PLUGIN_OUTPUT_DIRECTORY}/metacall.json + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/cli_cmd_plugin.js ${PLUGIN_OUTPUT_DIRECTORY}/cli_cmd_plugin.js +) + +# +# Target Properties +# + +set_target_properties(${target} + PROPERTIES + FOLDER "${IDE_FOLDER}" +) + +# +# Dependencies +# + +add_dependencies(${target} + plugin_extension + node_loader +) + +# +# Define test +# + +# Check if tests are enabled +if(NOT OPTION_BUILD_TESTS) + return() +endif() + +add_test(NAME ${target} + COMMAND ${NodeJS_EXECUTABLE} test.js + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source +) diff --git a/source/cli/plugins/cli_cmd_plugin/include/cli_cmd_plugin/cli_cmd_plugin.hpp b/source/cli/plugins/cli_cmd_plugin/include/cli_cmd_plugin/cli_cmd_plugin.hpp new file mode 100644 index 0000000000..3a82b78469 --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/include/cli_cmd_plugin/cli_cmd_plugin.hpp @@ -0,0 +1,67 @@ +/* + * CLI Command Plugin by Parra Studios + * A plugin implementing command line functionality for MetaCall CLI. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef CLI_CMD_PLUGIN_H +#define CLI_CMD_PLUGIN_H 1 + +#include + +#define cli_cmd_plugin() metacall_handle("node", "plugins/cli/internal/cli_cmd_plugin/cli_cmd_plugin.js") + +#define cli_cmd_plugin_register(cli_cmd_handle, name, type, destroy) \ + do \ + { \ + const size_t map_size = destroy == NULL ? 1 : 2; \ +\ + void *args[] = { \ + metacall_value_create_string(name, sizeof(name) - 1), \ + metacall_value_create_map(NULL, map_size) \ + }; \ +\ + void **options = metacall_value_to_map(args[1]); \ + options[0] = metacall_value_create_array(NULL, 2); \ + void **type_tupla = metacall_value_to_array(options[0]); \ +\ + static const char type_key[] = "type"; \ + static const char type_value[] = type; \ +\ + type_tupla[0] = metacall_value_create_string(type_key, sizeof(type_key) - 1); \ + type_tupla[1] = metacall_value_create_string(type_value, sizeof(type_value) - 1); \ +\ + if (destroy != NULL) \ + { \ + options[1] = metacall_value_create_array(NULL, 2); \ + void **destroy_tupla = metacall_value_to_array(options[1]); \ +\ + static const char destroy_key[] = "destroy"; \ +\ + destroy_tupla[0] = metacall_value_create_string(destroy_key, sizeof(destroy_key) - 1); \ + destroy_tupla[1] = metacall_value_create_function(metacall_handle_function(handle, destroy)); \ + } \ +\ + void *ret = metacallhv_s(cli_cmd_handle, "command_register", args, sizeof(args) / sizeof(args[0])); \ +\ + metacall_value_destroy(ret); \ + metacall_value_destroy(args[0]); \ + metacall_value_destroy(args[1]); \ +\ + } while (0) + +#endif /* CLI_CMD_PLUGIN_H */ diff --git a/source/cli/plugins/cli_cmd_plugin/source/cli_cmd_plugin.js b/source/cli/plugins/cli_cmd_plugin/source/cli_cmd_plugin.js new file mode 100644 index 0000000000..2c2045dce0 --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/source/cli_cmd_plugin.js @@ -0,0 +1,123 @@ +const util = require('util'); +const path = require('path'); +const fs = require('fs'); + +const options = {}; + +function command_initialize(plugin_path) { + /* Initialize all CMD descriptors + * This will load all plugin descriptors like: + * plugins/cli/cmd/${plugin_name}/${plugin_name}_cmd.js + * + * The expected format is the following (e.g: cli_help_plugin.js): + * module.exports = { + * 'help': { + * short: 'h', + * type: 'METACALL_BOOL' // This field is required + * fn: () => {} // Function will be executed when the command is called + * destroy: () => {} // Function will be executed at the end of the CLI lifecycle + * } + * }; + */ + const cmd_path = path.join(plugin_path, 'cli', 'cmd'); + const files = (() => { + try { + return fs.readdirSync(cmd_path); + } catch (e) { + /* If the directory does not exist, return no files */ + if (e !== undefined && e.code === 'ENOENT') { + return [] + } + + /* Otherwise, rethrow the exception */ + throw e; + } + })(); + + for (const file of files) { + const file_path = path.join(cmd_path, file); + const file_stat = fs.statSync(file_path); + + if (file_stat.isDirectory()) { + const descriptor_path = path.join(file_path, `${file}_cmd.js`); + try { + const descriptor_stat = fs.statSync(descriptor_path); + + if (descriptor_stat.isFile()) { + const descriptor = require(descriptor_path); + options = { ...options, ...descriptor }; + } + } catch (e) { + /* Skip */ + } + } + } +} + +function command_register(cmd, { short, type, multiple, func, destroy }) { + const type_map = { + METACALL_STRING: 'string', + METACALL_BOOL: 'boolean' + }; + + options[cmd] = { + type: type_map[type || 'METACALL_BOOL'] + }; + + if (short) { + options[cmd]['short'] = short; + } + + if (multiple) { + options[cmd]['multiple'] = multiple; + } + + if (func) { + options[cmd]['func'] = func; + } + + if (destroy) { + options[cmd]['destroy'] = destroy; + } +} + +function command_parse(...args) { + const { values, positionals } = util.parseArgs({ + args, + options, + allowPositionals: true + }); + + return [ { ...values }, positionals ]; +} + +function command_function(cmd) { + const COMMAND_NOT_REGISTERED = 0; + const COMMAND_FUNCTION_UNDEFINED = 1; + + if (options[cmd] === undefined) { + return COMMAND_NOT_REGISTERED; + } + + if (options[cmd].func === undefined) { + return COMMAND_FUNCTION_UNDEFINED; + } + + return options[cmd].func; +} + +function command_destroy() { + for (const { destroy } of Object.values(options)) { + if (destroy) { + destroy(); + } + } +} + +module.exports = { + command_initialize, + command_register, + command_parse, + command_function, + command_destroy +}; diff --git a/source/cli/plugins/cli_cmd_plugin/source/metacall.json b/source/cli/plugins/cli_cmd_plugin/source/metacall.json new file mode 100644 index 0000000000..408e5ab54f --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/source/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "cli_cmd_plugin.js" + ] +} diff --git a/source/cli/plugins/cli_cmd_plugin/source/test.js b/source/cli/plugins/cli_cmd_plugin/source/test.js new file mode 100644 index 0000000000..c3f8903ecd --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/source/test.js @@ -0,0 +1,15 @@ +const assert = require('assert').strict; +const { command_register, command_parse } = require('./cli_cmd_plugin') + +command_register('empty', {}); +command_register('help', { short: 'h', type: 'METACALL_BOOL' }); +command_register('option', { short: 'o', type: 'METACALL_STRING' }); +command_register('multiple', { short: 'm', type: 'METACALL_STRING', multiple: true }); + +assert.deepEqual(command_parse(), [{}, []]); +assert.deepEqual(command_parse('--empty'), [{ empty: true }, []]); +assert.deepEqual(command_parse('-h'), [{ help: true }, []]); +assert.deepEqual(command_parse('--help'), [{ help: true }, []]); +assert.deepEqual(command_parse('--help', 'a.js'), [{ help: true }, ['a.js']]); +assert.deepEqual(command_parse('--option=hello', 'a.js'), [{ option: 'hello' }, ['a.js']]); +assert.deepEqual(command_parse('--multiple=a', '--multiple=b', '--multiple=c'), [{ multiple: ['a', 'b', 'c'] }, []]); diff --git a/source/cli/plugins/cli_core_plugin/CMakeLists.txt b/source/cli/plugins/cli_core_plugin/CMakeLists.txt new file mode 100644 index 0000000000..680417f11c --- /dev/null +++ b/source/cli/plugins/cli_core_plugin/CMakeLists.txt @@ -0,0 +1,215 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Plugin name and options +# + +# Target name +set(target cli_core_plugin) + +# Exit here if required dependencies are not met +message(STATUS "Plugin ${target}") + +# Set API export file and macro +string(TOUPPER ${target} target_upper) +set(export_file "include/${target}/${target}_api.h") +set(export_macro "${target_upper}_API") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/cli_core_plugin.h +) + +set(sources + ${source_path}/cli_core_plugin.cpp +) + +set(scripts + ${source_path}/cli_core_plugin_repl.js +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +set(script_group "Script Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) +source_group_by_path(${source_path} "\\\\.js$" + ${source_group} ${scripts}) + +# +# Create library +# + +# Build library +add_library(${target} MODULE + ${sources} + ${headers} +) + +# Create namespaced alias +add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# Export library for downstream projects +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) + +# Create API export header +generate_export_header(${target} + EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${export_macro} +) + +# +# Project options +# + +set(PLUGIN_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/plugins/cli/repl/${target}") + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" + BUNDLE $<$:$<$>> + + # Define custom build output directory + LIBRARY_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${PLUGIN_OUTPUT_DIRECTORY}" + + RUNTIME_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${PLUGIN_OUTPUT_DIRECTORY}" + + ARCHIVE_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${PLUGIN_OUTPUT_DIRECTORY}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + + $ # MetaCall includes + + PUBLIC + ${DEFAULT_INCLUDE_DIRECTORIES} + + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${META_PROJECT_NAME}::metacall # MetaCall library + + PUBLIC + ${DEFAULT_LIBRARIES} + + INTERFACE +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + + PUBLIC + $<$>:${target_upper}_STATIC_DEFINE> + ${DEFAULT_COMPILE_DEFINITIONS} + + INTERFACE +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_COMPILE_OPTIONS} + + INTERFACE +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_LINKER_OPTIONS} + + INTERFACE +) + +# +# Dependencies +# + +add_dependencies(${target} + plugin_extension + cli_repl_plugin +) + +# +# Configuration +# + +add_custom_target(${target}_config ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${PLUGIN_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/metacall.json ${PLUGIN_OUTPUT_DIRECTORY}/metacall.json + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/cli_core_plugin_repl.js ${PLUGIN_OUTPUT_DIRECTORY}/cli_core_plugin_repl.js +) + +set_target_properties(${target}_config + PROPERTIES + FOLDER "${IDE_FOLDER}" +) diff --git a/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h b/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h new file mode 100644 index 0000000000..5f43a1358d --- /dev/null +++ b/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h @@ -0,0 +1,36 @@ +/* + * CLI Core Plugin by Parra Studios + * A plugin implementing core functionality for MetaCall CLI. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef CLI_CORE_PLUGIN_H +#define CLI_CORE_PLUGIN_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +CLI_CORE_PLUGIN_API int cli_core_plugin(void *loader, void *handle); + +#ifdef __cplusplus +} +#endif + +#endif /* CLI_CORE_PLUGIN_H */ diff --git a/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp b/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp new file mode 100644 index 0000000000..ea19c681d4 --- /dev/null +++ b/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp @@ -0,0 +1,686 @@ +/* + * CLI Core Plugin by Parra Studios + * A plugin implementing core functionality for MetaCall CLI. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Error messages */ +#define LOAD_ERROR "Failed to load a script" +#define INSPECT_ERROR "Failed to inspect MetaCall context" +#define EVAL_ERROR "Failed to evaluate the expression" +#define CALL_ERROR "Failed to call the function" +#define AWAIT_ERROR "Failed to await the function" +#define CLEAR_ERROR "Failed to clear the handle" +#define COPYRIGHT_ERROR "Failed to show the copyright" +#define HELP_ERROR "Failed to show the help" +#define DEBUG_ERROR "Failed to debug the command" + +#define COPYRIGHT_PRINT() \ + do \ + { \ + std::cout << "MetaCall Command Line Interface by Parra Studios" << std::endl; \ + std::cout << "A command line interface for MetaCall Core" << std::endl; \ + std::cout << std::endl; \ + std::cout << "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia " << std::endl; \ + } while (0) + +template +void *extension_function_check_array(const char *error, void *v, std::vector &array, cast_func_type &cast_func, enum metacall_value_id id) +{ + size_t size = metacall_value_count(v); + void **values = metacall_value_to_array(v); + + array.reserve(size); + + for (size_t i = 0; i < size; ++i) + { + if (metacall_value_id(values[i]) != id) + { + std::stringstream ss; + + ss << error << ", calling with wrong type of argument at argument position #" + << (i + 1) << ", expected " << metacall_value_id_name(id) + << ", got " << metacall_value_type_name(values[i]); + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + array.push_back(cast_func(values[i])); + } + + return NULL; +} + +static const std::vector loader_tags(void) +{ + static const std::vector loaders = { + "mock", "py", "node", "rb", "cs", "cob", + "ts", "js", "file", "wasm", "rs", "c", + "rpc", "ext", "java" + }; + + return loaders; +} + +static bool loader_tag_is_valid(const char *tag) +{ + static const std::vector loaders = loader_tags(); + + /* Check if invalid loader tag */ + return std::find(std::begin(loaders), std::end(loaders), tag) != std::end(loaders); +} + +static void *throw_invalid_tag(const char *error, const char *tag) +{ + static const std::vector loaders = loader_tags(); + auto it = std::begin(loaders); + std::stringstream ss; + ss << error << ", " << tag << " is not a valid tag, use: " << *it; + + do + { + ++it; + ss << ", " << *it; + } while (it != std::end(loaders)); + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); +} + +static void value_array_for_each(void *v, const std::function &lambda) +{ + void **v_array = static_cast(metacall_value_to_array(v)); + size_t count = metacall_value_count(v); + + std::for_each(v_array, v_array + count, lambda); +} + +static void value_map_for_each(void *v, const std::function &lambda) +{ + void **v_map = static_cast(metacall_value_to_map(v)); + size_t count = metacall_value_count(v); + + std::for_each(v_map, v_map + count, [&lambda](void *element) { + void **v_element = metacall_value_to_array(element); + lambda(metacall_value_to_string(v_element[0]), v_element[1]); + }); +} + +static void *parse_function_call(const char *error, void *func_str_value, void **func, std::string &func_args) +{ + std::string func_str = metacall_value_to_string(func_str_value); + + if (func_str.find('(') == std::string::npos || func_str.find(')') == std::string::npos) + { + std::stringstream ss; + + ss << error << ", calling function with malformed function call string: " << func_str; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + std::string::size_type first_parenthesis = func_str.find_first_of('('); + std::string::size_type last_parenthesis = func_str.find_first_of(')'); + std::string func_name = func_str.substr(0, first_parenthesis); + + if (first_parenthesis != func_name.size() || last_parenthesis != (func_str.size() - 1)) + { + std::stringstream ss; + + ss << error << ", calling function with malformed parameters: " << func_str; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + /* Convert arguments into an array */ + func_args = "["; + func_args += func_str.substr(first_parenthesis + 1, func_str.size() - first_parenthesis - 2); + func_args += "]"; + + /* Check if function is loaded */ + const char *func_name_str = const_cast(func_name.c_str()); + *func = metacall_function(func_name_str); + + if (*func == NULL) + { + std::stringstream ss; + + ss << error << ", function '" << func_name << "' does not exist"; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + return NULL; +} + +void *load(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(LOAD_ERROR, METACALL_STRING, METACALL_ARRAY); + + char *tag = metacall_value_to_string(args[0]); + + if (!loader_tag_is_valid(tag)) + { + return throw_invalid_tag(LOAD_ERROR, tag); + } + + std::vector scripts; + void *error = extension_function_check_array(LOAD_ERROR, args[1], scripts, metacall_value_to_string, METACALL_STRING); + + if (error != NULL) + { + return error; + } + + std::vector scripts_cstr; + + scripts_cstr.reserve(scripts.size()); + + for (auto it = std::begin(scripts); it != std::end(scripts); ++it) + { + scripts_cstr.push_back((*it).c_str()); + } + + int ret = metacall_load_from_file(tag, scripts_cstr.data(), scripts_cstr.size(), NULL); + + return metacall_value_create_int(ret); +} + +void *inspect(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(INSPECT_ERROR); + + void *v = metacall_inspect_value(); + + if (v == NULL) + { + EXTENSION_FUNCTION_THROW(INSPECT_ERROR ", inspect returned invalid value"); + } + + /* Print run-times */ + value_map_for_each(v, [](const char *key, void *modules) { + std::cout << "runtime " << key; + + if (metacall_value_count(modules) == 0) + { + std::cout << std::endl; + return; + } + + std::cout << " {" << std::endl; + + /* Print scripts */ + value_array_for_each(modules, [](void *module) { + /* Get module name */ + void **v_module_map = static_cast(metacall_value_to_map(module)); + void **v_module_name_tuple = metacall_value_to_array(v_module_map[0]); + const char *name = metacall_value_to_string(v_module_name_tuple[1]); + + std::cout << "\tmodule " << name << " { " << std::endl; + + /* Get module functions */ + void **v_module_scope_tuple = metacall_value_to_array(v_module_map[1]); + void **v_scope_map = metacall_value_to_map(v_module_scope_tuple[1]); + void **v_scope_funcs_tuple = metacall_value_to_array(v_scope_map[1]); + + if (metacall_value_count(v_scope_funcs_tuple[1]) != 0) + { + value_array_for_each(v_scope_funcs_tuple[1], [](void *func) { + /* Get function name */ + void **v_func_map = static_cast(metacall_value_to_map(func)); + void **v_func_tuple = metacall_value_to_array(v_func_map[0]); + const char *func_name = metacall_value_to_string(v_func_tuple[1]); + + std::cout << "\t\tfunction " << func_name << "("; + + /* Get function signature */ + void **v_signature_tuple = metacall_value_to_array(v_func_map[1]); + void **v_args_map = metacall_value_to_map(v_signature_tuple[1]); + void **v_args_tuple = metacall_value_to_array(v_args_map[1]); + void **v_args_array = metacall_value_to_array(v_args_tuple[1]); + + size_t iterator = 0, count = metacall_value_count(v_args_tuple[1]); + + value_array_for_each(v_args_array, [&iterator, &count](void *arg) { + void **v_arg_map = metacall_value_to_map(arg); + void **v_arg_name_tupla = metacall_value_to_array(v_arg_map[0]); + std::string parameter_name(metacall_value_to_string(v_arg_name_tupla[1])); + + if (parameter_name.empty()) + { + parameter_name += "arg"; + parameter_name += std::to_string(iterator); + } + + std::cout << parameter_name; + + if (iterator + 1 < count) + { + std::cout << ", "; + } + + ++iterator; + }); + + std::cout << ")" << std::endl; + }); + } + + std::cout << "\t}" << std::endl; + }); + + std::cout << "}" << std::endl; + }); + + metacall_value_destroy(v); + + return metacall_value_create_int(0); +} + +void *eval(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(EVAL_ERROR, METACALL_STRING, METACALL_STRING); + + char *tag = metacall_value_to_string(args[0]); + + if (!loader_tag_is_valid(tag)) + { + return throw_invalid_tag(EVAL_ERROR, tag); + } + + char *script = metacall_value_to_string(args[1]); + size_t size = metacall_value_size(args[1]); + + return metacall_value_create_int(metacall_load_from_memory(tag, script, size, NULL)); +} + +void *call(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(CALL_ERROR, METACALL_STRING); + + /* Parse function call */ + void *func = NULL; + std::string func_args; + void *error = parse_function_call(CALL_ERROR, args[0], &func, func_args); + + if (error != NULL) + { + return error; + } + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + void *result = metacallfs(func, func_args.c_str(), func_args.length() + 1, allocator); + + metacall_allocator_destroy(allocator); + + return result; +} + +void *await(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(CALL_ERROR, METACALL_STRING); + + /* Parse function call */ + void *func = NULL; + std::string func_args; + void *error = parse_function_call(AWAIT_ERROR, args[0], &func, func_args); + + if (error != NULL) + { + return error; + } + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + std::mutex await_mutex; /* Mutex for blocking the process until await is resolved */ + std::condition_variable await_cond; /* Condition to be fired once await method is resolved or rejected */ + + std::unique_lock lock(await_mutex); + + struct await_data_type + { + void *v; + std::mutex &mutex; + std::condition_variable &cond; + }; + + struct await_data_type fdata = { NULL, await_mutex, await_cond }; + + void *future = metacallfs_await( + func, func_args.c_str(), func_args.length() + 1, allocator, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->cond.notify_one(); + return NULL; + }, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->cond.notify_one(); + return NULL; + }, + static_cast(&fdata)); + + await_cond.wait(lock); + + /* Unused */ + metacall_value_destroy(future); + + metacall_allocator_destroy(allocator); + + return fdata.v; +} + +void *clear(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(CLEAR_ERROR, METACALL_STRING, METACALL_STRING); + + char *tag = metacall_value_to_string(args[0]); + + if (!loader_tag_is_valid(tag)) + { + return throw_invalid_tag(CLEAR_ERROR, tag); + } + + char *script = metacall_value_to_string(args[1]); + void *handle = metacall_handle(tag, script); + + if (handle == NULL) + { + std::stringstream ss; + + ss << CLEAR_ERROR ", handle '" << script << "' was not found in loader with tag: " << tag; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + return metacall_value_create_int(metacall_clear(handle)); +} + +void *copyright(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(COPYRIGHT_ERROR); + + COPYRIGHT_PRINT(); + + return metacall_value_create_int(0); +} + +void *help(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(HELP_ERROR); + + /* Print copyright first */ + COPYRIGHT_PRINT(); + + /* Command list */ + std::cout << std::endl + << "Command list:" << std::endl + << std::endl; + + /* Load command */ + std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "\t│ Load a script from file into MetaCall │" << std::endl; + std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; + std::cout << "\t│ Usage: │" << std::endl; + std::cout << "\t│ load ... │" << std::endl; + std::cout << "\t│ : identifier to the type of script │" << std::endl; + std::cout << "\t│ options : │" << std::endl; + std::cout << "\t│ mock - Mock (for testing purposes) │" << std::endl; + std::cout << "\t│ py - Python │" << std::endl; + std::cout << "\t│ node - NodeJS │" << std::endl; + std::cout << "\t│ rb - Ruby │" << std::endl; + std::cout << "\t│ cs - C# NetCore │" << std::endl; + std::cout << "\t│ cob - Cobol │" << std::endl; + std::cout << "\t│ ts - TypeScript │" << std::endl; + std::cout << "\t│ js - V8 JavaScript Engine │" << std::endl; + std::cout << "\t│ file - Files (for handling file systems) │" << std::endl; + std::cout << "\t│ ... : relative or absolute path to the script(s) │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Example: │" << std::endl; + std::cout << "\t│ load node concat.js │" << std::endl; + std::cout << "\t│ load py example.py │" << std::endl; + std::cout << "\t│ load rb hello.rb │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Result: │" << std::endl; + std::cout << "\t│ Script (concat.js) loaded correctly │" << std::endl; + std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl + << std::endl; + + /* Inspect command */ + std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "\t│ Show all runtimes, modules and functions (with their signature) loaded into MetaCall │" << std::endl; + std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; + std::cout << "\t│ Usage: │" << std::endl; + std::cout << "\t│ inspect │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Example: │" << std::endl; + std::cout << "\t│ inspect │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Result: │" << std::endl; + std::cout << "\t│ runtime node { │" << std::endl; + std::cout << "\t│ module concat { │" << std::endl; + std::cout << "\t│ function concat(left, right) │" << std::endl; + std::cout << "\t│ } │" << std::endl; + std::cout << "\t│ } │" << std::endl; + std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl + << std::endl; + + /* Eval command */ + std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "\t│ Evaluate a code snippet with the specified runtime tag │" << std::endl; + std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; + std::cout << "\t│ Usage: │" << std::endl; + std::cout << "\t│ eval + + + ); +} + +export default index; diff --git a/source/scripts/typescript/templating/CMakeLists.txt b/source/scripts/typescript/templating/CMakeLists.txt index 616f88123b..ca2e15fc9e 100644 --- a/source/scripts/typescript/templating/CMakeLists.txt +++ b/source/scripts/typescript/templating/CMakeLists.txt @@ -2,31 +2,30 @@ # External dependencies # -set(NODEJS_EXECUTABLE_ONLY ON) +find_package(NPM) -find_package(NodeJS 10.22.0) - -if(NOT NODEJS_FOUND) - message(STATUS "NodeJS executable not found") +if(NOT NPM_FOUND) + message(SEND_ERROR "NPM not found") return() endif() -# Detect if React is available -execute_process( - COMMAND ${NODEJS_EXECUTABLE} -e "require('react'); require('react-dom')" - RESULT_VARIABLE NODEJS_REACT_FOUND - ERROR_QUIET - OUTPUT_QUIET +# Dependencies +add_custom_target(typescript-templating-depends + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source/templating + COMMAND ${NPM_EXECUTABLE} install + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/templating/package.json ${CMAKE_CURRENT_SOURCE_DIR}/source/templating/package-lock.json ) -if("${NODEJS_REACT_FOUND}" EQUAL "1") - message(WARNING "NodeJS React not found, skipping the TypeScript templating script project") - return() -endif() - +set_target_properties(typescript-templating-depends + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}/TypeScript" +) # # Configure typescript project # ts_project(templating 0.1.0) + +add_dependencies(typescript-templating typescript-templating-depends) diff --git a/source/scripts/typescript/templating/source/templating/.gitignore b/source/scripts/typescript/templating/source/templating/.gitignore new file mode 100644 index 0000000000..982ce2624d --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/.gitignore @@ -0,0 +1,119 @@ +# Build directory +build + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/source/scripts/typescript/templating/source/templating/cli-test-tsx.py b/source/scripts/typescript/templating/source/templating/cli-test-tsx.py new file mode 100644 index 0000000000..8b82e45207 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/cli-test-tsx.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +# This script is here in order to reproduce the bug of python-tsx-example + +def main(): + import sys + import os + + # Insert Python Port folder first in the system path list + sys.path.insert(0, os.environ['PROJECT_METACALL_PORTS_DIRECTORY']) + + import metacall + from templating.tsx import hello + print(hello('World')) + +# Run the main +main(); diff --git a/source/scripts/typescript/templating/source/templating/package-lock.json b/source/scripts/typescript/templating/source/templating/package-lock.json new file mode 100644 index 0000000000..2f4aa09878 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/package-lock.json @@ -0,0 +1,120 @@ +{ + "name": "templating-tsx", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "templating-tsx", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } + } +} diff --git a/source/scripts/typescript/templating/source/templating/package.json b/source/scripts/typescript/templating/source/templating/package.json new file mode 100644 index 0000000000..1a182f9e45 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/package.json @@ -0,0 +1,13 @@ +{ + "name": "templating-tsx", + "version": "1.0.0", + "main": "templating.tsx", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "license": "Apache-2.0", + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } +} diff --git a/source/scripts/typescript/templating/source/templating.tsx b/source/scripts/typescript/templating/source/templating/templating.tsx similarity index 70% rename from source/scripts/typescript/templating/source/templating.tsx rename to source/scripts/typescript/templating/source/templating/templating.tsx index 70409f89fb..a2e102d5df 100644 --- a/source/scripts/typescript/templating/source/templating.tsx +++ b/source/scripts/typescript/templating/source/templating/templating.tsx @@ -2,5 +2,5 @@ import React from 'react'; import { renderToString } from 'react-dom/server'; export function hello(text: string): string { - return renderToString(

Hello ${text}

); + return renderToString(

{`Hello ${text}`}

); } diff --git a/source/scripts/typescript/templating/source/templating/tsconfig.json b/source/scripts/typescript/templating/source/templating/tsconfig.json new file mode 100644 index 0000000000..0bc8548d83 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/tsconfig.json @@ -0,0 +1,12 @@ +{ + "include": [ + "templating.tsx" + ], + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "esModuleInterop": true, + "jsx": "react", + "lib": ["es2016", "dom"] + } +} diff --git a/source/scripts/typescript/typedfunc/source/typedfunc.ts b/source/scripts/typescript/typedfunc/source/typedfunc/typedfunc.ts similarity index 63% rename from source/scripts/typescript/typedfunc/source/typedfunc.ts rename to source/scripts/typescript/typedfunc/source/typedfunc/typedfunc.ts index d77bdda072..9bad24df2a 100644 --- a/source/scripts/typescript/typedfunc/source/typedfunc.ts +++ b/source/scripts/typescript/typedfunc/source/typedfunc/typedfunc.ts @@ -1,11 +1,11 @@ 'use strict'; -export function typed_sum(left: number, rigth: number): number { - return left + rigth; +export function typed_sum(left: number, right: number): number { + return left + right; } -export async function typed_sum_async(left: number, rigth: number): number { - return left + rigth; +export async function typed_sum_async(left: number, right: number): Promise { + return left + right; } export function build_name(first: string, last = 'Smith') { @@ -14,11 +14,11 @@ export function build_name(first: string, last = 'Smith') { export function object_pattern_ts({ asd }) { return asd; -} +} export function typed_array(a: number[]): number { return a[0] + a[1] + a[2]; -} +} export function object_record(a: Record): number { return a.element; diff --git a/source/scripts/wasm/CMakeLists.txt b/source/scripts/wasm/CMakeLists.txt new file mode 100644 index 0000000000..34bff0bba5 --- /dev/null +++ b/source/scripts/wasm/CMakeLists.txt @@ -0,0 +1,14 @@ +# TODO: Automatically call wat2wasm on scripts when changed + +# Check if this script is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_WASM OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_WASM) + return() +endif() + +# Append cmake path +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# Wasm project utility +include(WasmProject) + +add_subdirectory(tests) diff --git a/source/scripts/wasm/cmake/WasmProject.cmake b/source/scripts/wasm/cmake/WasmProject.cmake new file mode 100644 index 0000000000..aa326a7401 --- /dev/null +++ b/source/scripts/wasm/cmake/WasmProject.cmake @@ -0,0 +1,51 @@ +# +# WebAssembly project generator by Parra Studios +# Generates a WebAssembly project embedded into CMake. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if(WASMPROJECT_FOUND) + return() +endif() + +set(WASMPROJECT_FOUND YES) + +# +# Generic script project generator +# + +include(ScriptProject) + +# Define current Wasm project configuration path +get_filename_component(WASM_PROJECT_CONFIG_PATH ${CMAKE_CURRENT_LIST_FILE} PATH) + +# +# Wasm sub-project util function +# + +function(wasm_project target version) + + # TODO + + # Configuration + set(PACKAGE_NAME ${target}) + set(PACKAGE_VERSION ${version}) + set(PACKAGE_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") + + # Create project file + script_project(${target} wasm ${WASM_PROJECT_CONFIG_PATH}/WasmProject.cmake.in) + +endfunction() diff --git a/source/scripts/wasm/cmake/WasmProject.cmake.in b/source/scripts/wasm/cmake/WasmProject.cmake.in new file mode 100644 index 0000000000..630c875bea --- /dev/null +++ b/source/scripts/wasm/cmake/WasmProject.cmake.in @@ -0,0 +1,35 @@ +# +# WebAssembly project generator by Parra Studios +# Generates a WebAssembly project embedded into CMake. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Check if this loader is enabled +if(NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_WASM) + return() +endif() + +# +# External dependencies +# + +# TODO + +# Target name +set(target @PACKAGE_NAME@) + +# Exit here if required dependencies are not met +message(STATUS "Script ${target}") diff --git a/source/scripts/wasm/tests/CMakeLists.txt b/source/scripts/wasm/tests/CMakeLists.txt new file mode 100644 index 0000000000..f7706402b9 --- /dev/null +++ b/source/scripts/wasm/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +# +# Configure Wasm project +# + +wasm_project(tests 0.1.0) diff --git a/source/scripts/wasm/tests/source/empty_module.wasm b/source/scripts/wasm/tests/source/empty_module.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/source/scripts/wasm/tests/source/empty_module.wasm differ diff --git a/source/scripts/wasm/tests/source/empty_module.wat b/source/scripts/wasm/tests/source/empty_module.wat new file mode 100644 index 0000000000..3af8f25454 --- /dev/null +++ b/source/scripts/wasm/tests/source/empty_module.wat @@ -0,0 +1 @@ +(module) diff --git a/source/scripts/wasm/tests/source/exports1.wat b/source/scripts/wasm/tests/source/exports1.wat new file mode 100644 index 0000000000..6dc3ff52c6 --- /dev/null +++ b/source/scripts/wasm/tests/source/exports1.wat @@ -0,0 +1,9 @@ +(module + (memory (export "memory") 1) + (table (export "table") 1 funcref) + (global (export "immutable_global") i32 (i32.const 1)) + (global (export "mutable_global") (mut i32) (i32.const 1)) + (func (export "duplicate_func") (result i32) + i32.const 1 + ) +) diff --git a/source/scripts/wasm/tests/source/exports2.wat b/source/scripts/wasm/tests/source/exports2.wat new file mode 100644 index 0000000000..92375ce9ec --- /dev/null +++ b/source/scripts/wasm/tests/source/exports2.wat @@ -0,0 +1,5 @@ +(module + (func (export "duplicate_func") (result i64) + i64.const 2 + ) +) diff --git a/source/scripts/wasm/tests/source/functions.wasm b/source/scripts/wasm/tests/source/functions.wasm new file mode 100644 index 0000000000..206383a061 Binary files /dev/null and b/source/scripts/wasm/tests/source/functions.wasm differ diff --git a/source/scripts/wasm/tests/source/functions.wat b/source/scripts/wasm/tests/source/functions.wat new file mode 100644 index 0000000000..655312543d --- /dev/null +++ b/source/scripts/wasm/tests/source/functions.wat @@ -0,0 +1,18 @@ +(module + (func (export "none_ret_none")) + (func (export "i64_ret_none") (param i64)) + (func (export "i32_f32_i64_f64_ret_none") (param i32) (param f32) (param i64) (param f64)) + (func (export "none_ret_i32") (result i32) + i32.const 1) + (func (export "none_ret_i32_f32_i64_f64") (result i32 f32 i64 f64) + i32.const 1 + f32.const 2 + i64.const 3 + f64.const 4) + (func (export "i32_f32_i64_f64_ret_i32_f32_i64_f64") (param i32) (param f32) (param i64) (param f64) (result i32 f32 i64 f64) + i32.const 1 + f32.const 2 + i64.const 3 + f64.const 4) + (func (export "trap") (unreachable)) +) diff --git a/source/scripts/wasm/tests/source/imports.wat b/source/scripts/wasm/tests/source/imports.wat new file mode 100644 index 0000000000..603144454d --- /dev/null +++ b/source/scripts/wasm/tests/source/imports.wat @@ -0,0 +1,12 @@ +(module + (import "exports1" "memory" (memory 1)) + (import "exports1" "table" (table 1 funcref)) + (import "exports1" "immutable_global" (global i32)) + (import "exports1" "mutable_global" (global (mut i32))) + + (import "exports1" "duplicate_func" (func $duplicate_func_i32 (result i32))) + (import "exports2" "duplicate_func" (func $duplicate_func_i64 (result i64))) + + (func (export "duplicate_func_i32") (result i32) (call $duplicate_func_i32)) + (func (export "duplicate_func_i64") (result i64) (call $duplicate_func_i64)) +) diff --git a/source/scripts/wasm/tests/source/invalid_module.wasm b/source/scripts/wasm/tests/source/invalid_module.wasm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source/serial/CMakeLists.txt b/source/serial/CMakeLists.txt index 16914d8636..87de042a14 100644 --- a/source/serial/CMakeLists.txt +++ b/source/serial/CMakeLists.txt @@ -10,7 +10,6 @@ message(STATUS "Lib ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -35,17 +34,12 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(headers ${include_path}/serial.h - ${include_path}/serial_impl.h - ${include_path}/serial_impl_handle.h + ${include_path}/serial_handle.h ${include_path}/serial_interface.h - ${include_path}/serial_singleton.h - ${include_path}/serial_host.h ) set(sources ${source_path}/serial.c - ${source_path}/serial_impl.c - ${source_path}/serial_singleton.c ) # Group source files @@ -70,30 +64,7 @@ add_library(${target} add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects -if(NOT OPTION_BUILD_DIST_LIBS) - export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -endif() - -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) # Create API export header generate_export_header(${target} @@ -140,12 +111,14 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory + ${META_PROJECT_NAME}::memory ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect ${META_PROJECT_NAME}::dynlink + ${META_PROJECT_NAME}::plugin + ${META_PROJECT_NAME}::reflect PUBLIC ${DEFAULT_LIBRARIES} @@ -159,6 +132,7 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE + ${target_upper}_EXPORTS # Export API PUBLIC $<$>:${target_upper}_STATIC_DEFINE> @@ -184,7 +158,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC @@ -192,17 +166,3 @@ target_link_libraries(${target} INTERFACE ) - -# -# Deployment -# - -# Library -if(NOT OPTION_BUILD_DIST_LIBS) - install(TARGETS ${target} - EXPORT "${target}-export" COMPONENT dev - RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime - LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime - ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev - ) -endif() diff --git a/source/serial/include/serial/serial.h b/source/serial/include/serial/serial.h index 6a0efe2d23..f10f9ea761 100644 --- a/source/serial/include/serial/serial.h +++ b/source/serial/include/serial/serial.h @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -15,19 +15,17 @@ #include +#include + #include #ifdef __cplusplus extern "C" { #endif -/* -- Forward Declarations -- */ - -struct serial_type; - /* -- Type Definitions -- */ -typedef struct serial_type * serial; +typedef plugin serial; /* -- Methods -- */ @@ -52,7 +50,7 @@ SERIAL_API int serial_initialize(void); * Pointer to serial on correct initialization, null otherwise * */ -SERIAL_API serial serial_create(const char * name); +SERIAL_API serial serial_create(const char *name); /** * @brief @@ -65,7 +63,7 @@ SERIAL_API serial serial_create(const char * name); * Static const string with serial extension * */ -SERIAL_API const char * serial_extension(serial s); +SERIAL_API const char *serial_extension(serial s); /** * @brief @@ -78,7 +76,7 @@ SERIAL_API const char * serial_extension(serial s); * Static const string with serial name * */ -SERIAL_API const char * serial_name(serial s); +SERIAL_API const char *serial_name(serial s); /** * @brief @@ -103,7 +101,7 @@ SERIAL_API const char * serial_name(serial s); * String with the value serialized on correct serialization, null otherwise * */ -SERIAL_API char * serial_serialize(serial s, value v, size_t * size, memory_allocator allocator); +SERIAL_API char *serial_serialize(serial s, value v, size_t *size, memory_allocator allocator); /** * @brief @@ -125,7 +123,7 @@ SERIAL_API char * serial_serialize(serial s, value v, size_t * size, memory_allo * Pointer to value deserialized on correct serialization, null otherwise * */ -SERIAL_API value serial_deserialize(serial s, const char * buffer, size_t size, memory_allocator allocator); +SERIAL_API value serial_deserialize(serial s, const char *buffer, size_t size, memory_allocator allocator); /** * @brief @@ -149,7 +147,7 @@ SERIAL_API void serial_destroy(void); * Static string containing module information * */ -SERIAL_API const char * serial_print_info(void); +SERIAL_API const char *serial_print_info(void); #ifdef __cplusplus } diff --git a/source/serial/include/serial/serial_impl_handle.h b/source/serial/include/serial/serial_handle.h similarity index 81% rename from source/serial/include/serial/serial_impl_handle.h rename to source/serial/include/serial/serial_handle.h index 4250254917..9c049fcbca 100644 --- a/source/serial/include/serial/serial_impl_handle.h +++ b/source/serial/include/serial/serial_handle.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ * */ -#ifndef SERIAL_IMPL_HANDLE_H -#define SERIAL_IMPL_HANDLE_H 1 +#ifndef SERIAL_HANDLE_H +#define SERIAL_HANDLE_H 1 /* -- Headers -- */ @@ -31,10 +31,10 @@ extern "C" { /* -- Type Definitions -- */ -typedef void * serial_impl_handle; +typedef void *serial_handle; #ifdef __cplusplus } #endif -#endif /* SERIAL_IMPL_HANDLE_H */ +#endif /* SERIAL_HANDLE_H */ diff --git a/source/serial/include/serial/serial_impl.h b/source/serial/include/serial/serial_impl.h deleted file mode 100644 index a26cb881a1..0000000000 --- a/source/serial/include/serial/serial_impl.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -* Serial Library by Parra Studios -* Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -* -* A cross-platform library for managing multiple serialization and deserialization formats. -* -*/ - -#ifndef SERIAL_IMPL_H -#define SERIAL_IMPL_H 1 - -/* -- Headers -- */ - -#include - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Forward Declarations -- */ - -struct serial_impl_type; - -/* -- Type Definitions -- */ - -typedef struct serial_impl_type * serial_impl; - -/* -- Methods -- */ - -/** -* @brief -* Create serial implementation -* -* @return -* Returns pointer to serial implementation correct creation, null otherwise -* -*/ -SERIAL_API serial_impl serial_impl_create(void); - -/** -* @brief -* Retrieve extension supported by the serial implementation -* -* @param[in] impl -* Pointer to serial implementation instance -* -* @return -* Returns constant string representing serial extension -* -*/ -SERIAL_API const char * serial_impl_extension(serial_impl impl); - -/** -* @brief -* Load serial implementation @impl -* -* @param[in] impl -* Pointer to the serial implementation to be loaded -* -* @param[in] path -* Path where dependency is located -* -* @param[in] name -* Dependency name to be injected -* -* @return -* Returns zero on correct loading, distinct from zero otherwise -* -*/ -SERIAL_API int serial_impl_load(serial_impl impl, const char * path, const char * name); - -/** -* @brief -* Convert a value @v to a serialized string using serial implementation @impl -* -* @param[in] impl -* Reference to the serial implementation will be used to serialize value @v -* -* @param[in] v -* Reference to the value is going to be serialized -* -* @param[out] size -* Size in bytes of the return buffer -* -* @param[in] allocator -* Allocator to be used serialize @v -* -* @return -* String with the value serialized on correct serialization, null otherwise -* -*/ -SERIAL_API char * serial_impl_serialize(serial_impl impl, value v, size_t * size, memory_allocator allocator); - -/** -* @brief -* Convert a string @buffer to a deserialized value using serial implementation @impl -* -* @param[in] impl -* Reference to the serial implementation will be used to deserialize string @buffer -* -* @param[in] buffer -* Reference to the string is going to be deserialized -* -* @param[in] size -* Size in bytes of the string @buffer -* -* @param[in] allocator -* Allocator to be used deserialize @buffer -* -* @return -* Pointer to value deserialized on correct serialization, null otherwise -* -*/ -SERIAL_API value serial_impl_deserialize(serial_impl impl, const char * buffer, size_t size, memory_allocator allocator); - -/** -* @brief -* Unload serial implementation @impl -* -* @param[in] impl -* Pointer to the serial implementation to be unloaded -* -* @return -* Returns zero on correct unloading, distinct from zero otherwise -* -*/ -SERIAL_API int serial_impl_unload(serial_impl impl); - -/** -* @brief -* Destroy serial implementation -* -* @param[in] impl -* Pointer to the serial implementation to be destroyed -* -* @return -* Returns zero on correct destruction, distinct from zero otherwise -* -*/ -SERIAL_API int serial_impl_destroy(serial_impl impl); - -#ifdef __cplusplus -} -#endif - -#endif /* SERIAL_IMPL_H */ diff --git a/source/serial/include/serial/serial_interface.h b/source/serial/include/serial/serial_interface.h index 02014ffc5e..f83a0af8e8 100644 --- a/source/serial/include/serial/serial_interface.h +++ b/source/serial/include/serial/serial_interface.h @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -13,8 +13,7 @@ #include -#include -#include +#include #include @@ -24,37 +23,21 @@ extern "C" { #endif -/* -- Forward Declarations -- */ - -struct serial_interface_type; - -/* -- Type Definitions -- */ - -typedef const char * (*serial_interface_extension)(void); - -typedef serial_impl_handle (*serial_interface_initialize)(memory_allocator, serial_host); - -typedef char * (*serial_interface_serialize)(serial_impl_handle, value, size_t *); - -typedef value (*serial_interface_deserialize)(serial_impl_handle, const char *, size_t); - -typedef int (*serial_interface_destroy)(serial_impl_handle); - -typedef struct serial_interface_type * serial_interface; - -typedef serial_interface (*serial_interface_singleton)(void); - /* -- Member Data -- */ struct serial_interface_type { - serial_interface_extension extension; - serial_interface_initialize initialize; - serial_interface_serialize serialize; - serial_interface_deserialize deserialize; - serial_interface_destroy destroy; + const char *(*extension)(void); + serial_handle (*initialize)(memory_allocator); + char *(*serialize)(serial_handle, value, size_t *); + value (*deserialize)(serial_handle, const char *, size_t); + int (*destroy)(serial_handle); }; +/* -- Type Definitions -- */ + +typedef struct serial_interface_type *serial_interface; + #ifdef __cplusplus } #endif diff --git a/source/serial/include/serial/serial_singleton.h b/source/serial/include/serial/serial_singleton.h deleted file mode 100644 index a2cddc7f89..0000000000 --- a/source/serial/include/serial/serial_singleton.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A cross-platform library for managing multiple serialization and deserialization formats. - * - */ - -#ifndef SERIAL_SINGLETON_H -#define SERIAL_SINGLETON_H 1 - -/* -- Headers -- */ - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Forward Declarations -- */ - -struct serial_singleton_type; - -/* -- Type Definitions -- */ - -typedef struct serial_singleton_type * serial_singleton; - -/* -- Methods -- */ - -/** -* @brief -* Initialize serial singleton -* -* @return -* Returns zero on correct serial singleton initialization, distinct from zero otherwise -* -*/ -SERIAL_API int serial_singleton_initialize(); - -/** -* @brief -* Register serial into serials map -* -* @param[in] s -* Pointer to serial -* -* @return -* Returns zero on correct serial singleton insertion, distinct from zero otherwise -* -*/ -SERIAL_API int serial_singleton_register(serial s); - -/** -* @brief -* Retrieve serial from serials map by @name -* -* @param[in] name -* Index which references the serial to be retrieved -* -* @return -* Returns pointer to serial if exists, null otherwise -* -*/ -SERIAL_API serial serial_singleton_get(const char * name); - -/** -* @brief -* Retrieve serial library path where serials are located -* -* @return -* Returns constant string representing serials library path -* -*/ -SERIAL_API const char * serial_singleton_path(void); - -/** -* @brief -* Remove serial from serials map -* -* @param[in] s -* Pointer to serial -* -* @return -* Returns zero on correct serial singleton removing, distinct from zero otherwise -* -*/ -SERIAL_API int serial_singleton_clear(serial s); - -/** -* @brief -* Destroy serial singleton -* -*/ -SERIAL_API void serial_singleton_destroy(void); - -#ifdef __cplusplus -} -#endif - -#endif /* SERIAL_SINGLETON_H */ diff --git a/source/serial/source/serial.c b/source/serial/source/serial.c index 2338a4c238..7a7e71faba 100644 --- a/source/serial/source/serial.c +++ b/source/serial/source/serial.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -11,225 +11,146 @@ #include #include -#include -#include +#include + +#include #include -#include +/* -- Definitions -- */ + +#define SERIAL_MANAGER_NAME "serial" +#define SERIAL_LIBRARY_PATH "SERIAL_LIBRARY_PATH" +#define SERIAL_LIBRARY_DEFAULT_PATH "serials" + +/* -- Macros -- */ + +#define serial_iface(s) \ + plugin_iface_type(s, serial_interface) /* -- Member Data -- */ -struct serial_type -{ - char * name; - serial_impl impl; -}; +static plugin_manager_declare(serial_manager); /* -- Methods -- */ -int serial_initialize() +int serial_initialize(void) { - if (serial_singleton_initialize() != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton initialization"); - - return 1; - } + return plugin_manager_initialize( + &serial_manager, + SERIAL_MANAGER_NAME, + SERIAL_LIBRARY_PATH, +#if defined(SERIAL_LIBRARY_INSTALL_PATH) + SERIAL_LIBRARY_INSTALL_PATH, +#else + SERIAL_LIBRARY_DEFAULT_PATH, +#endif /* SERIAL_LIBRARY_INSTALL_PATH */ + NULL, + NULL); +} - return 0; +serial serial_create(const char *name) +{ + return plugin_manager_create(&serial_manager, name, NULL, NULL); } -serial serial_create(const char * name) +const char *serial_extension(serial s) { - serial s; + return serial_iface(s)->extension(); +} - size_t name_length; +const char *serial_name(serial s) +{ + return plugin_name(s); +} - if (name == NULL) +char *serial_serialize(serial s, value v, size_t *size, memory_allocator allocator) +{ + if (s == NULL || v == NULL || size == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial name"); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serialization arguments"); return NULL; } - s = serial_singleton_get(name); - - if (s != NULL) - { - log_write("metacall", LOG_LEVEL_DEBUG, "Serial <%p> already exists", (void *)s); - - return s; - } - - name_length = strlen(name); + serial_handle handle = serial_iface(s)->initialize(allocator); - if (name_length == 0) + if (handle == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial name length"); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); return NULL; } - s = malloc(sizeof(struct serial_type)); + char *buffer = serial_iface(s)->serialize(handle, v, size); - if (s == NULL) + if (buffer == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial allocation"); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle serialization"); } - s->name = malloc(sizeof(char) * (name_length + 1)); - - if (s->name == NULL) + if (serial_iface(s)->destroy(handle) != 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial name allocation"); - - free(s); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); } - strncpy(s->name, name, name_length); - - s->name[name_length] = '\0'; - - s->impl = serial_impl_create(); + return buffer; +} - if (s->impl == NULL) +value serial_deserialize(serial s, const char *buffer, size_t size, memory_allocator allocator) +{ + if (s == NULL || buffer == NULL || size == 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation creation"); - - free(s->name); - - free(s); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid deserialization arguments"); return NULL; } - if (serial_impl_load(s->impl, serial_singleton_path(), s->name) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation loading"); - - serial_impl_destroy(s->impl); - - free(s->name); + serial_handle handle = serial_iface(s)->initialize(allocator); - free(s); - - return NULL; - } - - if (serial_singleton_register(s) != 0) + if (handle == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton insert"); - - serial_impl_destroy(s->impl); - - free(s->name); - - free(s); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); return NULL; } - return s; -} + value v = serial_iface(s)->deserialize(handle, buffer, size); -const char * serial_extension(serial s) -{ - return serial_impl_extension(s->impl); -} - -const char * serial_name(serial s) -{ - return s->name; -} - -char * serial_serialize(serial s, value v, size_t * size, memory_allocator allocator) -{ - if (s == NULL || v == NULL || size == NULL) + if (v == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serialization arguments"); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle deserialization"); } - return serial_impl_serialize(s->impl, v, size, allocator); -} - -value serial_deserialize(serial s, const char * buffer, size_t size, memory_allocator allocator) -{ - if (s == NULL || buffer == NULL || size == 0) + if (serial_iface(s)->destroy(handle) != 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid deserialization arguments"); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); } - return serial_impl_deserialize(s->impl, buffer, size, allocator); + return v; } int serial_clear(serial s) { - if (s != NULL) - { - int result = 0; - - if (serial_singleton_clear(s) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton clear"); - - result = 1; - } - - if (s->name != NULL) - { - free(s->name); - } - - if (s->impl != NULL) - { - if (serial_impl_unload(s->impl) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation unloading"); - - result = 1; - } - - if (serial_impl_destroy(s->impl) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation destruction"); - - result = 1; - } - } - - free(s); - - return result; - } - - return 0; + return plugin_manager_clear(&serial_manager, s); } -void serial_destroy() +void serial_destroy(void) { - serial_singleton_destroy(); + plugin_manager_destroy(&serial_manager); } -const char * serial_print_info() +const char *serial_print_info(void) { static const char serial_info[] = "Serial Library " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef SERIAL_STATIC_DEFINE - "Compiled as static library type" - #else - "Compiled as shared library type" - #endif +#ifdef SERIAL_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif "\n"; diff --git a/source/serial/source/serial_impl.c b/source/serial/source/serial_impl.c deleted file mode 100644 index 78868a3db5..0000000000 --- a/source/serial/source/serial_impl.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A cross-platform library for managing multiple serialization and deserialization formats. - * - */ - -/* -- Headers -- */ - -#include -#include -#include - -#include - -#include - -#include - -/* -- Definitions -- */ - -#define SERIAL_DYNLINK_NAME_SIZE 0x40 -#define SERIAL_DYNLINK_SUFFIX "_serial" - -/* -- Member Data -- */ - -struct serial_impl_type -{ - dynlink handle; - serial_interface iface; - serial_host host; -}; - -/* -- Private Methods -- */ - -static dynlink serial_impl_load_dynlink(const char * path, const char * name); - -static int serial_impl_load_symbol(dynlink handle, const char * name, dynlink_symbol_addr * singleton_addr_ptr); - -/* -- Methods -- */ - -dynlink serial_impl_load_dynlink(const char * path, const char * name) -{ - #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) - const char serial_dynlink_suffix[] = SERIAL_DYNLINK_SUFFIX "d"; - #else - const char serial_dynlink_suffix[] = SERIAL_DYNLINK_SUFFIX; - #endif - - #define SERIAL_DYNLINK_NAME_FULL_SIZE \ - (sizeof(serial_dynlink_suffix) + SERIAL_DYNLINK_NAME_SIZE) - - char serial_dynlink_name[SERIAL_DYNLINK_NAME_FULL_SIZE]; - - strncpy(serial_dynlink_name, name, SERIAL_DYNLINK_NAME_FULL_SIZE); - - strncat(serial_dynlink_name, serial_dynlink_suffix, - SERIAL_DYNLINK_NAME_FULL_SIZE - strnlen(serial_dynlink_name, SERIAL_DYNLINK_NAME_FULL_SIZE - 1) - 1); - - #undef SERIAL_DYNLINK_NAME_FULL_SIZE - - log_write("metacall", LOG_LEVEL_DEBUG, "Loading serial plugin: %s", serial_dynlink_name); - - return dynlink_load(path, serial_dynlink_name, DYNLINK_FLAGS_BIND_LAZY | DYNLINK_FLAGS_BIND_GLOBAL); -} - -int serial_impl_load_symbol(dynlink handle, const char * name, dynlink_symbol_addr * singleton_addr_ptr) -{ - const char serial_dynlink_symbol_prefix[] = DYNLINK_SYMBOL_STR(""); - const char serial_dynlink_symbol_suffix[] = "_serial_impl_interface_singleton"; - - #define SERIAL_DYNLINK_SYMBOL_SIZE \ - (sizeof(serial_dynlink_symbol_prefix) + SERIAL_DYNLINK_NAME_SIZE + sizeof(serial_dynlink_symbol_suffix)) - - char serial_dynlink_symbol[SERIAL_DYNLINK_SYMBOL_SIZE]; - - strncpy(serial_dynlink_symbol, serial_dynlink_symbol_prefix, SERIAL_DYNLINK_SYMBOL_SIZE); - - strncat(serial_dynlink_symbol, name, - SERIAL_DYNLINK_SYMBOL_SIZE - strnlen(serial_dynlink_symbol, SERIAL_DYNLINK_SYMBOL_SIZE - 1) - 1); - - strncat(serial_dynlink_symbol, serial_dynlink_symbol_suffix, - SERIAL_DYNLINK_SYMBOL_SIZE - strnlen(serial_dynlink_symbol, SERIAL_DYNLINK_SYMBOL_SIZE - 1) - 1); - - #undef SERIAL_DYNLINK_SYMBOL_SIZE - - log_write("metacall", LOG_LEVEL_DEBUG, "Loading serial symbol: %s", serial_dynlink_symbol); - - return dynlink_symbol(handle, serial_dynlink_symbol, singleton_addr_ptr); -} - -serial_impl serial_impl_create() -{ - serial_impl impl = malloc(sizeof(struct serial_impl_type)); - - if (impl == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation allocation"); - - return NULL; - } - - impl->host = (serial_host)malloc(sizeof(struct serial_host_type)); - - if (impl->host == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation host allocation"); - - free(impl); - - return NULL; - } - - impl->host->log = log_instance(); - - impl->handle = NULL; - impl->iface = NULL; - - return impl; -} - -const char * serial_impl_extension(serial_impl impl) -{ - return impl->iface->extension(); -} - -int serial_impl_load(serial_impl impl, const char * path, const char * name) -{ - dynlink_symbol_addr singleton_addr; - - serial_interface_singleton iface_singleton; - - impl->handle = serial_impl_load_dynlink(path, name); - - if (impl->handle == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial dynlink loading"); - - return 1; - } - - if (serial_impl_load_symbol(impl->handle, name, &singleton_addr) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial dynlink symbol loading"); - - dynlink_unload(impl->handle); - - impl->handle = NULL; - - return 1; - } - - iface_singleton = (serial_interface_singleton)DYNLINK_SYMBOL_GET(singleton_addr); - - if (iface_singleton == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial interface singleton access"); - - dynlink_unload(impl->handle); - - impl->handle = NULL; - - return 1; - } - - impl->iface = iface_singleton(); - - return 0; -} - -char * serial_impl_serialize(serial_impl impl, value v, size_t * size, memory_allocator allocator) -{ - serial_impl_handle handle = impl->iface->initialize(allocator, impl->host); - - char * buffer; - - if (handle == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); - - return NULL; - } - - buffer = impl->iface->serialize(handle, v, size); - - if (buffer == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle serialization"); - } - - if (impl->iface->destroy(handle) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); - } - - return buffer; -} - -value serial_impl_deserialize(serial_impl impl, const char * buffer, size_t size, memory_allocator allocator) -{ - serial_impl_handle handle = impl->iface->initialize(allocator, impl->host); - - value v; - - if (handle == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); - - return NULL; - } - - v = impl->iface->deserialize(handle, buffer, size); - - if (v == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle deserialization"); - } - - if (impl->iface->destroy(handle) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); - } - - return v; -} - -int serial_impl_unload(serial_impl impl) -{ - if (impl != NULL) - { - impl->iface = NULL; - - if (impl->handle != NULL) - { - dynlink_unload(impl->handle); - - impl->handle = NULL; - } - } - - return 0; -} - -int serial_impl_destroy(serial_impl impl) -{ - if (impl != NULL) - { - if (impl->handle != NULL) - { - dynlink_unload(impl->handle); - } - - if (impl->host != NULL) - { - free(impl->host); - } - - free(impl); - } - - return 0; -} diff --git a/source/serial/source/serial_singleton.c b/source/serial/source/serial_singleton.c deleted file mode 100644 index 6802a9d1c3..0000000000 --- a/source/serial/source/serial_singleton.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * A cross-platform library for managing multiple serialization and deserialization formats. - * - */ - -/* -- Headers -- */ - -#include - -#include - -#include - -#include - -#include - -/* -- Definitions -- */ - -#define SERIAL_LIBRARY_PATH "SERIAL_LIBRARY_PATH" -#define SERIAL_LIBRARY_DEFAULT_PATH "serials" - -/* -- Member Data -- */ - -struct serial_singleton_type -{ - set serials; - char * library_path; -}; - -/* -- Private Methods -- */ - -/** -* @brief -* Wrapper of serial singleton instance -* -* @return -* Pointer to serial singleton instance -* -*/ -static serial_singleton serial_singleton_instance(void); - -/** -* @brief -* Serial singleton destroy callback iterator -* -* @param[in] s -* Pointer to serials set -* -* @param[in] key -* Pointer to current serial key -* -* @param[in] val -* Pointer to current serial instance -* -* @param[in] args -* Pointer to user defined callback arguments -* -* @return -* Returns zero to continue iteration, distinct from zero otherwise -* -*/ -static int serial_singleton_destroy_cb_iterate(set s, set_key key, set_value val, set_cb_iterate_args args); - -/* -- Methods -- */ - -serial_singleton serial_singleton_instance() -{ - static struct serial_singleton_type singleton = - { - NULL, - NULL - }; - - return &singleton; -} - -int serial_singleton_initialize() -{ - serial_singleton singleton = serial_singleton_instance(); - - if (singleton->serials == NULL) - { - singleton->serials = set_create(&hash_callback_str, &comparable_callback_str); - - if (singleton->serials == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton set initialization"); - - serial_singleton_destroy(); - - return 1; - } - } - - if (singleton->library_path == NULL) - { - static const char serial_library_path[] = SERIAL_LIBRARY_PATH; - - #if defined(SERIAL_LIBRARY_INSTALL_PATH) - static const char serial_library_default_path[] = SERIAL_LIBRARY_INSTALL_PATH; - #else - static const char serial_library_default_path[] = SERIAL_LIBRARY_DEFAULT_PATH; - #endif /* SERIAL_LIBRARY_INSTALL_PATH */ - - singleton->library_path = environment_variable_path_create(serial_library_path, serial_library_default_path); - - if (singleton->library_path == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton library path initialization"); - - serial_singleton_destroy(); - - return 1; - } - } - - return 0; -} - -int serial_singleton_register(serial s) -{ - serial_singleton singleton = serial_singleton_instance(); - - const char * name = serial_name(s); - - if (set_get(singleton->serials, (set_key)name) != NULL) - { - return 1; - } - - return set_insert(singleton->serials, (set_key)name, s); -} - -serial serial_singleton_get(const char * name) -{ - serial_singleton singleton = serial_singleton_instance(); - - return set_get(singleton->serials, (set_key)name); -} - -const char * serial_singleton_path() -{ - serial_singleton singleton = serial_singleton_instance(); - - return singleton->library_path; -} - -int serial_singleton_clear(serial s) -{ - serial_singleton singleton = serial_singleton_instance(); - - const char * name = serial_name(s); - - if (set_get(singleton->serials, (set_key)name) == NULL) - { - return 0; - } - - if (set_remove(singleton->serials, (const set_key)name) == NULL) - { - return 1; - } - - return 0; -} - -int serial_singleton_destroy_cb_iterate(set s, set_key key, set_value val, set_cb_iterate_args args) -{ - (void)s; - (void)key; - (void)args; - - if (val != NULL) - { - serial_clear((serial)val); - } - - return 0; -} - -void serial_singleton_destroy() -{ - serial_singleton singleton = serial_singleton_instance(); - - if (singleton->serials != NULL) - { - set_iterate(singleton->serials, &serial_singleton_destroy_cb_iterate, NULL); - - set_destroy(singleton->serials); - - singleton->serials = NULL; - } - - if (singleton->library_path != NULL) - { - environment_variable_path_destroy(singleton->library_path); - - singleton->library_path = NULL; - } -} diff --git a/source/serials/CMakeLists.txt b/source/serials/CMakeLists.txt index 5d2fb8807e..5fc2711dc5 100644 --- a/source/serials/CMakeLists.txt +++ b/source/serials/CMakeLists.txt @@ -7,21 +7,6 @@ endif() option(OPTION_BUILD_SERIALS_METACALL "MetaCall Native Format library serial." ON) option(OPTION_BUILD_SERIALS_RAPID_JSON "RapidJSON library serial." ON) -# Define serial modules -set(SERIAL_MODULE_NAMES - version - preprocessor - environment - format - log - memory - portability - adt - reflect - dynlink - serial -) - # Serial packages add_subdirectory(metacall_serial) # MetaCall Native Format library add_subdirectory(rapid_json_serial) # RapidJSON library diff --git a/source/serials/metacall_serial/CMakeLists.txt b/source/serials/metacall_serial/CMakeLists.txt index d833e9abe1..5a11cbc8e5 100644 --- a/source/serials/metacall_serial/CMakeLists.txt +++ b/source/serials/metacall_serial/CMakeLists.txt @@ -15,7 +15,6 @@ message(STATUS "Serial ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -64,54 +63,18 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Create library # -include(Distributable) - -# Create unity build library -set(metacall_serial_unity_build "${CMAKE_CURRENT_BINARY_DIR}/${target}_unity_build.c") - -distributable_generate(${target} ${metacall_serial_unity_build} - ${SERIAL_MODULE_NAMES} -) - # Build library add_library(${target} MODULE - ${metacall_serial_unity_build} ${sources} ${headers} ) -# Add target dependencies -add_dependencies(${target} - ${SERIAL_MODULE_NAMES} -) - # Create namespaced alias add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() - # Create API export header generate_export_header(${target} EXPORT_FILE_NAME ${export_file} @@ -139,6 +102,8 @@ target_include_directories(${target} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include + $ # MetaCall includes + PUBLIC ${DEFAULT_INCLUDE_DIRECTORIES} @@ -154,6 +119,7 @@ target_include_directories(${target} target_link_libraries(${target} PRIVATE + ${META_PROJECT_NAME}::metacall # MetaCall library PUBLIC ${DEFAULT_LIBRARIES} @@ -192,7 +158,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h index 7682088634..d6aece4e79 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,6 @@ #include -#include - #ifdef __cplusplus extern "C" { #endif @@ -45,8 +43,6 @@ extern "C" { */ METACALL_SERIAL_API serial_interface metacall_serial_impl_interface_singleton(void); -DYNLINK_SYMBOL_EXPORT(metacall_serial_impl_interface_singleton); - /** * @brief * Provide the module information @@ -55,9 +51,7 @@ DYNLINK_SYMBOL_EXPORT(metacall_serial_impl_interface_singleton); * Static string containing module information * */ -METACALL_SERIAL_API const char * metacall_serial_print_info(void); - -DYNLINK_SYMBOL_EXPORT(metacall_serial_print_info); +METACALL_SERIAL_API const char *metacall_serial_print_info(void); #ifdef __cplusplus } diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h index 8dcb9390ce..8dd25ce64a 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -29,7 +29,7 @@ extern "C" { * Returns constant string representing serial extension * */ -METACALL_SERIAL_API const char * metacall_serial_impl_extension(void); +METACALL_SERIAL_API const char *metacall_serial_impl_extension(void); /** * @brief @@ -39,7 +39,7 @@ METACALL_SERIAL_API const char * metacall_serial_impl_extension(void); * Returns pointer to serial document implementation on success, null pointer otherwise * */ -METACALL_SERIAL_API serial_impl_handle metacall_serial_impl_initialize(memory_allocator allocator, serial_host host); +METACALL_SERIAL_API serial_handle metacall_serial_impl_initialize(memory_allocator allocator); /** * @brief @@ -58,7 +58,7 @@ METACALL_SERIAL_API serial_impl_handle metacall_serial_impl_initialize(memory_al * String with the value serialized on correct serialization, null otherwise * */ -METACALL_SERIAL_API char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size); +METACALL_SERIAL_API char *metacall_serial_impl_serialize(serial_handle handle, value v, size_t *size); /** * @brief @@ -77,7 +77,7 @@ METACALL_SERIAL_API char * metacall_serial_impl_serialize(serial_impl_handle han * Pointer to value deserialized on correct serialization, null otherwise * */ -METACALL_SERIAL_API value metacall_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size); +METACALL_SERIAL_API value metacall_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size); /** * @brief @@ -87,7 +87,7 @@ METACALL_SERIAL_API value metacall_serial_impl_deserialize(serial_impl_handle ha * Returns zero on correct destruction, distinct from zero otherwise * */ -METACALL_SERIAL_API int metacall_serial_impl_destroy(serial_impl_handle handle); +METACALL_SERIAL_API int metacall_serial_impl_destroy(serial_handle handle); #ifdef __cplusplus } diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h index db4418d5e0..f6324b4412 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h index dcbfa2c980..21c0eb0601 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ typedef void (*metacall_serialize_impl_ptr)(value, char *, size_t, const char *, * @return * Print format assigned to type id @id */ -METACALL_SERIAL_API const char * metacall_serial_impl_serialize_format(type_id id); +METACALL_SERIAL_API const char *metacall_serial_impl_serialize_format(type_id id); /** * @brief diff --git a/source/serials/metacall_serial/source/metacall_serial.c b/source/serials/metacall_serial/source/metacall_serial.c index 05a8641a79..70b16af396 100644 --- a/source/serials/metacall_serial/source/metacall_serial.c +++ b/source/serials/metacall_serial/source/metacall_serial.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -15,10 +15,9 @@ /* -- Methods -- */ -serial_interface metacall_serial_impl_interface_singleton() +serial_interface metacall_serial_impl_interface_singleton(void) { - static struct serial_interface_type interface_instance_metacall = - { + static struct serial_interface_type interface_instance_metacall = { &metacall_serial_impl_extension, &metacall_serial_impl_initialize, &metacall_serial_impl_serialize, @@ -29,17 +28,17 @@ serial_interface metacall_serial_impl_interface_singleton() return &interface_instance_metacall; } -const char * metacall_serial_print_info() +const char *metacall_serial_print_info(void) { static const char metacall_serial_info[] = "MetaCall Native Format Serial Plugin " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef METACALL_SERIAL_STATIC_DEFINE - "Compiled as static library type\n" - #else - "Compiled as shared library type\n" - #endif +#ifdef METACALL_SERIAL_STATIC_DEFINE + "Compiled as static library type\n" +#else + "Compiled as shared library type\n" +#endif "\n"; diff --git a/source/serials/metacall_serial/source/metacall_serial_impl.c b/source/serials/metacall_serial/source/metacall_serial_impl.c index f35c1a749c..8e48d1e7d5 100644 --- a/source/serials/metacall_serial/source/metacall_serial_impl.c +++ b/source/serials/metacall_serial/source/metacall_serial_impl.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -9,51 +9,49 @@ /* -- Headers -- */ #include -#include #include +#include #include /* -- Private Methods -- */ -static void metacall_serial_impl_serialize_value(value v, char * dest, size_t size, size_t * length); +static void metacall_serial_impl_serialize_value(value v, char *dest, size_t size, size_t *length); -static value metacall_serial_impl_deserialize_value(const char * buffer, size_t size); +static value metacall_serial_impl_deserialize_value(const char *buffer, size_t size); /* -- Methods -- */ -const char * metacall_serial_impl_extension() +const char *metacall_serial_impl_extension(void) { static const char extension[] = "meta"; return extension; } -serial_impl_handle metacall_serial_impl_initialize(memory_allocator allocator, serial_host host) +serial_handle metacall_serial_impl_initialize(memory_allocator allocator) { - log_copy(host->log); - return allocator; } -void metacall_serial_impl_serialize_value(value v, char * dest, size_t size, size_t * length) +void metacall_serial_impl_serialize_value(value v, char *dest, size_t size, size_t *length) { type_id id = value_type_id(v); - const char * format = metacall_serial_impl_serialize_format(id); + const char *format = metacall_serial_impl_serialize_format(id); metacall_serialize_impl_ptr serialize_ptr = metacall_serial_impl_serialize_func(id); serialize_ptr(v, dest, size, format, length); } -char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size) +char *metacall_serial_impl_serialize(serial_handle handle, value v, size_t *size) { memory_allocator allocator; size_t length, buffer_size; - char * buffer; + char *buffer; if (handle == NULL || v == NULL || size == NULL) { @@ -91,8 +89,9 @@ char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t if (length + 1 != buffer_size) { log_write("metacall", LOG_LEVEL_ERROR, "Serialization invalid length + 1 != buffer " - "(%" PRIuS " != %" PRIuS ") in MetaCall Native Format implementation", length + 1, buffer_size); - + "(%" PRIuS " != %" PRIuS ") in MetaCall Native Format implementation", + length + 1, buffer_size); + memory_allocator_deallocate(allocator, buffer); *size = 0; @@ -105,7 +104,7 @@ char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t return buffer; } -value metacall_serial_impl_deserialize_value(const char * buffer, size_t size) +value metacall_serial_impl_deserialize_value(const char *buffer, size_t size) { value v = NULL; @@ -126,7 +125,7 @@ value metacall_serial_impl_deserialize_value(const char * buffer, size_t size) return NULL; } -value metacall_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size) +value metacall_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size) { if (handle == NULL || buffer == NULL || size == 0) { @@ -138,7 +137,7 @@ value metacall_serial_impl_deserialize(serial_impl_handle handle, const char * b return metacall_serial_impl_deserialize_value(buffer, size); } -int metacall_serial_impl_destroy(serial_impl_handle handle) +int metacall_serial_impl_destroy(serial_handle handle) { (void)handle; diff --git a/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c b/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c index dd70503ff5..3f415ade35 100644 --- a/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c +++ b/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,43 +24,42 @@ #include -#include #include +#include /* -- Private Methods -- */ -static int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_bool(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_char(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_char(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_short(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_short(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_int(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_int(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_long(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_long(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_float(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_float(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_double(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_double(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_string(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_string(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_buffer(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_buffer(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_array(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_array(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_map(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_map(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_ptr(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_ptr(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_null(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_null(value *v, const char *src, size_t length); /* -- Methods -- */ metacall_deserialize_impl_ptr metacall_serial_impl_deserialize_func(type_id id) { - static metacall_deserialize_impl_ptr deserialize_func[TYPE_SIZE] = - { + static metacall_deserialize_impl_ptr deserialize_func[TYPE_SIZE] = { &metacall_serial_impl_deserialize_bool, &metacall_serial_impl_deserialize_char, &metacall_serial_impl_deserialize_short, @@ -79,7 +78,7 @@ metacall_deserialize_impl_ptr metacall_serial_impl_deserialize_func(type_id id) return deserialize_func[id]; } -int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_bool(value *v, const char *src, size_t length) { static const char true_str[] = "true"; @@ -91,7 +90,7 @@ int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t le return (*v == NULL); } - + if (strncmp(src, false_str, length) == 0) { *v = value_create_bool((boolean)0L); @@ -102,7 +101,7 @@ int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t le return 1; } -int metacall_serial_impl_deserialize_char(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_char(value *v, const char *src, size_t length) { long l = 0; @@ -125,7 +124,7 @@ int metacall_serial_impl_deserialize_char(value * v, const char * src, size_t le return (*v == NULL); } -int metacall_serial_impl_deserialize_short(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_short(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -135,7 +134,7 @@ int metacall_serial_impl_deserialize_short(value * v, const char * src, size_t l return 1; } -int metacall_serial_impl_deserialize_int(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_int(value *v, const char *src, size_t length) { size_t iterator; @@ -157,13 +156,13 @@ int metacall_serial_impl_deserialize_int(value * v, const char * src, size_t len return (*v == NULL); } -int metacall_serial_impl_deserialize_long(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_long(value *v, const char *src, size_t length) { char buffer[24]; const size_t last = length - 1; - char * end = (char *)&src[last]; + char *end = (char *)&src[last]; size_t iterator; @@ -194,7 +193,7 @@ int metacall_serial_impl_deserialize_long(value * v, const char * src, size_t le return (*v == NULL); } -int metacall_serial_impl_deserialize_float(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_float(value *v, const char *src, size_t length) { size_t iterator, is_digit; @@ -228,7 +227,7 @@ int metacall_serial_impl_deserialize_float(value * v, const char * src, size_t l return (*v == NULL); } -int metacall_serial_impl_deserialize_double(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_double(value *v, const char *src, size_t length) { size_t iterator, is_digit; @@ -260,7 +259,7 @@ int metacall_serial_impl_deserialize_double(value * v, const char * src, size_t return (*v == NULL); } -int metacall_serial_impl_deserialize_string(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_string(value *v, const char *src, size_t length) { size_t iterator; @@ -277,7 +276,7 @@ int metacall_serial_impl_deserialize_string(value * v, const char * src, size_t return (*v == NULL); } -int metacall_serial_impl_deserialize_buffer(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_buffer(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -287,7 +286,7 @@ int metacall_serial_impl_deserialize_buffer(value * v, const char * src, size_t return 1; } -int metacall_serial_impl_deserialize_array(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_array(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -297,7 +296,7 @@ int metacall_serial_impl_deserialize_array(value * v, const char * src, size_t l return 1; } -int metacall_serial_impl_deserialize_map(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_map(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -307,7 +306,7 @@ int metacall_serial_impl_deserialize_map(value * v, const char * src, size_t len return 1; } -int metacall_serial_impl_deserialize_ptr(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_ptr(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -317,7 +316,7 @@ int metacall_serial_impl_deserialize_ptr(value * v, const char * src, size_t len return 1; } -int metacall_serial_impl_deserialize_null(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_null(value *v, const char *src, size_t length) { static const char null_str[] = "(null)"; diff --git a/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c b/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c index 3294038903..16dd1d2679 100644 --- a/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c +++ b/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,49 +31,56 @@ /* -- Definitions -- */ #if defined(_WIN32) && defined(_MSC_VER) -# define METACALL_SERIALIZE_VALUE_FORMAT_PTR "0x%p" + #define METACALL_SERIALIZE_VALUE_FORMAT_PTR "0x%p" #elif defined(__linux) || defined(__linux__) -# define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" + #define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" #else -# define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" + #define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" #endif /* -- Private Methods -- */ -static void metacall_serial_impl_serialize_bool(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_bool(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_char(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_char(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_short(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_short(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_int(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_int(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_long(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_long(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_float(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_float(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_double(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_double(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_string(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_string(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_buffer(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_array(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_map(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_map(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_ptr(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_ptr(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_future(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_future(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_function(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_function(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_null(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_null(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_class(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_object(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_exception(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_throwable(value v, char *dest, size_t size, const char *format, size_t *length); /* -- Definitions -- */ -static const char * metacall_serialize_format[] = -{ +static const char *metacall_serialize_format[] = { "%s", "%c", "%d", @@ -88,14 +95,17 @@ static const char * metacall_serialize_format[] = METACALL_SERIALIZE_VALUE_FORMAT_PTR, NULL, /* TODO: Future */ NULL, /* TODO: Function */ - "%s" + "%s", + NULL, /* TODO: Class */ + NULL, /* TODO: Object */ + NULL, /* TODO: Exception */ + NULL /* TODO: Throwable */ }; -static_assert((size_t) TYPE_SIZE == (size_t) sizeof(metacall_serialize_format) / sizeof(metacall_serialize_format[0]), +portability_static_assert((size_t)TYPE_SIZE == (size_t)sizeof(metacall_serialize_format) / sizeof(metacall_serialize_format[0]), "MetaCall serializer format does not match MetaCall type size"); -static metacall_serialize_impl_ptr serialize_func[] = -{ +static metacall_serialize_impl_ptr serialize_func[] = { &metacall_serial_impl_serialize_bool, &metacall_serial_impl_serialize_char, &metacall_serial_impl_serialize_short, @@ -110,15 +120,19 @@ static metacall_serialize_impl_ptr serialize_func[] = &metacall_serial_impl_serialize_ptr, &metacall_serial_impl_serialize_future, &metacall_serial_impl_serialize_function, - &metacall_serial_impl_serialize_null + &metacall_serial_impl_serialize_null, + &metacall_serial_impl_serialize_class, + &metacall_serial_impl_serialize_object, + &metacall_serial_impl_serialize_exception, + &metacall_serial_impl_serialize_throwable }; -static_assert((size_t) TYPE_SIZE == (size_t) sizeof(serialize_func) / sizeof(serialize_func[0]), +portability_static_assert((size_t)TYPE_SIZE == (size_t)sizeof(serialize_func) / sizeof(serialize_func[0]), "MetaCall serializer function does not match MetaCall type size"); /* -- Methods -- */ -const char * metacall_serial_impl_serialize_format(type_id id) +const char *metacall_serial_impl_serialize_format(type_id id) { return metacall_serialize_format[id]; } @@ -128,7 +142,7 @@ metacall_serialize_impl_ptr metacall_serial_impl_serialize_func(type_id id) return serialize_func[id]; } -void metacall_serial_impl_serialize_bool(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_bool(value v, char *dest, size_t size, const char *format, size_t *length) { static const char value_boolean_str[] = "false\0true"; @@ -142,42 +156,42 @@ void metacall_serial_impl_serialize_bool(value v, char * dest, size_t size, cons *length = snprintf(dest, size, format, (const char *)(&value_boolean_str[offset])); } -void metacall_serial_impl_serialize_char(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_char(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_char(v)); } -void metacall_serial_impl_serialize_short(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_short(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_short(v)); } -void metacall_serial_impl_serialize_int(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_int(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_int(v)); } -void metacall_serial_impl_serialize_long(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_long(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_long(v)); } -void metacall_serial_impl_serialize_float(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_float(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_float(v)); } -void metacall_serial_impl_serialize_double(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_double(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_double(v)); } -void metacall_serial_impl_serialize_string(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_string(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_string(v)); } -void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_buffer(value v, char *dest, size_t size, const char *format, size_t *length) { if (dest == NULL && size == 0) { @@ -186,7 +200,7 @@ void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, co } else { - const char * buffer = (const char *)value_to_buffer(v); + const char *buffer = (const char *)value_to_buffer(v); size_t dest_iterator, iterator, buffer_length = 0, buffer_size = value_type_size(v); @@ -199,22 +213,22 @@ void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, co } } -void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_array(value v, char *dest, size_t size, const char *format, size_t *length) { size_t iterator, array_value_length = 0, array_size = value_type_count(v); - value * array_value = value_to_array(v); + value *array_value = value_to_array(v); (void)format; - /* Calculate sum of all array values lenght */ + /* Calculate sum of all array values length */ for (iterator = 0; iterator < array_size; ++iterator) { value current_value = array_value[iterator]; type_id id = value_type_id(current_value); - const char * fmt = metacall_serial_impl_serialize_format(id); + const char *fmt = metacall_serial_impl_serialize_format(id); metacall_serialize_impl_ptr serialize_ptr = metacall_serial_impl_serialize_func(id); @@ -255,7 +269,7 @@ void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, con type_id id = value_type_id(current_value); - const char * fmt = metacall_serial_impl_serialize_format(id); + const char *fmt = metacall_serial_impl_serialize_format(id); metacall_serialize_impl_ptr serialize_ptr = metacall_serial_impl_serialize_func(id); @@ -284,19 +298,19 @@ void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, con } } -void metacall_serial_impl_serialize_map(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_map(value v, char *dest, size_t size, const char *format, size_t *length) { /* TODO: Implement map iteration like array stringify */ *length = snprintf(dest, size, format, value_to_map(v)); } -void metacall_serial_impl_serialize_ptr(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_ptr(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_ptr(v)); } -void metacall_serial_impl_serialize_future(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_future(value v, char *dest, size_t size, const char *format, size_t *length) { /* TODO: Implement future serialization */ (void)v; @@ -307,7 +321,7 @@ void metacall_serial_impl_serialize_future(value v, char * dest, size_t size, co *length = 0; } -void metacall_serial_impl_serialize_function(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_function(value v, char *dest, size_t size, const char *format, size_t *length) { /* TODO: Implement function serialization */ (void)v; @@ -318,7 +332,7 @@ void metacall_serial_impl_serialize_function(value v, char * dest, size_t size, *length = 0; } -void metacall_serial_impl_serialize_null(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_null(value v, char *dest, size_t size, const char *format, size_t *length) { static const char value_null_str[] = "(null)"; @@ -326,3 +340,47 @@ void metacall_serial_impl_serialize_null(value v, char * dest, size_t size, cons *length = snprintf(dest, size, format, value_null_str); } + +void metacall_serial_impl_serialize_class(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement class serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_object(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement object serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_exception(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement exception serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_throwable(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement throwable serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} diff --git a/source/serials/rapid_json_serial/CMakeLists.txt b/source/serials/rapid_json_serial/CMakeLists.txt index 156b3ea690..29fd739aa5 100644 --- a/source/serials/rapid_json_serial/CMakeLists.txt +++ b/source/serials/rapid_json_serial/CMakeLists.txt @@ -8,13 +8,13 @@ endif() # External dependencies # -find_package(RapidJSON 1.1.0) +find_package(RapidJSON) if(NOT RAPIDJSON_FOUND) include(InstallRapidJSON) if(NOT RAPIDJSON_FOUND) - message(STATUS "RapidJSON libraries not found") + message(SEND_ERROR "RapidJSON libraries not found") return() endif() @@ -33,7 +33,6 @@ message(STATUS "Serial ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -78,18 +77,8 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Create library # -include(Distributable) - -# Create unity build library -set(rapid_json_serial_unity_build "${CMAKE_CURRENT_BINARY_DIR}/${target}_unity_build.c") - -distributable_generate(${target} ${rapid_json_serial_unity_build} - ${SERIAL_MODULE_NAMES} -) - # Build library add_library(${target} MODULE - ${rapid_json_serial_unity_build} ${sources} ${headers} ) @@ -108,7 +97,6 @@ endif() # Add target dependencies add_dependencies(${target} - ${SERIAL_MODULE_NAMES} RapidJSON ) @@ -118,27 +106,6 @@ add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() - # Create API export header generate_export_header(${target} EXPORT_FILE_NAME ${export_file} @@ -167,6 +134,7 @@ target_include_directories(${target} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include + $ # MetaCall includes ${RAPIDJSON_INCLUDE_DIRS} # RapidJSON includes PUBLIC @@ -184,6 +152,7 @@ target_include_directories(${target} target_link_libraries(${target} PRIVATE + ${META_PROJECT_NAME}::metacall # MetaCall library PUBLIC ${DEFAULT_LIBRARIES} @@ -222,7 +191,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC diff --git a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h index 1856e35cc5..12bfd53432 100644 --- a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h +++ b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,6 @@ #include -#include - #ifdef __cplusplus extern "C" { #endif @@ -45,8 +43,6 @@ extern "C" { */ RAPID_JSON_SERIAL_API serial_interface rapid_json_serial_impl_interface_singleton(void); -DYNLINK_SYMBOL_EXPORT(rapid_json_serial_impl_interface_singleton); - /** * @brief * Provide the module information @@ -55,9 +51,7 @@ DYNLINK_SYMBOL_EXPORT(rapid_json_serial_impl_interface_singleton); * Static string containing module information * */ -RAPID_JSON_SERIAL_API const char * rapid_json_serial_print_info(void); - -DYNLINK_SYMBOL_EXPORT(rapid_json_serial_print_info); +RAPID_JSON_SERIAL_API const char *rapid_json_serial_print_info(void); #ifdef __cplusplus } diff --git a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h index ce97e00dc3..7d7fda83e7 100644 --- a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h +++ b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ extern "C" { * Returns constant string representing serial extension * */ -RAPID_JSON_SERIAL_API const char * rapid_json_serial_impl_extension(void); +RAPID_JSON_SERIAL_API const char *rapid_json_serial_impl_extension(void); /** * @brief @@ -51,7 +51,7 @@ RAPID_JSON_SERIAL_API const char * rapid_json_serial_impl_extension(void); * Returns pointer to serial document implementation on success, null pointer otherwise * */ -RAPID_JSON_SERIAL_API serial_impl_handle rapid_json_serial_impl_initialize(memory_allocator allocator, serial_host host); +RAPID_JSON_SERIAL_API serial_handle rapid_json_serial_impl_initialize(memory_allocator allocator); /** * @brief @@ -70,7 +70,7 @@ RAPID_JSON_SERIAL_API serial_impl_handle rapid_json_serial_impl_initialize(memor * String with the value serialized on correct serialization, null otherwise * */ -RAPID_JSON_SERIAL_API char * rapid_json_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size); +RAPID_JSON_SERIAL_API char *rapid_json_serial_impl_serialize(serial_handle handle, value v, size_t *size); /** * @brief @@ -89,7 +89,7 @@ RAPID_JSON_SERIAL_API char * rapid_json_serial_impl_serialize(serial_impl_handle * Pointer to value deserialized on correct serialization, null otherwise * */ -RAPID_JSON_SERIAL_API value rapid_json_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size); +RAPID_JSON_SERIAL_API value rapid_json_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size); /** * @brief @@ -99,7 +99,7 @@ RAPID_JSON_SERIAL_API value rapid_json_serial_impl_deserialize(serial_impl_handl * Returns zero on correct destruction, distinct from zero otherwise * */ -RAPID_JSON_SERIAL_API int rapid_json_serial_impl_destroy(serial_impl_handle handle); +RAPID_JSON_SERIAL_API int rapid_json_serial_impl_destroy(serial_handle handle); #ifdef __cplusplus } diff --git a/source/serials/rapid_json_serial/source/rapid_json_serial.c b/source/serials/rapid_json_serial/source/rapid_json_serial.c index 71edcd082c..e7671b55a0 100644 --- a/source/serials/rapid_json_serial/source/rapid_json_serial.c +++ b/source/serials/rapid_json_serial/source/rapid_json_serial.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -15,10 +15,9 @@ /* -- Methods -- */ -serial_interface rapid_json_serial_impl_interface_singleton() +serial_interface rapid_json_serial_impl_interface_singleton(void) { - static struct serial_interface_type interface_instance_rapid_json = - { + static struct serial_interface_type interface_instance_rapid_json = { &rapid_json_serial_impl_extension, &rapid_json_serial_impl_initialize, &rapid_json_serial_impl_serialize, @@ -29,17 +28,17 @@ serial_interface rapid_json_serial_impl_interface_singleton() return &interface_instance_rapid_json; } -const char * rapid_json_serial_print_info() +const char *rapid_json_serial_print_info(void) { static const char rapid_json_serial_info[] = "Rapid JSON Serial Plugin " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef RAPID_JSON_SERIAL_STATIC_DEFINE - "Compiled as static library type\n" - #else - "Compiled as shared library type\n" - #endif +#ifdef RAPID_JSON_SERIAL_STATIC_DEFINE + "Compiled as static library type\n" +#else + "Compiled as shared library type\n" +#endif "\n"; diff --git a/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp b/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp index db3ec66505..f46cb09b1b 100644 --- a/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp +++ b/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -12,146 +12,74 @@ #include +/* Disable warnings from RapidJSON */ +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wstrict-overflow" +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif + #include +#include #include #include -#include - -#include -/* -- Forward Declarations -- */ +/* Disable warnings from RapidJSON */ +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif -class MemoryAllocator; +#include /* -- Type Definitions -- */ -typedef rapidjson::GenericDocument, MemoryAllocator> RapidJSONSerialDocument; +typedef struct rapid_json_document_type +{ + rapidjson::Document impl; + memory_allocator allocator; -typedef rapidjson::GenericValue, MemoryAllocator> RapidJSONSerialValue; +} * rapid_json_document; /* -- Private Methods -- */ -static void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json_v, RapidJSONSerialDocument::AllocatorType & allocator); +static void rapid_json_serial_impl_serialize_value(value v, rapidjson::Value *json_v); -static char * rapid_json_serial_impl_document_stringify(RapidJSONSerialDocument * document, size_t * size); +static char *rapid_json_serial_impl_document_stringify(rapid_json_document document, size_t *size); -static value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v); +static value rapid_json_serial_impl_deserialize_value(const rapidjson::Value *v); /* -- Classes -- */ -/** -* @brief -* Memory allocator concept to decouple document allocation. -* Head like approach with each allocator has been implemented -* in order to support static like Free method used in RapidJSON -*/ -class MemoryAllocator -{ -public: - static const bool kNeedFree = true; - - MemoryAllocator() - { - this->allocator = memory_allocator_std(&malloc, &realloc, &free); - } - - MemoryAllocator(memory_allocator allocator) : allocator(allocator) - { - - } - - memory_allocator Impl() - { - return this->allocator; - } - - void * Malloc(size_t size) - { - void * data; - - if (size == 0) - { - return NULL; - } - - data = memory_allocator_allocate(allocator, sizeof(memory_allocator) + size); - - memcpy(data, &allocator, sizeof(memory_allocator)); - - return reinterpret_cast(reinterpret_cast(data) + sizeof(memory_allocator)); - } - - void * Realloc(void * data, size_t size, size_t new_size) - { - void * data_ptr, * new_data; - - (void)size; - - if (data == NULL) - { - return this->Malloc(new_size); - } - - data_ptr = reinterpret_cast(reinterpret_cast(data) - sizeof(memory_allocator)); - - new_data = memory_allocator_reallocate(allocator, data_ptr, size, new_size); - - return reinterpret_cast(reinterpret_cast(new_data) + sizeof(memory_allocator)); - } - - static void Free(void * data) - { - if (data != NULL) - { - void * data_ptr = reinterpret_cast(reinterpret_cast(data)-sizeof(memory_allocator)); - - memory_allocator allocator = *(static_cast(data_ptr)); - - memory_allocator_deallocate(allocator, data_ptr); - } - } - -private: - memory_allocator allocator; -}; +rapidjson::MemoryPoolAllocator<> rapid_json_allocator; /* -- Methods -- */ -const char * rapid_json_serial_impl_extension() +const char *rapid_json_serial_impl_extension() { static const char extension[] = "json"; return extension; } -serial_impl_handle rapid_json_serial_impl_initialize(memory_allocator allocator, serial_host host) +serial_handle rapid_json_serial_impl_initialize(memory_allocator allocator) { - MemoryAllocator * rapid_json_allocator; - - RapidJSONSerialDocument * document; - - log_copy(host->log); - - rapid_json_allocator = new MemoryAllocator(allocator); - - if (rapid_json_allocator == nullptr) - { - return NULL; - } - - document = new RapidJSONSerialDocument(rapid_json_allocator); + rapid_json_document document = new rapid_json_document_type(); if (document == nullptr) { - delete rapid_json_allocator; - return NULL; } - return (serial_impl_handle)document; + document->allocator = allocator; + + return (serial_handle)document; } -void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json_v, RapidJSONSerialDocument::AllocatorType & allocator) +void rapid_json_serial_impl_serialize_value(value v, rapidjson::Value *json_v) { type_id id = value_type_id(v); @@ -175,9 +103,7 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { short s = value_to_short(v); - int i = (int)s; - - json_v->SetInt(i); + json_v->SetInt((int)s); } else if (id == TYPE_INT) { @@ -189,9 +115,7 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { long l = value_to_long(v); - log_write("metacall", LOG_LEVEL_WARNING, "Casting long to int64_t (posible incompatible types) in RapidJSON implementation"); - - json_v->SetInt64(l); + json_v->SetInt64((int64_t)l); } else if (id == TYPE_FLOAT) { @@ -207,60 +131,60 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json } else if (id == TYPE_STRING) { - const char * str = value_to_string(v); + const char *str = value_to_string(v); size_t size = value_type_size(v); - rapidjson::SizeType length = size > 0 ? (rapidjson::SizeType)(size - 1) : 0; + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; json_v->SetString(str, length); } else if (id == TYPE_BUFFER) { - RapidJSONSerialValue & json_map = json_v->SetObject(); + rapidjson::Value &json_map = json_v->SetObject(); - RapidJSONSerialValue json_array(rapidjson::kArrayType); + rapidjson::Value json_array(rapidjson::kArrayType); - void * buffer = value_to_buffer(v); + void *buffer = value_to_buffer(v); size_t size = value_type_size(v); for (size_t iterator = 0; iterator < size; ++iterator) { - const char * data = (const char *)(((uintptr_t)buffer) + iterator); + const char *data = (const char *)(((uintptr_t)buffer) + iterator); - RapidJSONSerialValue json_inner_value; + rapidjson::Value json_inner_value; json_inner_value.SetUint((unsigned int)*data); - json_array.PushBack(json_inner_value, allocator); + json_array.PushBack(json_inner_value, rapid_json_allocator); } // Set data { - RapidJSONSerialValue json_member; + rapidjson::Value json_member; json_member.SetString("data"); - json_map.AddMember(json_member, json_array, allocator); + json_map.AddMember(json_member, json_array, rapid_json_allocator); } // Set length { - RapidJSONSerialValue json_member, json_inner_value; + rapidjson::Value json_member, json_inner_value; json_member.SetString("length"); json_inner_value.SetUint64((uint64_t)size); - json_map.AddMember(json_member, json_inner_value, allocator); + json_map.AddMember(json_member, json_inner_value, rapid_json_allocator); } } else if (id == TYPE_ARRAY) { - RapidJSONSerialValue & json_array = json_v->SetArray(); + rapidjson::Value &json_array = json_v->SetArray(); - value * value_array = value_to_array(v); + value *value_array = value_to_array(v); size_t array_size = value_type_count(v); @@ -268,18 +192,18 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { value current_value = value_array[iterator]; - RapidJSONSerialValue json_inner_value; + rapidjson::Value json_inner_value; - rapid_json_serial_impl_serialize_value(current_value, &json_inner_value, allocator); + rapid_json_serial_impl_serialize_value(current_value, &json_inner_value); - json_array.PushBack(json_inner_value, allocator); + json_array.PushBack(json_inner_value, rapid_json_allocator); } } else if (id == TYPE_MAP) { - RapidJSONSerialValue & json_map = json_v->SetObject(); + rapidjson::Value &json_map = json_v->SetObject(); - value * value_map = value_to_map(v); + value *value_map = value_to_map(v); size_t map_size = value_type_count(v); @@ -287,39 +211,115 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { value tupla = value_map[iterator]; - value * tupla_array = value_to_array(tupla); + value *tupla_array = value_to_array(tupla); - RapidJSONSerialValue json_member, json_inner_value; + rapidjson::Value json_member, json_inner_value; - rapid_json_serial_impl_serialize_value(tupla_array[0], &json_member, allocator); + rapid_json_serial_impl_serialize_value(tupla_array[0], &json_member); - rapid_json_serial_impl_serialize_value(tupla_array[1], &json_inner_value, allocator); + rapid_json_serial_impl_serialize_value(tupla_array[1], &json_inner_value); - json_map.AddMember(json_member, json_inner_value, allocator); + json_map.AddMember(json_member, json_inner_value, rapid_json_allocator); } } else if (id == TYPE_FUTURE) { /* TODO: Improve future serialization */ - const char str[] = "[Future]"; + static const char str[] = "[Future]"; size_t size = sizeof(str); - rapidjson::SizeType length = size > 0 ? (rapidjson::SizeType)(size - 1) : 0; + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; json_v->SetString(str, length); } else if (id == TYPE_FUNCTION) { /* TODO: Improve function serialization */ - const char str[] = "[Function]"; + static const char str[] = "[Function]"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; + + json_v->SetString(str, length); + } + else if (id == TYPE_CLASS) + { + /* TODO: Improve class serialization */ + static const char str[] = "[Class]"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; + + json_v->SetString(str, length); + } + else if (id == TYPE_OBJECT) + { + /* TODO: Improve object serialization */ + static const char str[] = "[Object]"; size_t size = sizeof(str); - rapidjson::SizeType length = size > 0 ? (rapidjson::SizeType)(size - 1) : 0; + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; json_v->SetString(str, length); } + else if (id == TYPE_EXCEPTION) + { + rapidjson::Value &json_map = json_v->SetObject(); + + exception ex = value_to_exception(v); + + rapidjson::Value message_member, message_value; + static const char message_str[] = "message"; + + message_member.SetString(message_str, static_cast(sizeof(message_str) - 1)); + message_value.SetString(exception_message(ex), static_cast(strlen(exception_message(ex)))); + json_map.AddMember(message_member, message_value, rapid_json_allocator); + + rapidjson::Value label_member, label_value; + static const char label_str[] = "label"; + + label_member.SetString(label_str, static_cast(sizeof(label_str) - 1)); + label_value.SetString(exception_label(ex), static_cast(strlen(exception_label(ex)))); + json_map.AddMember(label_member, label_value, rapid_json_allocator); + + rapidjson::Value code_member, code_value; + static const char code_str[] = "code"; + + code_member.SetString(code_str, static_cast(sizeof(code_str) - 1)); + code_value.SetInt64(exception_error_code(ex)); + json_map.AddMember(code_member, code_value, rapid_json_allocator); + + rapidjson::Value stacktrace_member, stacktrace_value; + static const char stacktrace_str[] = "stacktrace"; + + stacktrace_member.SetString(stacktrace_str, static_cast(sizeof(stacktrace_str) - 1)); + stacktrace_value.SetString(exception_stacktrace(ex), static_cast(strlen(exception_stacktrace(ex)))); + json_map.AddMember(stacktrace_member, stacktrace_value, rapid_json_allocator); + } + else if (id == TYPE_THROWABLE) + { + rapidjson::Value &json_map = json_v->SetObject(); + + throwable th = value_to_throwable(v); + + static const char str[] = "ExceptionThrown"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = static_cast(size - 1); + + rapidjson::Value json_member, json_inner_value; + + json_member.SetString(str, length); + + rapid_json_serial_impl_serialize_value(throwable_value(th), &json_inner_value); + + json_map.AddMember(json_member, json_inner_value, rapid_json_allocator); + } else if (id == TYPE_PTR) { std::ostringstream ostream; @@ -328,7 +328,7 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json std::string s = ostream.str(); - json_v->SetString(s.c_str(), (rapidjson::SizeType)s.length()); + json_v->SetString(s.c_str(), static_cast(s.length())); } else if (id == TYPE_NULL) { @@ -336,33 +336,18 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json } } -char * rapid_json_serial_impl_document_stringify(RapidJSONSerialDocument * document, size_t * size) +char *rapid_json_serial_impl_document_stringify(rapid_json_document document, size_t *size) { - char * buffer_str; - - size_t buffer_size, buffer_str_size; - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - - document->Accept(writer); - - /* StringBuffer does not contain '\0' character so buffer size equals to buffer_str length */ - buffer_size = buffer.GetSize(); - - buffer_str_size = buffer_size + 1; - - RapidJSONSerialDocument::AllocatorType & allocator = document->GetAllocator(); - - memory_allocator impl = allocator.Impl(); - - buffer_str = static_cast(memory_allocator_allocate(impl, sizeof(char) * buffer_str_size)); + document->impl.Accept(writer); + size_t buffer_size = buffer.GetSize(); + size_t buffer_str_size = buffer_size + 1; + char *buffer_str = static_cast(memory_allocator_allocate(document->allocator, sizeof(char) * buffer_str_size)); if (buffer_str == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid string allocation for document stringifycation in RapidJSON implementation"); - return NULL; } @@ -375,9 +360,9 @@ char * rapid_json_serial_impl_document_stringify(RapidJSONSerialDocument * docum return buffer_str; } -char * rapid_json_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size) +char *rapid_json_serial_impl_serialize(serial_handle handle, value v, size_t *size) { - RapidJSONSerialDocument * document = (RapidJSONSerialDocument *)handle; + rapid_json_document document = static_cast(handle); if (handle == NULL || v == NULL || size == NULL) { @@ -386,14 +371,12 @@ char * rapid_json_serial_impl_serialize(serial_impl_handle handle, value v, size return NULL; } - RapidJSONSerialDocument::AllocatorType & allocator = document->GetAllocator(); - - rapid_json_serial_impl_serialize_value(v, document, allocator); + rapid_json_serial_impl_serialize_value(v, &document->impl); return rapid_json_serial_impl_document_stringify(document, size); } -value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) +value rapid_json_serial_impl_deserialize_value(const rapidjson::Value *v) { if (v->IsNull()) { @@ -419,6 +402,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) { unsigned int ui = v->GetUint(); + /* TODO: Review this, in case of underflow/overflow store it in a bigger type? */ log_write("metacall", LOG_LEVEL_WARNING, "Casting unsigned integer to integer (posible overflow) in RapidJSON implementation"); return value_create_int((int)ui); @@ -427,13 +411,21 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) { int64_t i = v->GetInt64(); + /* TODO: Review this, in case of underflow/overflow store it in a bigger type? */ +#if LONG_MAX < INT64_MAX + log_write("metacall", LOG_LEVEL_WARNING, "Casting long to int (posible overflow) in RapidJSON implementation"); +#endif + return value_create_long((long)i); } else if (v->IsUint64() == true) { uint64_t ui = v->GetUint64(); + /* TODO: Review this, in case of underflow/overflow store it in a bigger type? */ +#if LONG_MAX < UINT64_MAX log_write("metacall", LOG_LEVEL_WARNING, "Casting unsigned long to int (posible overflow) in RapidJSON implementation"); +#endif return value_create_long((long)ui); } @@ -453,7 +445,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) { rapidjson::SizeType length = v->GetStringLength(); - const char * str = v->GetString(); + const char *str = v->GetString(); return value_create_string(str, (size_t)length); } @@ -463,7 +455,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) value v_array = value_create_array(NULL, size); - value * values; + value *values; size_t index = 0; @@ -474,9 +466,23 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) values = static_cast(value_to_array(v_array)); - for (RapidJSONSerialValue::ConstValueIterator it = v->Begin(); it != v->End(); ++it) + for (rapidjson::Value::ConstValueIterator it = v->Begin(); it != v->End(); ++it) { - values[index++] = rapid_json_serial_impl_deserialize_value(it); + values[index] = rapid_json_serial_impl_deserialize_value(it); + + if (values[index] == NULL) + { + for (size_t iterator = 0; iterator < index; ++iterator) + { + value_type_destroy(values[iterator]); + } + + value_destroy(v_array); + + return NULL; + } + + ++index; } return v_array; @@ -487,7 +493,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) value v_map = value_create_map(NULL, size); - value * tuples; + value *tuples; size_t index = 0; @@ -498,14 +504,30 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) tuples = static_cast(value_to_map(v_map)); - for (RapidJSONSerialValue::ConstMemberIterator it = v->MemberBegin(); it != v->MemberEnd(); ++it) + for (rapidjson::Value::ConstMemberIterator it = v->MemberBegin(); it != v->MemberEnd(); ++it) { - const value tupla[] = - { + const value tupla[] = { rapid_json_serial_impl_deserialize_value(&it->name), rapid_json_serial_impl_deserialize_value(&it->value) }; + if (tupla[0] == NULL || tupla[1] == NULL) + { + if (tupla[0] != NULL) + { + value_type_destroy(tupla[0]); + } + + if (tupla[1] != NULL) + { + value_type_destroy(tupla[1]); + } + + value_type_destroy(v_map); + + return NULL; + } + tuples[index++] = value_create_array(tupla, sizeof(tupla) / sizeof(tupla[0])); } @@ -517,9 +539,9 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) return NULL; } -value rapid_json_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size) +value rapid_json_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size) { - RapidJSONSerialDocument * document = (RapidJSONSerialDocument *)handle; + rapid_json_document document = static_cast(handle); if (handle == NULL || buffer == NULL || size == 0) { @@ -528,11 +550,11 @@ value rapid_json_serial_impl_deserialize(serial_impl_handle handle, const char * return NULL; } - rapidjson::ParseResult parse_result = document->Parse(buffer, size - 1); + rapidjson::ParseResult parse_result = document->impl.Parse(buffer, size - 1); if (parse_result.IsError() == true) { - const RAPIDJSON_ERROR_CHARTYPE * error_message = rapidjson::GetParseError_En(parse_result.Code()); + const RAPIDJSON_ERROR_CHARTYPE *error_message = rapidjson::GetParseError_En(parse_result.Code()); log_write("metacall", LOG_LEVEL_ERROR, "Invalid parsing of document (%s) in RapidJSON implementation: %s at %" PRIuS, buffer, error_message, parse_result.Offset()); @@ -540,22 +562,15 @@ value rapid_json_serial_impl_deserialize(serial_impl_handle handle, const char * return NULL; } - return rapid_json_serial_impl_deserialize_value(document); + return rapid_json_serial_impl_deserialize_value(&document->impl); } -int rapid_json_serial_impl_destroy(serial_impl_handle handle) +int rapid_json_serial_impl_destroy(serial_handle handle) { - RapidJSONSerialDocument * document = (RapidJSONSerialDocument *)handle; + rapid_json_document document = static_cast(handle); - if (document != nullptr) + if (document != NULL) { - RapidJSONSerialDocument::AllocatorType * allocator = &document->GetAllocator(); - - if (allocator != nullptr) - { - delete allocator; - } - delete document; } diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index d2d5fd11ed..2f2e6bb890 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -12,7 +12,7 @@ if("${CMAKE_VERSION}" VERSION_LESS "3.11" AND POLICY CMP0037) set_policy(CMP0037 OLD) endif() -set(GTEST_VERSION 1.8.1) +set(GTEST_VERSION 1.16.0) find_package(GTest ${GTEST_VERSION}) @@ -20,7 +20,7 @@ if(NOT GTEST_FOUND) include(InstallGTest) if(NOT GTEST_FOUND) - message(STATUS "GTest libraries not found") + message(SEND_ERROR "GTest libraries not found") return() endif() @@ -47,18 +47,12 @@ endif() # Set memory check configuration option(OPTION_TEST_MEMORYCHECK "Run tests with memory checker (valgrind)." OFF) -if(OPTION_TEST_MEMORYCHECK) - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --leak-check=full") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --trace-children=yes") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --show-reachable=yes") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --track-origins=yes") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --num-callers=50") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --smc-check=all-non-file") # for JITs - set(MEMORYCHECK_SUPPRESSIONS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/memcheck/valgrind.supp") - set(MEMORYCHECK_SUPPRESSIONS_FILE "${MEMORYCHECK_SUPPRESSIONS_FILE};${CMAKE_CURRENT_SOURCE_DIR}/memcheck/valgrind-python.supp") - set(MEMORYCHECK_SUPPRESSIONS_FILE "${MEMORYCHECK_SUPPRESSIONS_FILE};${CMAKE_CURRENT_SOURCE_DIR}/memcheck/valgrind-node.supp") +if(OPTION_TEST_MEMORYCHECK AND (OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER OR OPTION_BUILD_MEMORY_SANITIZER)) + message(WARNING "OPTION_TEST_MEMORYCHECK and OPTION_BUILD_{ADDRESS,THREAD,MEMORY}_SANITIZER are not compatible, disabling memcheck tests.") +endif() +if(OPTION_TEST_MEMORYCHECK AND NOT (OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER OR OPTION_BUILD_MEMORY_SANITIZER)) # TODO: Memory check does not work properly with CoreCLR # # Remove MEMCHECK_IGNORE label from the following tests: @@ -68,14 +62,19 @@ if(OPTION_TEST_MEMORYCHECK) # - metacall-inspect-test # - metacall-integration-test + include(ProcessorCount) + + ProcessorCount(N) + add_custom_target(memcheck WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_CTEST_COMMAND} + -j${N} --label-exclude MEMCHECK_IGNORE --force-new-ctest-process --test-action memcheck - --timeout 60 - #COMMAND cat "${CMAKE_BINARY_DIR}/Testing/Temporary/MemoryChecker.*.log" + --timeout 5400 + COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_BINARY_DIR}/Testing/Temporary/MemoryChecker.*.log" ) endif() @@ -91,40 +90,66 @@ add_subdirectory(log_test) add_subdirectory(log_custom_test) add_subdirectory(adt_set_test) add_subdirectory(adt_trie_test) +add_subdirectory(adt_vector_test) +add_subdirectory(adt_map_test) add_subdirectory(reflect_value_cast_test) add_subdirectory(reflect_function_test) +add_subdirectory(reflect_object_class_test) add_subdirectory(reflect_scope_test) add_subdirectory(reflect_metadata_test) add_subdirectory(dynlink_test) add_subdirectory(detour_test) add_subdirectory(serial_test) add_subdirectory(configuration_test) -add_subdirectory(py_loader_test) -#add_subdirectory(py_django_integration_test) # TODO: Solve deadlock -add_subdirectory(py_loader_port_test) -add_subdirectory(rb_loader_test) add_subdirectory(rb_loader_parser_test) -add_subdirectory(rb_loader_parser_integration_test) -#add_subdirectory(rb_rails_integration_test) # TODO -add_subdirectory(cs_loader_test) -add_subdirectory(node_loader_test) -add_subdirectory(file_loader_test) -add_subdirectory(loader_path_test) +add_subdirectory(portability_path_test) add_subdirectory(metacall_logs_test) add_subdirectory(metacall_load_memory_test) +add_subdirectory(metacall_load_memory_empty_test) add_subdirectory(metacall_load_configuration_test) +add_subdirectory(metacall_load_configuration_fail_test) add_subdirectory(metacall_load_configuration_relative_test) +add_subdirectory(metacall_load_configuration_python_node_test) +add_subdirectory(metacall_load_configuration_node_python_test) add_subdirectory(metacall_duplicated_handle_test) add_subdirectory(metacall_duplicated_symbols_test) add_subdirectory(metacall_handle_export_test) +add_subdirectory(metacall_handle_get_test) add_subdirectory(metacall_test) add_subdirectory(metacall_node_test) add_subdirectory(metacall_node_event_loop_test) -#add_subdirectory(metacall_node_default_export_test) # TODO: Default exporter in bootstrap.js +add_subdirectory(metacall_node_event_loop_signal_test) +#add_subdirectory(metacall_node_default_export_test) # TODO: This is a feature in order to export by default all functions if there is no module.exports (bootstrap.js) add_subdirectory(metacall_node_call_test) add_subdirectory(metacall_node_inline_test) add_subdirectory(metacall_node_async_test) +add_subdirectory(metacall_node_async_multiple_test) add_subdirectory(metacall_node_reentrant_test) +add_subdirectory(metacall_node_port_test) +add_subdirectory(metacall_node_port_await_test) +add_subdirectory(metacall_node_port_rs_test) +add_subdirectory(metacall_node_port_c_lib_test) +add_subdirectory(metacall_node_python_port_mock_test) +add_subdirectory(metacall_node_python_port_ruby_test) +add_subdirectory(metacall_node_python_ruby_test) +add_subdirectory(metacall_node_callback_test) +add_subdirectory(metacall_node_fail_test) +add_subdirectory(metacall_node_fail_env_var_test) +add_subdirectory(metacall_node_fail_load_leak_test) +add_subdirectory(metacall_node_typescript_test) +add_subdirectory(metacall_node_python_async_after_destroy_test) +add_subdirectory(metacall_node_python_await_test) +# add_subdirectory(metacall_node_python_await_extended_test) # TODO: https://github.com/metacall/core/issues/519 +add_subdirectory(metacall_node_python_exception_test) +add_subdirectory(metacall_node_clear_mem_test) +add_subdirectory(metacall_node_async_resources_test) +add_subdirectory(metacall_node_await_chain_test) +add_subdirectory(metacall_node_exception_test) +add_subdirectory(metacall_node_python_deadlock_test) +# add_subdirectory(metacall_node_signal_handler_test) # Note: Not used anymore but leaving it here for reference to solve this: https://github.com/metacall/core/issues/121 +add_subdirectory(metacall_node_native_code_test) +add_subdirectory(metacall_node_extension_test) +add_subdirectory(metacall_node_multithread_deadlock_test) add_subdirectory(metacall_distributable_test) add_subdirectory(metacall_cast_test) add_subdirectory(metacall_init_fini_test) @@ -133,25 +158,92 @@ add_subdirectory(metacall_inspect_test) add_subdirectory(metacall_integration_test) add_subdirectory(metacall_depends_test) add_subdirectory(metacall_configuration_exec_path_test) +add_subdirectory(metacall_configuration_exec_relative_path_test) +add_subdirectory(metacall_configuration_default_test) add_subdirectory(metacall_clear_test) -add_subdirectory(metacall_python_class_test) +add_subdirectory(metacall_python_test) +add_subdirectory(metacall_python_object_class_test) add_subdirectory(metacall_python_gc_test) add_subdirectory(metacall_python_open_test) add_subdirectory(metacall_python_dict_test) add_subdirectory(metacall_python_model_test) add_subdirectory(metacall_python_pointer_test) add_subdirectory(metacall_python_reentrant_test) +add_subdirectory(metacall_python_varargs_test) +add_subdirectory(metacall_python_loader_port_test) +add_subdirectory(metacall_python_port_test) +add_subdirectory(metacall_python_port_https_test) +add_subdirectory(metacall_python_port_callback_test) +add_subdirectory(metacall_python_port_pointer_test) +add_subdirectory(metacall_python_port_import_test) +add_subdirectory(metacall_python_callback_test) +add_subdirectory(metacall_python_fail_test) +add_subdirectory(metacall_python_relative_path_test) +add_subdirectory(metacall_python_without_functions_test) +add_subdirectory(metacall_python_builtins_test) +add_subdirectory(metacall_python_async_test) +# TODO: add_subdirectory(metacall_python_await_test) # TODO: Implement metacall_await in Python Port +add_subdirectory(metacall_python_exception_test) +# TODO: add_subdirectory(metacall_python_node_await_test) # TODO: Implement metacall_await in Python Port +add_subdirectory(metacall_python_without_env_vars_test) add_subdirectory(metacall_map_test) add_subdirectory(metacall_map_await_test) add_subdirectory(metacall_initialize_test) add_subdirectory(metacall_initialize_ex_test) add_subdirectory(metacall_reinitialize_test) +add_subdirectory(metacall_initialize_destroy_multiple_test) +add_subdirectory(metacall_initialize_destroy_multiple_node_test) +add_subdirectory(metacall_reload_functions_test) +add_subdirectory(metacall_invalid_loader_test) add_subdirectory(metacall_fork_test) add_subdirectory(metacall_return_monad_test) -add_subdirectory(metacall_callback_test) +add_subdirectory(metacall_callback_complex_test) add_subdirectory(metacall_ruby_fail_test) +add_subdirectory(metacall_ruby_fail_empty_test) +add_subdirectory(metacall_ruby_object_class_test) +add_subdirectory(metacall_ruby_parser_integration_test) +# add_subdirectory(metacall_ruby_rails_integration_test) # TODO add_subdirectory(metacall_function_test) add_subdirectory(metacall_cobol_test) add_subdirectory(metacall_file_test) +add_subdirectory(metacall_file_fail_test) +add_subdirectory(metacall_file_glob_test) add_subdirectory(metacall_typescript_test) -# add_subdirectory(metacall_typescript_tsx_test) # TODO: Implement dependency management for React and ReactDOM +add_subdirectory(metacall_typescript_node_test) +add_subdirectory(metacall_typescript_call_map_test) +add_subdirectory(metacall_typescript_tsx_test) +add_subdirectory(metacall_typescript_tsx_loop_fail_test) +add_subdirectory(metacall_typescript_require_test) +add_subdirectory(metacall_typescript_jsx_default_test) +add_subdirectory(metacall_lua_test) +add_subdirectory(metacall_rpc_test) +#add_subdirectory(metacall_csharp_function_test) # TODO: C# 9.0 seems not to work so top level expressions do not work +add_subdirectory(metacall_csharp_static_class_test) +add_subdirectory(metacall_llvm_test) +add_subdirectory(metacall_ruby_test) +add_subdirectory(metacall_cs_test) +add_subdirectory(metacall_julia_test) +add_subdirectory(metacall_java_test) +add_subdirectory(metacall_wasm_test) +add_subdirectory(metacall_wasm_python_port_test) +add_subdirectory(metacall_rust_test) +add_subdirectory(metacall_rust_load_from_mem_test) +add_subdirectory(metacall_rust_load_from_package_test) +add_subdirectory(metacall_rust_load_from_package_dep_test) +add_subdirectory(metacall_rust_load_from_package_class_test) +add_subdirectory(metacall_rust_class_test) +add_subdirectory(metacall_c_test) +add_subdirectory(metacall_c_lib_test) +add_subdirectory(metacall_version_test) +add_subdirectory(metacall_dynlink_path_test) +add_subdirectory(metacall_library_path_without_env_vars_test) +add_subdirectory(metacall_ext_test) +add_subdirectory(metacall_plugin_extension_test) +add_subdirectory(metacall_plugin_extension_local_test) +add_subdirectory(metacall_plugin_extension_destroy_order_test) +add_subdirectory(metacall_plugin_extension_invalid_path_test) +add_subdirectory(metacall_cli_core_plugin_test) +add_subdirectory(metacall_cli_core_plugin_await_test) +add_subdirectory(metacall_backtrace_plugin_test) +add_subdirectory(metacall_sandbox_plugin_test) +add_subdirectory(metacall_cxx_port_test) diff --git a/source/tests/loader_path_test/CMakeLists.txt b/source/tests/adt_map_test/CMakeLists.txt similarity index 89% rename from source/tests/loader_path_test/CMakeLists.txt rename to source/tests/adt_map_test/CMakeLists.txt index 4eaa0cf413..0767bcbb2b 100644 --- a/source/tests/loader_path_test/CMakeLists.txt +++ b/source/tests/adt_map_test/CMakeLists.txt @@ -3,7 +3,7 @@ # # Target name -set(target loader-path-test) +set(target adt-map-test) message(STATUS "Test ${target}") # @@ -27,7 +27,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/loader_path_test.cpp + ${source_path}/adt_map_test.cpp ) # Group source files @@ -83,10 +83,10 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader + ) # @@ -107,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/adt_map_test/source/adt_map_test.cpp b/source/tests/adt_map_test/source/adt_map_test.cpp new file mode 100644 index 0000000000..24217e3834 --- /dev/null +++ b/source/tests/adt_map_test/source/adt_map_test.cpp @@ -0,0 +1,247 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include + +typedef char key_str[7]; + +static size_t iterator_counter = 0; +static std::vector order_iterate; +static std::vector order_iterator; + +int map_cb_iterate_str_to_int(map m, map_key key, map_value value, map_cb_iterate_args args) +{ + if (m && args == NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "%s -> %d", (char *)key, *((int *)(value))); + + order_iterate.push_back(*((int *)(value))); + + ++iterator_counter; + + return 0; + } + + return 1; +} + +class adt_map_test : public testing::Test +{ +public: +}; + +TEST_F(adt_map_test, map_int) +{ + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + map m = map_create(&hash_callback_str, &comparable_callback_str); + + static key_str key_array[] = { + "aaaaaa", "aaaaab", "aaaaba", "aaaabb", + "aaabaa", "aaabab", "aaabba", "aaabbb", + "aabaaa", "aabaab", "aababa", "aababb", + "aabbaa", "aabbab", "aabbba", "aabbbb", + "abaaaa", "abaaab", "abaaba", "abaabb", + "ababaa", "ababab", "ababbb", "abbaaa", + "abbaab", "abbaba", "abbabb", "abbbaa", + "abbbab", "abbbba", "abbbbb", "baaaaa", + "baaaab", "baaaba", "baaabb", "baabaa", + "baabab", "baabba", "baabbb", "babaaa", + "babaab", "bababa", "bababb", "babbaa", + "babbab", "babbba", "babbbb", "bbaaaa", + "bbaaab", "bbaaba", "bbaabb", "bbabaa", + "bbabab", "bbabba", "bbabbb", "bbbaaa", + "bbbaab", "bbbaba", "bbbabb", "bbbbaa", + "bbbbab", "bbbbba", "bbbbbb" + }; + + static const size_t key_array_size = sizeof(key_array) / sizeof(key_array[0]); + + static int value_array[] = { + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72 + }; + + static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + + EXPECT_EQ((size_t)key_array_size, (size_t)value_array_size); + + /* Insert value */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)map_insert(m, key_array[i], &value_array[i])); + } + + /* Insert duplicated values */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)map_insert(m, key_array[i], &value_array[value_array_size - 1 - i])); + } + + map_iterate(m, &map_cb_iterate_str_to_int, NULL); + + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size * 2); + + /* Iterators */ + iterator_counter = 0; + struct map_iterator_type it; + + for (map_iterator_begin(&it, m); map_iterator_end(&it) != 0; map_iterator_next(&it)) + { + char *key = (char *)map_iterator_key(&it); + int *value = (int *)map_iterator_value(&it); + + log_write("metacall", LOG_LEVEL_DEBUG, "[%s -> %d]", (char *)key, *((int *)(value))); + + order_iterator.push_back(*((int *)(value))); + + iterator_counter++; + } + + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size * 2); + + EXPECT_EQ((bool)true, (bool)(order_iterator == order_iterate)); + + /* Get value */ + for (size_t i = 0; i < key_array_size; ++i) + { + vector v = map_get(m, key_array[i]); + + EXPECT_EQ((size_t)vector_size(v), (size_t)2); + + int *value0 = vector_at_type(v, 0, int *); + + EXPECT_EQ((int)(value_array[i] == *value0 || value_array[value_array_size - 1 - i] == *value0), (int)1); + + int *value1 = vector_at_type(v, 1, int *); + + EXPECT_EQ((int)(value_array[i] == *value1 || value_array[value_array_size - 1 - i] == *value1), (int)1); + + log_write("metacall", LOG_LEVEL_DEBUG, "%s -> %d | %d", key_array[i], *value0, *value1); + + vector_destroy(v); + } + + EXPECT_EQ((size_t)key_array_size * 2, (size_t)map_size(m)); + + /* Remove value */ + for (size_t i = 0; i < key_array_size; ++i) + { + int *remove_value = (int *)map_remove(m, key_array[i]); + + EXPECT_EQ((int)(value_array[i] == *remove_value || value_array[value_array_size - 1 - i] == *remove_value), (int)1); + } + + EXPECT_EQ((size_t)key_array_size, (size_t)map_size(m)); + + /* Remove multiple values */ + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[5])); + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[6])); + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[7])); + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[8])); + + vector v = map_remove_all(m, key_array[0]); + + EXPECT_NE((vector)NULL, (vector)v); + + EXPECT_EQ((size_t)5, (size_t)vector_size(v)); + + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 0, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 1, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 2, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 3, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 4, int *))); + + vector_destroy(v); + + log_write("metacall", LOG_LEVEL_DEBUG, "vvvvvvvvvvvvvvvv - This invalid message is correct, we are testing for a invalid remove"); + v = map_remove_all(m, key_array[0]); + log_write("metacall", LOG_LEVEL_DEBUG, "^^^^^^^^^^^^^^^^ - This invalid message is correct, we are testing for a invalid remove"); + + EXPECT_EQ((vector)NULL, (vector)v); + + EXPECT_EQ((size_t)key_array_size - 1, (size_t)map_size(m)); + + map_destroy(m); +} + +TEST_F(adt_map_test, map_structs) +{ + typedef struct stru_type + { + const char *str; + int a; + } * stru; + + static const char k[] = "random"; + + stru a = new stru_type(); + a->str = k; + a->a = 123; + + stru b = new stru_type(); + b->str = k; + b->a = 321; + + map m = map_create(&hash_callback_str, &comparable_callback_str); + + EXPECT_EQ((int)0, (int)map_insert(m, (map_key)a->str, a)); + EXPECT_EQ((int)0, (int)map_insert(m, (map_key)b->str, b)); + + vector v = map_get(m, (map_key)k); + + EXPECT_EQ((size_t)vector_size(v), (size_t)2); + + stru value0 = vector_at_type(v, 0, stru); + + EXPECT_EQ((int)(123 == value0->a || 321 == value0->a), (int)1); + + stru value1 = vector_at_type(v, 1, stru); + + EXPECT_EQ((int)(123 == value1->a || 321 == value1->a), (int)1); + + EXPECT_EQ((int)(a == value0 || a == value1), (int)1); + EXPECT_EQ((int)(b == value0 || b == value1), (int)1); + + log_write("metacall", LOG_LEVEL_DEBUG, "A-B %p | %p", a, b); + log_write("metacall", LOG_LEVEL_DEBUG, "val %p | %p", value0, value1); + + vector_destroy(v); + + map_destroy(m); + + delete a; + delete b; +} diff --git a/source/tests/loader_path_test/source/main.cpp b/source/tests/adt_map_test/source/main.cpp similarity index 81% rename from source/tests/loader_path_test/source/main.cpp rename to source/tests/adt_map_test/source/main.cpp index 596d0a2762..24fae6af69 100644 --- a/source/tests/loader_path_test/source/main.cpp +++ b/source/tests/adt_map_test/source/main.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/adt_set_test/CMakeLists.txt b/source/tests/adt_set_test/CMakeLists.txt index 8e6c231ec2..a3d3b1c3f0 100644 --- a/source/tests/adt_set_test/CMakeLists.txt +++ b/source/tests/adt_set_test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::adt @@ -106,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/adt_set_test/source/adt_set_test.cpp b/source/tests/adt_set_test/source/adt_set_test.cpp index c3c9680d93..85c087b9ce 100644 --- a/source/tests/adt_set_test/source/adt_set_test.cpp +++ b/source/tests/adt_set_test/source/adt_set_test.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -27,6 +27,8 @@ typedef char key_str[7]; static size_t iterator_counter = 0; +static std::vector order_iterate; +static std::vector order_iterator; int set_cb_iterate_str_to_int(set s, set_key key, set_value value, set_cb_iterate_args args) { @@ -34,6 +36,8 @@ int set_cb_iterate_str_to_int(set s, set_key key, set_value value, set_cb_iterat { log_write("metacall", LOG_LEVEL_DEBUG, "%s -> %d", (char *)key, *((int *)(value))); + order_iterate.push_back(*((int *)(value))); + ++iterator_counter; return 0; @@ -42,101 +46,205 @@ int set_cb_iterate_str_to_int(set s, set_key key, set_value value, set_cb_iterat return 1; } +static size_t iterator_counter_ptr = 0; + +int set_cb_iterate_ptr_to_int(set s, set_key key, set_value value, set_cb_iterate_args args) +{ + if (s && args == NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "%p -> %d", key, *((int *)(value))); + + ++iterator_counter_ptr; + + return 0; + } + + return 1; +} + class adt_set_test : public testing::Test { - public: +public: }; TEST_F(adt_set_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); - set s = set_create(&hash_callback_str, &comparable_callback_str); - - static key_str key_array[] = - { - "aaaaaa", "aaaaab", "aaaaba", "aaaabb", - "aaabaa", "aaabab", "aaabba", "aaabbb", - "aabaaa", "aabaab", "aababa", "aababb", - "aabbaa", "aabbab", "aabbba", "aabbbb", - "abaaaa", "abaaab", "abaaba", "abaabb", - "ababaa", "ababab", "ababbb", "abbaaa", - "abbaab", "abbaba", "abbabb", "abbbaa", - "abbbab", "abbbba", "abbbbb", "baaaaa", - "baaaab", "baaaba", "baaabb", "baabaa", - "baabab", "baabba", "baabbb", "babaaa", - "babaab", "bababa", "bababb", "babbaa", - "babbab", "babbba", "babbbb", "bbaaaa", - "bbaaab", "bbaaba", "bbaabb", "bbabaa", - "bbabab", "bbabba", "bbabbb", "bbbaaa", - "bbbaab", "bbbaba", "bbbabb", "bbbbaa", - "bbbbab", "bbbbba", "bbbbbb" - }; - - static const size_t key_array_size = sizeof(key_array) / sizeof(key_array[0]); - - static int value_array[] = + /* Hash String */ { - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72 - }; + set s = set_create(&hash_callback_str, &comparable_callback_str); + + static key_str key_array[] = { + "aaaaaa", "aaaaab", "aaaaba", "aaaabb", + "aaabaa", "aaabab", "aaabba", "aaabbb", + "aabaaa", "aabaab", "aababa", "aababb", + "aabbaa", "aabbab", "aabbba", "aabbbb", + "abaaaa", "abaaab", "abaaba", "abaabb", + "ababaa", "ababab", "ababbb", "abbaaa", + "abbaab", "abbaba", "abbabb", "abbbaa", + "abbbab", "abbbba", "abbbbb", "baaaaa", + "baaaab", "baaaba", "baaabb", "baabaa", + "baabab", "baabba", "baabbb", "babaaa", + "babaab", "bababa", "bababb", "babbaa", + "babbab", "babbba", "babbbb", "bbaaaa", + "bbaaab", "bbaaba", "bbaabb", "bbabaa", + "bbabab", "bbabba", "bbabbb", "bbbaaa", + "bbbaab", "bbbaba", "bbbabb", "bbbbaa", + "bbbbab", "bbbbba", "bbbbbb" + }; + + static const size_t key_array_size = sizeof(key_array) / sizeof(key_array[0]); + + static int value_array[] = { + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72 + }; + + static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + + EXPECT_EQ((size_t)key_array_size, (size_t)value_array_size); + + /* Insert value */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)set_insert(s, key_array[i], &value_array[i])); + } - static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + set_iterate(s, &set_cb_iterate_str_to_int, NULL); - EXPECT_EQ((size_t) key_array_size, (size_t) value_array_size); + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size); - /* Insert value */ - for (size_t i = 0; i < key_array_size; ++i) - { - EXPECT_EQ((int) 0, (int) set_insert(s, key_array[i], &value_array[i])); - } + /* Iterators */ + iterator_counter = 0; + struct set_iterator_type it; - set_iterate(s, &set_cb_iterate_str_to_int, NULL); + for (set_iterator_begin(&it, s); set_iterator_end(&it) != 0; set_iterator_next(&it)) + { + char *key = (char *)set_iterator_key(&it); + int *value = (int *)set_iterator_value(&it); - EXPECT_EQ((size_t) iterator_counter, (size_t) value_array_size); + log_write("metacall", LOG_LEVEL_DEBUG, "[%s -> %d]", (char *)key, *((int *)(value))); - /* Get value */ - for (size_t i = 0; i < key_array_size; ++i) - { - int * value = (int *)set_get(s, key_array[i]); + order_iterator.push_back(*((int *)(value))); - EXPECT_NE((int *) NULL, (int *) value); + iterator_counter++; + } - EXPECT_EQ((int) value_array[i], (int) *value); - } + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size); + + EXPECT_EQ((bool)true, (bool)(order_iterator == order_iterate)); + + /* Get value */ + for (size_t i = 0; i < key_array_size; ++i) + { + int *value = (int *)set_get(s, key_array[i]); - /* Overwrite value */ - EXPECT_EQ((int) 0, (int) set_insert(s, key_array[1], &value_array[0])); + EXPECT_NE((int *)NULL, (int *)value); - int * value = (int *)set_get(s, key_array[1]); + EXPECT_EQ((int)value_array[i], (int)*value); + } + + /* Overwrite value */ + EXPECT_EQ((int)0, (int)set_insert(s, key_array[1], &value_array[0])); - EXPECT_EQ((int) value_array[0], (int) *value); + int *value = (int *)set_get(s, key_array[1]); - EXPECT_EQ((size_t) key_array_size, (size_t) set_size(s)); + EXPECT_EQ((int)value_array[0], (int)*value); - /* Remove value */ - for (size_t i = 0; i < key_array_size; ++i) + EXPECT_EQ((size_t)key_array_size, (size_t)set_size(s)); + + /* Remove value */ + for (size_t i = 0; i < key_array_size; ++i) + { + int *remove_value = (int *)set_remove(s, key_array[i]); + + if (i == 1) + { + EXPECT_EQ((int)value_array[0], (int)*remove_value); + } + else + { + EXPECT_EQ((int)value_array[i], (int)*remove_value); + } + } + + set_destroy(s); + } + + /* Hash Pointer */ { - int * remove_value = (int *)set_remove(s, key_array[i]); + set s = set_create(&hash_callback_ptr, &comparable_callback_ptr); + + static int value_array[] = { + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72 + }; + + static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + + static int key_array[value_array_size] = { 0 }; + + static const size_t key_array_size = value_array_size; + + /* Insert value */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)set_insert(s, &key_array[i], &value_array[i])); + } + + set_iterate(s, &set_cb_iterate_ptr_to_int, NULL); + + EXPECT_EQ((size_t)iterator_counter_ptr, (size_t)value_array_size); - if (i == 1) + /* Get value */ + for (size_t i = 0; i < key_array_size; ++i) { - EXPECT_EQ((int) value_array[0], (int) *remove_value); + int *value = (int *)set_get(s, &key_array[i]); + + EXPECT_NE((int *)NULL, (int *)value); + + EXPECT_EQ((int)value_array[i], (int)*value); } - else + + /* Overwrite value */ + EXPECT_EQ((int)0, (int)set_insert(s, &key_array[1], &value_array[0])); + + int *value = (int *)set_get(s, &key_array[1]); + + EXPECT_EQ((int)value_array[0], (int)*value); + + EXPECT_EQ((size_t)key_array_size, (size_t)set_size(s)); + + /* Remove value */ + for (size_t i = 0; i < key_array_size; ++i) { - EXPECT_EQ((int) value_array[i], (int) *remove_value); + int *remove_value = (int *)set_remove(s, &key_array[i]); + + if (i == 1) + { + EXPECT_EQ((int)value_array[0], (int)*remove_value); + } + else + { + EXPECT_EQ((int)value_array[i], (int)*remove_value); + } } - } - set_destroy(s); + set_destroy(s); + } } diff --git a/source/tests/adt_set_test/source/main.cpp b/source/tests/adt_set_test/source/main.cpp index 596d0a2762..24fae6af69 100644 --- a/source/tests/adt_set_test/source/main.cpp +++ b/source/tests/adt_set_test/source/main.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/adt_trie_test/CMakeLists.txt b/source/tests/adt_trie_test/CMakeLists.txt index dbe6da5cdb..19babf556b 100644 --- a/source/tests/adt_trie_test/CMakeLists.txt +++ b/source/tests/adt_trie_test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::adt ) @@ -105,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/adt_trie_test/source/adt_trie_test.cpp b/source/tests/adt_trie_test/source/adt_trie_test.cpp index 6225f7b5f3..1d9541dc03 100644 --- a/source/tests/adt_trie_test/source/adt_trie_test.cpp +++ b/source/tests/adt_trie_test/source/adt_trie_test.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ * */ -#include +#include -#include #include +#include #include @@ -29,7 +29,7 @@ class adt_trie_test : public testing::Test { - public: +public: }; int trie_iterator_cb_print(trie t, trie_key key, trie_value value, trie_cb_iterate_args args) @@ -38,8 +38,8 @@ int trie_iterator_cb_print(trie t, trie_key key, trie_value value, trie_cb_itera if (t != NULL && key != NULL && value != NULL) { - const char * key_str = reinterpret_cast(key); - const char * value_str = reinterpret_cast(value); + const char *key_str = reinterpret_cast(key); + const char *value_str = reinterpret_cast(value); log_write("metacall", LOG_LEVEL_DEBUG, "%s : %s", key_str, value_str); @@ -67,21 +67,19 @@ int trie_iterator_cb_clear(trie t, trie_key key, trie_value value, trie_cb_itera TEST_F(adt_trie_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); size_t iterator; - static const char * keys_str[] = - { + static const char *keys_str[] = { "this", "is", "a", "path" }; - static const char * values_str[] = - { + static const char *values_str[] = { "these", "are", "the", "values" }; @@ -95,7 +93,7 @@ TEST_F(adt_trie_test, DefaultConstructor) trie suffix_trie = NULL; - trie_key * last_prefix; + trie_key *last_prefix; size_t keys_size = sizeof(keys_str) / sizeof(keys_str[0]); @@ -112,7 +110,7 @@ TEST_F(adt_trie_test, DefaultConstructor) vector_push_back(keys, &key); - EXPECT_EQ((int) 0, (int) trie_insert(t, keys, value)); + EXPECT_EQ((int)0, (int)trie_insert(t, keys, value)); } keys_copy = vector_copy(keys); @@ -121,11 +119,11 @@ TEST_F(adt_trie_test, DefaultConstructor) { trie_value value = trie_get(t, keys_copy); - const char * value_str = reinterpret_cast(value); + const char *value_str = reinterpret_cast(value); log_write("metacall", LOG_LEVEL_DEBUG, "%" PRIuS " -> %s", iterator, value_str); - EXPECT_EQ((int) 0, (int) strcmp(values_str[keys_size - iterator - 1], value_str)); + EXPECT_STREQ(values_str[keys_size - iterator - 1], value_str); vector_pop_back(keys_copy); } @@ -134,19 +132,19 @@ TEST_F(adt_trie_test, DefaultConstructor) last_prefix = reinterpret_cast(vector_back(keys)); - EXPECT_EQ((int) 0, (int) trie_prefixes(t, *last_prefix, prefixes)); + EXPECT_EQ((int)0, (int)trie_prefixes(t, *last_prefix, prefixes)); log_write("metacall", LOG_LEVEL_DEBUG, "cannonical path: "); for (iterator = 0; iterator < vector_size(prefixes); ++iterator) { - trie_key * key = reinterpret_cast(vector_at(prefixes, iterator)); + trie_key *key = reinterpret_cast(vector_at(prefixes, iterator)); - const char * key_str = reinterpret_cast(*key); + const char *key_str = reinterpret_cast(*key); log_write("metacall", LOG_LEVEL_DEBUG, "%s/", key_str); - EXPECT_EQ((int) 0, (int) strcmp(keys_str[iterator], key_str)); + EXPECT_STREQ(keys_str[iterator], key_str); } vector_pop_back(keys); diff --git a/source/tests/adt_trie_test/source/main.cpp b/source/tests/adt_trie_test/source/main.cpp index 596d0a2762..24fae6af69 100644 --- a/source/tests/adt_trie_test/source/main.cpp +++ b/source/tests/adt_trie_test/source/main.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/adt_vector_test/CMakeLists.txt b/source/tests/adt_vector_test/CMakeLists.txt new file mode 100644 index 0000000000..e454f0266d --- /dev/null +++ b/source/tests/adt_vector_test/CMakeLists.txt @@ -0,0 +1,142 @@ +# +# Executable name and options +# + +# Target name +set(target adt-vector-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/adt_vector_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::adt + +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/adt_vector_test/source/adt_vector_test.cpp b/source/tests/adt_vector_test/source/adt_vector_test.cpp new file mode 100644 index 0000000000..816c3bcf59 --- /dev/null +++ b/source/tests/adt_vector_test/source/adt_vector_test.cpp @@ -0,0 +1,54 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class adt_vector_test : public testing::Test +{ +public: +}; + +TEST_F(adt_vector_test, DefaultConstructor) +{ + static const size_t capacity = 50; + + vector v = vector_create_reserve_type(size_t, capacity); + + ASSERT_EQ((size_t)vector_type_size(v), (size_t)sizeof(size_t)); + ASSERT_EQ((size_t)vector_size(v), (size_t)0); + + for (size_t i = 0; i < capacity; ++i) + { + vector_push_back_var(v, i); + } + + ASSERT_EQ((size_t)vector_size(v), (size_t)capacity); + + for (size_t i = 0; i < capacity; ++i) + { + size_t current = vector_at_type(v, i, size_t); + + ASSERT_EQ((size_t)current, (size_t)i); + } + + vector_destroy(v); +} diff --git a/source/tests/adt_vector_test/source/main.cpp b/source/tests/adt_vector_test/source/main.cpp new file mode 100644 index 0000000000..24fae6af69 --- /dev/null +++ b/source/tests/adt_vector_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/configuration_test/CMakeLists.txt b/source/tests/configuration_test/CMakeLists.txt index d2626b2835..0700e2bf71 100644 --- a/source/tests/configuration_test/CMakeLists.txt +++ b/source/tests/configuration_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,18 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration + ${META_PROJECT_NAME}::metacall ) # @@ -112,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/configuration_test/source/configuration_test.cpp b/source/tests/configuration_test/source/configuration_test.cpp index ff78cb096a..056d99493f 100644 --- a/source/tests/configuration_test/source/configuration_test.cpp +++ b/source/tests/configuration_test/source/configuration_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -32,26 +32,26 @@ class configuration_test : public testing::Test { - public: +public: }; TEST_F(configuration_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); const char key_value[] = "value"; - char * configuration_path = environment_variable_create(CONFIGURATION_PATH, NULL); + char *configuration_path = environment_variable_create(CONFIGURATION_PATH, NULL); memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); - ASSERT_NE((memory_allocator) NULL, (memory_allocator) allocator); + ASSERT_NE((memory_allocator)NULL, (memory_allocator)allocator); - ASSERT_EQ((int) 0, (int) configuration_initialize("rapid_json", configuration_path, allocator)); + ASSERT_EQ((int)0, (int)configuration_initialize("rapid_json", configuration_path, allocator)); environment_variable_destroy(configuration_path); @@ -62,19 +62,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration global = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) global); + ASSERT_NE((configuration)NULL, (configuration)global); value v = configuration_value(global, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 12345, (int) value_to_int(v)); + EXPECT_EQ((int)12345, (int)value_to_int(v)); - v = configuration_value(global, key_value_local); + v = configuration_value_type(global, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 321321, (int) value_to_int(v)); + EXPECT_EQ((int)321321, (int)value_to_int(v)); } /* Child A */ @@ -84,19 +84,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_a = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_a); + ASSERT_NE((configuration)NULL, (configuration)child_a); value v = configuration_value(child_a, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 65432345, (int) value_to_int(v)); + EXPECT_EQ((int)65432345, (int)value_to_int(v)); - v = configuration_value(child_a, key_value_local); + v = configuration_value_type(child_a, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 555, (int) value_to_int(v)); + EXPECT_EQ((int)555, (int)value_to_int(v)); } /* Child B */ @@ -106,19 +106,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_b = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_b); + ASSERT_NE((configuration)NULL, (configuration)child_b); value v = configuration_value(child_b, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 54321, (int) value_to_int(v)); + EXPECT_EQ((int)54321, (int)value_to_int(v)); - v = configuration_value(child_b, key_value_local); + v = configuration_value_type(child_b, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 333, (int) value_to_int(v)); + EXPECT_EQ((int)333, (int)value_to_int(v)); } /* Child C */ @@ -128,19 +128,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_c = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_c); + ASSERT_NE((configuration)NULL, (configuration)child_c); value v = configuration_value(child_c, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 1111, (int) value_to_int(v)); + EXPECT_EQ((int)1111, (int)value_to_int(v)); - v = configuration_value(child_c, key_value_local); + v = configuration_value_type(child_c, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 8080, (int) value_to_int(v)); + EXPECT_EQ((int)8080, (int)value_to_int(v)); } /* Child D */ @@ -150,19 +150,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_d = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_d); + ASSERT_NE((configuration)NULL, (configuration)child_d); value v = configuration_value(child_d, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 22222, (int) value_to_int(v)); + EXPECT_EQ((int)22222, (int)value_to_int(v)); - v = configuration_value(child_d, key_value_local); + v = configuration_value_type(child_d, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 999999, (int) value_to_int(v)); + EXPECT_EQ((int)999999, (int)value_to_int(v)); } configuration_destroy(); diff --git a/source/tests/configuration_test/source/main.cpp b/source/tests/configuration_test/source/main.cpp index 625e705654..4b3063b2f5 100644 --- a/source/tests/configuration_test/source/main.cpp +++ b/source/tests/configuration_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/cs_loader_test/source/cs_loader_test.cpp b/source/tests/cs_loader_test/source/cs_loader_test.cpp deleted file mode 100644 index a1b48c5f87..0000000000 --- a/source/tests/cs_loader_test/source/cs_loader_test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for dynamic loading and linking shared objects at run-time. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -#include - -class cs_loader_test : public testing::Test -{ -protected: -}; - -TEST_F(cs_loader_test, SayHello) -{ - ASSERT_NE((void *) NULL, (void *) metacall_function("SayHello")); - - metacall("SayHello"); -} - -TEST_F(cs_loader_test, SayAny) -{ - ASSERT_NE((void *) NULL, (void *) metacall_function("Say")); - - metacall("Say", "Any"); -} - -TEST_F(cs_loader_test, Jump) -{ - value ret = NULL; - - ASSERT_NE((void *) NULL, (void *) metacall_function("SuperJump")); - - ret = metacall("SuperJump"); - - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((int) 2, (int) metacall_value_to_int(ret)); - - value_destroy(ret); -} - -TEST_F(cs_loader_test, Sum) -{ - value ret = NULL; - - ASSERT_NE((void *) NULL, (void *) metacall_function("Sum")); - - ret = metacall("Sum", 5, 10); - - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((int) 15, (int) metacall_value_to_int(ret)); - - value_destroy(ret); -} - -TEST_F(cs_loader_test, Concat) -{ - value ret = NULL; - - ASSERT_NE((void *) NULL, (void *) metacall_function("Concat")); - - ret = metacall("Concat", "Hello ", "World"); - - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((int) 0, (int) strcmp((const char *)metacall_value_to_string(ret), "Hello World")); - - metacall_value_destroy(ret); -} diff --git a/source/tests/detour_test/CMakeLists.txt b/source/tests/detour_test/CMakeLists.txt index ae3e46a95d..06e3ef9794 100644 --- a/source/tests/detour_test/CMakeLists.txt +++ b/source/tests/detour_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if detours are enabled -if(NOT OPTION_FORK_SAFE OR NOT OPTION_BUILD_DETOURS OR NOT OPTION_BUILD_DETOURS_FUNCHOOK) +if(NOT OPTION_BUILD_DETOURS OR NOT OPTION_BUILD_DETOURS_PLTHOOK) return() endif() @@ -89,10 +89,13 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::memory + ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::adt ${META_PROJECT_NAME}::dynlink + ${META_PROJECT_NAME}::plugin ${META_PROJECT_NAME}::detour ) @@ -114,11 +117,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,6 +143,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + plthook_detour +) + # # Define test properties # diff --git a/source/tests/detour_test/source/detour_test.cpp b/source/tests/detour_test/source/detour_test.cpp index ed7ba31ade..4882867e83 100644 --- a/source/tests/detour_test/source/detour_test.cpp +++ b/source/tests/detour_test/source/detour_test.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,75 +18,146 @@ * */ -#include +#include #include #include +#include + #include class detour_test : public testing::Test { - public: +public: }; -static detour_handle handle; +static detour_handle handle = NULL; +static const char *(*trampoline)(void) = NULL; -int hook_function(int x) +int check_detour_hook(const char *(*fp)(void)) { - EXPECT_EQ((int) 2, (int) x); + static const char str_without_hook[] = "Detour Library"; - log_write("metacall", LOG_LEVEL_DEBUG, "Hook function %d", x); + const char *str = fp(); - int (*target_function_ptr)(int) = (int(*)(int))detour_trampoline(handle); + log_write("metacall", LOG_LEVEL_DEBUG, "Check: %s", str); - return target_function_ptr(x + 4) + 2; + return strncmp(str, str_without_hook, sizeof(str_without_hook) - 1); } -int target_function(int x) +const char *hook_function(void) { - EXPECT_EQ((int) 6, (int) x); + static const char str_with_hook[] = "Yeet"; + + log_write("metacall", LOG_LEVEL_DEBUG, "HOOK WORKING PROPERLY"); + log_write("metacall", LOG_LEVEL_DEBUG, "Original function: %s", trampoline()); + + /* Here we check that we got the correct trampoline implementation (aka the original function) + and we can call it from inside of the body of the hook function */ + EXPECT_EQ((int)0, (int)check_detour_hook(trampoline)); + return str_with_hook; +} + +/* TODO: +* This test is not going to work because detour_enumeration does not walk in +* the following sections: +* T Global text symbol +* t Local text symbol +* This funtion we are searching for is stored in: +* 0000000000073630 T test_exported_symbols_from_executable +* 00000000000736e0 t _Z13hook_functionv +* 0000000000072e34 t _Z13hook_functionv.cold +* 0000000000073680 t _Z17check_detour_hookPFPKcvE +* We can find all the sections here: https://en.wikipedia.org/wiki/Nm_(Unix) +* For listing properly all the symbols we should replicate something like +* GNU libc does under the hood for dlsym, which is implemented through do_lookup: +* https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=elf/dl-lookup.c;hb=HEAD +* We will leave this for future versions, including support for GNU hashed symbols. +*/ +#define TODO_TEST_EXPORTED_SYMBOLS_FROM_EXECUTABLE 1 + +#ifdef _WIN32 + #define EXPORT_SYMBOL __declspec(dllexport) +#else + #define EXPORT_SYMBOL __attribute__((visibility("default"))) +#endif + +extern "C" EXPORT_SYMBOL int test_exported_symbols_from_executable(int x) +{ log_write("metacall", LOG_LEVEL_DEBUG, "Target function %d", x); - return 4; + return x; } TEST_F(detour_test, DefaultConstructor) { - static const char name[] = "funchook"; + static const char name[] = "plthook"; /* Initialize log */ - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); /* Initialize detour */ - EXPECT_EQ((int) 0, (int) detour_initialize()); + EXPECT_EQ((int)0, (int)detour_initialize()); - /* Create detour funchook */ + /* Create detour plthook */ detour d = detour_create(name); - EXPECT_NE((detour) NULL, (detour) d); + ASSERT_NE((detour)NULL, (detour)d); + + EXPECT_STREQ(name, detour_name(d)); + + /* Load detour of detour library */ + handle = detour_load_file(d, NULL); + + ASSERT_NE((detour_handle)NULL, (detour_handle)handle); + + /* Check if it can list exported symbols from executable */ +#ifndef TODO_TEST_EXPORTED_SYMBOLS_FROM_EXECUTABLE + test_exported_symbols_from_executable(3); + + unsigned int position = 0; + const char *fn_name = NULL; + void (**addr)(void) = NULL; + bool found = false; + while (detour_enumerate(d, handle, &position, &fn_name, &addr) == 0) + { + log_write("metacall", LOG_LEVEL_DEBUG, "[%d] %p %s", position, *addr, fn_name); + + if (strcmp("test_exported_symbols_from_executable", fn_name) == 0) + { + found = true; + EXPECT_EQ((void *)(*addr), (void *)(&test_exported_symbols_from_executable)); + break; + } + } - EXPECT_EQ((int) 0, (int) strcmp(name, detour_name(d))); + EXPECT_EQ((bool)true, (bool)found); +#endif /* Install detour */ - handle = detour_install(d, (void(*)(void))&target_function, (void(*)(void))&hook_function); + union + { + const char *(**trampoline)(void); + void (**ptr)(void); + } cast = { &trampoline }; - EXPECT_NE((detour_handle) NULL, (detour_handle) handle); + ASSERT_EQ((int)0, detour_replace(d, handle, "detour_print_info", (void (*)(void))(&hook_function), cast.ptr)); - /* Call detour, it should call hooked function */ - EXPECT_EQ((int) 6, (int) target_function(2)); + /* This must return "Yeet", so when checking the test it should return distinct from 0, then the funtion is properly hooked */ + EXPECT_NE((int)0, (int)check_detour_hook(&detour_print_info)); /* Uninstall detour */ - EXPECT_EQ((int) 0, (int) detour_uninstall(d, handle)); + detour_unload(d, handle); /* Clear detour */ - EXPECT_EQ((int) 0, (int) detour_clear(d)); + EXPECT_EQ((int)0, (int)detour_clear(d)); /* Destroy detour */ detour_destroy(); diff --git a/source/tests/detour_test/source/main.cpp b/source/tests/detour_test/source/main.cpp index 41de036517..ccd0ee35a6 100644 --- a/source/tests/detour_test/source/main.cpp +++ b/source/tests/detour_test/source/main.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/dynlink_test/CMakeLists.txt b/source/tests/dynlink_test/CMakeLists.txt index 2ae5afddd8..ae0eabeeea 100644 --- a/source/tests/dynlink_test/CMakeLists.txt +++ b/source/tests/dynlink_test/CMakeLists.txt @@ -84,7 +84,9 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::dynlink ) @@ -95,6 +97,8 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + + $<$,$>:DYNLINK_TEST_MOCK_LOADER> ) # @@ -106,13 +110,25 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} + + $<$,$>:-Wl,-export_dynamic> + $<$:-rdynamic> ) # @@ -123,6 +139,16 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_MOCK) + add_dependencies(${target} + mock_loader + ) +endif() + # # Define test labels # diff --git a/source/tests/dynlink_test/source/dynlink_test.cpp b/source/tests/dynlink_test/source/dynlink_test.cpp index 5ee9aa26c2..0119510cab 100644 --- a/source/tests/dynlink_test/source/dynlink_test.cpp +++ b/source/tests/dynlink_test/source/dynlink_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -28,65 +28,154 @@ #define DYNLINK_TEST_LIBRARY_PATH "DYNLINK_TEST_LIBRARY_PATH" -typedef void (*mock_loader_print_func)(void); +typedef const char *(*mock_loader_print_func)(void); class dynlink_test : public testing::Test { - protected: +protected: }; +#ifdef _WIN32 + #define EXPORT_SYMBOL __declspec(dllexport) +#else + #define EXPORT_SYMBOL __attribute__((visibility("default"))) +#endif + +extern "C" EXPORT_SYMBOL int function_from_current_executable(void) +{ + log_write("metacall", LOG_LEVEL_INFO, "function_from_current_executable"); + + return 48; +} + TEST_F(dynlink_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); dynlink_print_info(); log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object extension: %s", dynlink_extension()); + /* Test loading symbols from current process */ { - #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) - const char library_name[] = "mock_loaderd"; - #else - const char library_name[] = "mock_loader"; - #endif + dynlink proc = dynlink_load_self(DYNLINK_FLAGS_BIND_LAZY | DYNLINK_FLAGS_BIND_GLOBAL); - char * path = environment_variable_path_create(DYNLINK_TEST_LIBRARY_PATH, NULL); + ASSERT_NE((dynlink)proc, (dynlink)(NULL)); - dynlink handle = dynlink_load(path, library_name, DYNLINK_FLAGS_BIND_NOW | DYNLINK_FLAGS_BIND_GLOBAL); + dynlink_symbol_addr addr; - environment_variable_path_destroy(path); + EXPECT_EQ((int)0, dynlink_symbol(proc, "function_from_current_executable", &addr)); + + ASSERT_NE((dynlink_symbol_addr)addr, (dynlink_symbol_addr)NULL); + + int (*fn_ptr)(void) = (int (*)(void))addr; + + EXPECT_EQ((int)48, fn_ptr()); + + EXPECT_EQ((int (*)(void))(&function_from_current_executable), (int (*)(void))fn_ptr); + + dynlink_unload(proc); /* Should do nothing except by freeing the handle */ + } + +#ifdef DYNLINK_TEST_MOCK_LOADER + { + #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) + const char library_name[] = "mock_loaderd"; + #else + const char library_name[] = "mock_loader"; + #endif - EXPECT_NE(handle, (dynlink) NULL); + char *path = environment_variable_path_create(DYNLINK_TEST_LIBRARY_PATH, NULL, 0, NULL); - log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file: %s", dynlink_get_name_impl(handle)); + ASSERT_NE((char *)path, (char *)NULL); - if (handle != NULL) + /* Test library loading */ { - static dynlink_symbol_addr mock_loader_print_info_addr; + dynlink handle = dynlink_load(path, library_name, DYNLINK_FLAGS_BIND_NOW | DYNLINK_FLAGS_BIND_GLOBAL); - EXPECT_EQ((int) 0, dynlink_symbol(handle, DYNLINK_SYMBOL_STR("mock_loader_print_info"), &mock_loader_print_info_addr)); + ASSERT_NE(handle, (dynlink)NULL); - if (mock_loader_print_info_addr != NULL) - { - mock_loader_print_func print = DYNLINK_SYMBOL_GET(mock_loader_print_info_addr); + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file: %s", dynlink_get_path(handle)); + + EXPECT_STREQ(library_name, dynlink_get_name(handle)); - log_write("metacall", LOG_LEVEL_DEBUG, "Print function: %p", (void *)print); + if (handle != NULL) + { + dynlink_symbol_addr mock_loader_print_info_addr; - log_write("metacall", LOG_LEVEL_DEBUG, "Symbol pointer: %p", (void *)mock_loader_print_info_addr); + EXPECT_EQ((int)0, dynlink_symbol(handle, "mock_loader_print_info", &mock_loader_print_info_addr)); - if (DYNLINK_SYMBOL_GET(mock_loader_print_info_addr) != NULL) + if (mock_loader_print_info_addr != NULL) { - log_write("metacall", LOG_LEVEL_DEBUG, "Pointer is valid"); + mock_loader_print_func print = (mock_loader_print_func)mock_loader_print_info_addr; + + log_write("metacall", LOG_LEVEL_DEBUG, "Print function: %p", (void *)print); + + log_write("metacall", LOG_LEVEL_DEBUG, "Symbol pointer: %p", (void *)mock_loader_print_info_addr); + + if (mock_loader_print_info_addr != NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "Pointer is valid"); + } + + log_write("metacall", LOG_LEVEL_DEBUG, "Print: %s", print()); } - print(); + dynlink_unload(handle); } + } - dynlink_unload(handle); + /* Test loading symbols from absolute path */ + { + char library_name_platform[PORTABILITY_PATH_SIZE]; + char absolute_path[PORTABILITY_PATH_SIZE]; + + dynlink_platform_name(library_name, library_name_platform); + + portability_path_join(path, strlen(path) + 1, library_name_platform, strlen(library_name_platform) + 1, absolute_path, PORTABILITY_PATH_SIZE); + + dynlink handle = dynlink_load_absolute(absolute_path, DYNLINK_FLAGS_BIND_NOW | DYNLINK_FLAGS_BIND_GLOBAL); + + ASSERT_NE(handle, (dynlink)NULL); + + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object absolute path: %s", absolute_path); + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file name: %s", dynlink_get_path(handle)); + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file: %s", dynlink_get_name(handle)); + + EXPECT_STREQ(absolute_path, dynlink_get_path(handle)); + EXPECT_STREQ(library_name, dynlink_get_name(handle)); + + if (handle != NULL) + { + dynlink_symbol_addr mock_loader_print_info_addr; + + EXPECT_EQ((int)0, dynlink_symbol(handle, "mock_loader_print_info", &mock_loader_print_info_addr)); + + if (mock_loader_print_info_addr != NULL) + { + mock_loader_print_func print = (mock_loader_print_func)mock_loader_print_info_addr; + + log_write("metacall", LOG_LEVEL_DEBUG, "Print function: %p", (void *)print); + + log_write("metacall", LOG_LEVEL_DEBUG, "Symbol pointer: %p", (void *)mock_loader_print_info_addr); + + if (mock_loader_print_info_addr != NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "Pointer is valid"); + } + + log_write("metacall", LOG_LEVEL_DEBUG, "Print: %s", print()); + } + + dynlink_unload(handle); + } } + + environment_variable_path_destroy(path); } +#endif } diff --git a/source/tests/dynlink_test/source/main.cpp b/source/tests/dynlink_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/dynlink_test/source/main.cpp +++ b/source/tests/dynlink_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/environment_test/CMakeLists.txt b/source/tests/environment_test/CMakeLists.txt index a37700c27b..3472e06a8b 100644 --- a/source/tests/environment_test/CMakeLists.txt +++ b/source/tests/environment_test/CMakeLists.txt @@ -81,6 +81,7 @@ target_link_libraries(${target} GTest ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ) @@ -102,11 +103,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/environment_test/source/environment_test.cpp b/source/tests/environment_test/source/environment_test.cpp index be964b4e2b..576dfb28aa 100644 --- a/source/tests/environment_test/source/environment_test.cpp +++ b/source/tests/environment_test/source/environment_test.cpp @@ -2,7 +2,7 @@ * Format Library by Parra Studios * A cross-platform library for supporting platform specific environment features. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -27,19 +27,19 @@ class environment_test : public testing::Test public: }; -#define ENVIRONMENT_TEST_VARIABLE_TEXT "ENV_TEXT" -#define ENVIRONMENT_TEST_VARIABLE_PATH "ENV_PATH" +#define ENVIRONMENT_TEST_VARIABLE_TEXT "ENV_TEXT" +#define ENVIRONMENT_TEST_VARIABLE_PATH "ENV_PATH" #define ENVIRONMENT_TEST_VARIABLE_PATH_SANITIZED "ENV_SAN" TEST_F(environment_test, variable_text) { static const char variable_text_name[] = ENVIRONMENT_TEST_VARIABLE_TEXT; - char * variable_text = environment_variable_create(variable_text_name, "default"); + char *variable_text = environment_variable_create(variable_text_name, "default"); - ASSERT_NE((const char *) NULL, (const char *) variable_text); + ASSERT_NE((const char *)NULL, (const char *)variable_text); - EXPECT_EQ((int) 0, (int) strcmp(variable_text, "abcd")); + EXPECT_STREQ(variable_text, "abcd"); environment_variable_destroy(variable_text); } @@ -48,11 +48,11 @@ TEST_F(environment_test, variable_text_default) { static const char variable_text_name[] = "UNKNOWN"; - char * variable_text = environment_variable_create(variable_text_name, "default"); + char *variable_text = environment_variable_create(variable_text_name, "default"); - ASSERT_NE((const char *) NULL, (const char *) variable_text); + ASSERT_NE((const char *)NULL, (const char *)variable_text); - EXPECT_EQ((int) 0, (int) strcmp(variable_text, "default")); + EXPECT_STREQ(variable_text, "default"); environment_variable_destroy(variable_text); } @@ -61,20 +61,20 @@ TEST_F(environment_test, variable_static) { static const char variable_text_name[] = ENVIRONMENT_TEST_VARIABLE_TEXT; - const char * variable_text_static = environment_variable_get(variable_text_name, "default"); + const char *variable_text_static = environment_variable_get(variable_text_name, "default"); - EXPECT_EQ((int) 0, (int) strcmp(variable_text_static, "abcd")); + EXPECT_STREQ(variable_text_static, "abcd"); } TEST_F(environment_test, variable_path) { static const char variable_path_name[] = ENVIRONMENT_TEST_VARIABLE_PATH; - char * variable_path = environment_variable_path_create(variable_path_name, "default_path"); + char *variable_path = environment_variable_path_create(variable_path_name, "default_path", sizeof("default_path"), NULL); - ASSERT_NE((const char *) NULL, (const char *) variable_path); - - EXPECT_EQ((int) 0, (int) strcmp(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR)); + ASSERT_NE((const char *)NULL, (const char *)variable_path); + + EXPECT_STREQ(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR); environment_variable_path_destroy(variable_path); } @@ -83,25 +83,24 @@ TEST_F(environment_test, variable_path_default) { static const char variable_path_name[] = "UNKNOWN"; - char * variable_path = environment_variable_path_create(variable_path_name, "default_path"); + char *variable_path = environment_variable_path_create(variable_path_name, "default_path", sizeof("default_path"), NULL); - ASSERT_NE((const char *) NULL, (const char *) variable_path); + ASSERT_NE((const char *)NULL, (const char *)variable_path); - EXPECT_EQ((int) 0, (int) strcmp(variable_path, "default_path" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR)); + EXPECT_STREQ(variable_path, "default_path" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR); environment_variable_path_destroy(variable_path); } - TEST_F(environment_test, variable_path_sanitized) { static const char variable_path_name[] = ENVIRONMENT_TEST_VARIABLE_PATH_SANITIZED; - char * variable_path = environment_variable_path_create(variable_path_name, "default_path"); + char *variable_path = environment_variable_path_create(variable_path_name, "default_path", sizeof("default_path"), NULL); - ASSERT_NE((const char *) NULL, (const char *) variable_path); + ASSERT_NE((const char *)NULL, (const char *)variable_path); - EXPECT_EQ((int) 0, (int) strcmp(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR)); + EXPECT_STREQ(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR); environment_variable_path_destroy(variable_path); } diff --git a/source/tests/environment_test/source/main.cpp b/source/tests/environment_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/environment_test/source/main.cpp +++ b/source/tests/environment_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/file_loader_test/source/file_loader_test.cpp b/source/tests/file_loader_test/source/file_loader_test.cpp deleted file mode 100644 index 82921b278b..0000000000 --- a/source/tests/file_loader_test/source/file_loader_test.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -#include - -class file_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(file_loader_test, DefaultConstructor) -{ - const loader_naming_tag tag = "file"; - - const loader_naming_path scripts[] = - { - "favicon.ico", - "a/a.txt" - }; - - const size_t size = sizeof(scripts) / sizeof(scripts[0]); - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) loader_load_from_file(tag, scripts, size, NULL)); - - void * handle = loader_get_handle(tag, "favicon"); - - EXPECT_NE((void *) NULL, (void *) handle); - - EXPECT_EQ((int) 0, (int) loader_clear(handle)); - - EXPECT_EQ((int) 0, (int) loader_unload()); -} diff --git a/source/tests/loader_path_test/source/loader_path_test.cpp b/source/tests/loader_path_test/source/loader_path_test.cpp deleted file mode 100644 index df1232368d..0000000000 --- a/source/tests/loader_path_test/source/loader_path_test.cpp +++ /dev/null @@ -1,462 +0,0 @@ -/* -* Loader Library by Parra Studios -* Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -* -* A library for loading executable code at run-time into a process. -* -*/ - -#include - -#include - -#include - -class loader_path_test : public testing::Test -{ - public: -}; - -TEST_F(loader_path_test, loader_path_test_get_path_of_path) -{ - const char base[] = "/a/b/c/"; - const char result[] = "/a/b/c/"; - - loader_naming_path path; - - size_t size = loader_path_get_path(base, sizeof(base), path); - - EXPECT_EQ((int) 0, (int) strcmp(path, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_get_path_of_filepath) -{ - const char base[] = "/a/b/c/asd"; - const char result[] = "/a/b/c/"; - - loader_naming_path path; - - size_t size = loader_path_get_path(base, sizeof(base), path); - - EXPECT_EQ((int) 0, (int) strcmp(path, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_get_relative) -{ - const char base[] = "/a/b/c/"; - const char path[] = "/a/b/c/abc"; - const char result[] = "abc"; - - loader_naming_path relative; - - size_t size = loader_path_get_relative(base, path, relative); - - EXPECT_EQ((int) 0, (int) strcmp(relative, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_get_relative_fail) -{ - const char base[] = "/this/is/not/shared/with/path"; - const char path[] = "/a/b/c/abc"; - const char result[] = "a/b/c/abc"; - - loader_naming_path relative; - - size_t size = loader_path_get_relative(base, path, relative); - - EXPECT_EQ((int) 0, (int) strcmp(relative, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_none_slash) -{ - const char left[] = "/a/b/c"; - const char right[] = "e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_left_slash) -{ - const char left[] = "/a/b/c/"; - const char right[] = "e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_right_slash) -{ - const char left[] = "/a/b/c"; - const char right[] = "/e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_both_slash) -{ - const char left[] = "/a/b/c/"; - const char right[] = "/e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_left_empty) -{ - const char left[] = ""; - const char right[] = "/e/f/g/"; - const char result[] = "/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_right_empty) -{ - const char left[] = "/a/b/c/"; - const char right[] = ""; - const char result[] = "/a/b/c/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_right_empty_non_slash) -{ - const char left[] = "/a/b/c"; - const char right[] = ""; - const char result[] = "/a/b/c/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_both_empty) -{ - const char left[] = ""; - const char right[] = ""; - const char result[] = ""; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_dot) -{ - const char path[] = "./a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_double_dot) -{ - const char path[] = "../a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_many_dot) -{ - const char path[] = "./././././a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_many_double_dot) -{ - const char path[] = "../../../../../a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_dot_non_slash) -{ - const char path[] = ".a/b/c"; - const char result[] = ".a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_many_dot_non_slash) -{ - const char path[] = "..a/b/c"; - const char result[] = "..a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_invalid) -{ - const char path[] = "..././.../...../a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_double_dot) -{ - const char path[] = "../a/b/../c"; - const char result[] = "a/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_double_dot_all) -{ - const char path[] = "../a/b/../../c"; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_double_dot_break) -{ - const char path[] = "../a/b/../../../c"; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_dot) -{ - const char path[] = "../a/./././b/./././c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_mixed_dot) -{ - const char path[] = "../a/./././../b/././.././../c"; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_end_dot) -{ - const char path[] = "../a/./././../b/././.././../c/."; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_end_double_dot) -{ - const char path[] = "../a/./././b/././../c/.."; - const char result[] = "a"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_end_mixed_dot) -{ - const char path[] = "../a/b/c/.././../."; - const char result[] = "a"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_absolute_end_mixed_dot) -{ - const char path[] = "/a/b/c/../../../."; - const char result[] = "/"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_absolute_end_dot) -{ - const char path[] = "/."; - const char result[] = "/"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_relative_begin_end_dot) -{ - const char path[] = "./."; - const char result[] = "."; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_absolute_end_many_dot) -{ - const char path[] = "/./././"; - const char result[] = "/"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} diff --git a/source/tests/log_custom_test/CMakeLists.txt b/source/tests/log_custom_test/CMakeLists.txt index 153dde7012..f46ff859e2 100644 --- a/source/tests/log_custom_test/CMakeLists.txt +++ b/source/tests/log_custom_test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ) @@ -104,11 +105,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/log_custom_test/source/log_custom_test.cpp b/source/tests/log_custom_test/source/log_custom_test.cpp index 99204c7039..f97ebcc6b6 100644 --- a/source/tests/log_custom_test/source/log_custom_test.cpp +++ b/source/tests/log_custom_test/source/log_custom_test.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,21 +18,21 @@ * */ -#include +#include #include #include -#include #include +#include -static const char format[] = "%.19s #%" PRIuS " %s:%" PRIuS " %s @%s "; +static const char format[] = "%.19s #%" PRIu64 " %s:%" PRIuS " %s @%s "; class log_custom_test : public testing::Test { - public: +public: }; -size_t format_size(void * context, const char * time, size_t thread_id, size_t line, const char * func, const char * file, const char * level, const char * message, log_policy_format_custom_va_list args) +size_t format_size(void *context, const char *time, uint64_t id, size_t line, const char *func, const char *file, const char *level, const char *message, log_policy_format_custom_va_list args) { size_t length = 0; @@ -53,13 +53,13 @@ size_t format_size(void * context, const char * time, size_t thread_id, size_t l length = strlen(message); } - return snprintf(NULL, 0, format, time, thread_id, file, line, func, level) + length + 1; + return snprintf(NULL, 0, format, time, id, file, line, func, level) + length + 1; } -size_t format_serialize(void * context, void * buffer, const size_t size, const char * time, size_t thread_id, size_t line, const char * func, const char * file, const char * level, const char * message, log_policy_format_custom_va_list args) +size_t format_serialize(void *context, void *buffer, const size_t size, const char *time, uint64_t id, size_t line, const char *func, const char *file, const char *level, const char *message, log_policy_format_custom_va_list args) { - size_t length = snprintf((char *)buffer, size, format, time, thread_id, file, line, func, level); - char * body = &(((char *)buffer)[length]); + size_t length = snprintf((char *)buffer, size, format, time, id, file, line, func, level); + char *body = &(((char *)buffer)[length]); (void)context; @@ -75,19 +75,19 @@ size_t format_serialize(void * context, void * buffer, const size_t size, const } else { - length += snprintf(body, size - length, message); + length += snprintf(body, size - length, "%s", message); } return length + 1; } -size_t format_deserialize(void * context, const void * buffer, const size_t size, const char * time, size_t thread_id, size_t line, const char * func, const char * file, const char * level, const char * message, log_policy_format_custom_va_list args) +size_t format_deserialize(void *context, const void *buffer, const size_t size, const char *time, uint64_t id, size_t line, const char *func, const char *file, const char *level, const char *message, log_policy_format_custom_va_list args) { /* TODO */ (void)context; (void)buffer; (void)time; - (void)thread_id; + (void)id; (void)line; (void)func; (void)file; @@ -98,7 +98,7 @@ size_t format_deserialize(void * context, const void * buffer, const size_t size return size; } -int stream_flush(void * context) +int stream_flush(void *context) { (void)context; @@ -107,7 +107,7 @@ int stream_flush(void * context) return 0; } -int stream_write(void * context, const char * buffer, const size_t size) +int stream_write(void *context, const char *buffer, const size_t size) { (void)context; (void)size; @@ -122,21 +122,21 @@ TEST_F(log_custom_test, DefaultConstructor) const char name[] = "custom_log"; /* Create logs */ - EXPECT_EQ((int) 0, (int) log_create(name)); + EXPECT_EQ((int)0, (int)log_create(name)); /* Set policies */ - EXPECT_EQ((int) 0, (int) log_configure(name, - log_policy_format_custom(NULL, &format_size, &format_serialize, &format_deserialize), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_custom(NULL, &stream_write, &stream_flush))); + EXPECT_EQ((int)0, (int)log_configure(name, + log_policy_format_custom(NULL, &format_size, &format_serialize, &format_deserialize), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_custom(NULL, &stream_write, &stream_flush))); /* Write simple logs */ - EXPECT_EQ((int) 0, (int) log_write(name, LOG_LEVEL_INFO, "hello world")); + EXPECT_EQ((int)0, (int)log_write(name, LOG_LEVEL_INFO, "hello world")); /* Write varidic log */ - EXPECT_EQ((int) 0, (int) log_write(name, LOG_LEVEL_INFO, "hello world from log %d", 20)); + EXPECT_EQ((int)0, (int)log_write(name, LOG_LEVEL_INFO, "hello world from log %d", 20)); /* Clear log */ - EXPECT_EQ((int) 0, (int) log_clear(name)); + EXPECT_EQ((int)0, (int)log_clear(name)); } diff --git a/source/tests/log_custom_test/source/main.cpp b/source/tests/log_custom_test/source/main.cpp index 41de036517..ccd0ee35a6 100644 --- a/source/tests/log_custom_test/source/main.cpp +++ b/source/tests/log_custom_test/source/main.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/log_test/CMakeLists.txt b/source/tests/log_test/CMakeLists.txt index 83747b931c..d091de149f 100644 --- a/source/tests/log_test/CMakeLists.txt +++ b/source/tests/log_test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ) @@ -104,11 +105,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/log_test/source/log_test.cpp b/source/tests/log_test/source/log_test.cpp index ce683bd7ee..3e1121bc7a 100644 --- a/source/tests/log_test/source/log_test.cpp +++ b/source/tests/log_test/source/log_test.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,37 +18,35 @@ * */ -#include +#include #include -#include #include +#include #include class log_test : public testing::Test { - public: +public: }; TEST_F(log_test, DefaultConstructor) { struct log_name_list_type { - const char * name; + const char *name; unsigned int value; - } - log_name_list[] = - { - { "test_log_text_sync_seq_stdout", 0 }, - { "test_log_text_sync_seq_stderr", 1 }, - { "test_log_text_sync_seq_file", 2 }, - { "test_log_text_sync_seq_syslog", 3 }, - { "test_log_text_sync_seq_socket", 4 }, - { "test_log_bin_async_bat_stdout", 5 }, - { "test_log_bin_async_bat_stderr", 6 }, - { "test_log_bin_async_bat_file", 7 }, - { "test_log_bin_async_bat_syslog", 8 }, - { "test_log_bin_async_bat_socket", 9 } + } log_name_list[] = { + { "test_log_text_sync_seq_stdout", 0 }, + { "test_log_text_sync_seq_stderr", 1 }, + { "test_log_text_sync_seq_file", 2 }, + { "test_log_text_sync_seq_syslog", 3 }, + { "test_log_text_sync_seq_socket", 4 }, + { "test_log_bin_async_bat_stdout", 5 }, + { "test_log_bin_async_bat_stderr", 6 }, + { "test_log_bin_async_bat_file", 7 }, + { "test_log_bin_async_bat_syslog", 8 }, + { "test_log_bin_async_bat_socket", 9 } }; const size_t log_name_list_size = sizeof(log_name_list) / sizeof(log_name_list[0]); @@ -61,36 +59,36 @@ TEST_F(log_test, DefaultConstructor) log_map map = log_map_create((size_t)0x00000200); - EXPECT_NE((log_map) NULL, (log_map) map); + EXPECT_NE((log_map)NULL, (log_map)map); for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_map_insert(map, log_name_list[iterator].name, (const void *)&log_name_list[iterator].value)); + EXPECT_EQ((int)0, (int)log_map_insert(map, log_name_list[iterator].name, (const void *)&log_name_list[iterator].value)); } - EXPECT_EQ((size_t) log_name_list_size, (size_t) log_map_size(map)); + EXPECT_EQ((size_t)log_name_list_size, (size_t)log_map_size(map)); for (iterator = 0; iterator < log_name_list_size; ++iterator) { - const void * value_ptr = log_map_get(map, log_name_list[iterator].name); + const void *value_ptr = log_map_get(map, log_name_list[iterator].name); unsigned int value = *((unsigned int *)value_ptr); - EXPECT_EQ((unsigned int) log_name_list[iterator].value, (unsigned int) value); + EXPECT_EQ((unsigned int)log_name_list[iterator].value, (unsigned int)value); } for (map_iterator = log_map_iterator_begin(map); log_map_iterator_end(map_iterator) != 0; log_map_iterator_next(map_iterator)) { - const char * key = log_map_iterator_key(map_iterator); + const char *key = log_map_iterator_key(map_iterator); - const void * value_ptr = log_map_iterator_value(map_iterator); + const void *value_ptr = log_map_iterator_value(map_iterator); unsigned int value = *((unsigned int *)value_ptr); - EXPECT_EQ((int) 0, (int) strcmp(log_name_list[value].name, key)); + EXPECT_STREQ(log_name_list[value].name, key); } - EXPECT_EQ((int) log_map_destroy(map), (int) 0); + EXPECT_EQ((int)log_map_destroy(map), (int)0); } /* Create logs */ @@ -99,7 +97,7 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_create(log_name_list[iterator].name)); + EXPECT_EQ((int)0, (int)log_create(log_name_list[iterator].name)); } } @@ -107,66 +105,65 @@ TEST_F(log_test, DefaultConstructor) { const size_t storage_batch_size = ((size_t)0x00000010); - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[0].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[1].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stderr))); - - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[2].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_file(log_name_list[2].name, "a+"))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[3].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_syslog(log_name_list[3].name))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[4].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_socket("127.0.0.1", UINT16_C(0x0208)))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[5].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[6].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_stdio(stderr))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[7].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_file(log_name_list[7].name, "a+"))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[8].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_syslog(log_name_list[8].name))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[9].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_socket("127.0.0.1", UINT16_C(0x0209)))); + EXPECT_EQ((int)0, (int)log_configure(log_name_list[0].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[1].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stderr))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[2].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_file(log_name_list[2].name, "a+"))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[3].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_syslog(log_name_list[3].name))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[4].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_socket("127.0.0.1", UINT16_C(0x0208)))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[5].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[6].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_stdio(stderr))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[7].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_file(log_name_list[7].name, "a+"))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[8].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_syslog(log_name_list[8].name))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[9].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_socket("127.0.0.1", UINT16_C(0x0209)))); } /* Write simple logs */ @@ -175,8 +172,8 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world")); - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hell yeah")); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world")); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hell yeah")); } } @@ -186,9 +183,9 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", iterator)); - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "%" PRIuS " %f", iterator + 1, 34.02346)); - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "double log (id : %" PRIuS ")", iterator * 2)); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", iterator)); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "%" PRIuS " %f", iterator + 1, 34.02346)); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "double log (id : %" PRIuS ")", iterator * 2)); } } @@ -198,35 +195,35 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_clear(log_name_list[iterator].name)); + EXPECT_EQ((int)0, (int)log_clear(log_name_list[iterator].name)); } } /* Policy format text flags */ { - EXPECT_EQ((int) 0, (int) log_configure("newline", - log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_NEWLINE), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "NEW LINE A")); - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "NEW LINE B")); - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); - - EXPECT_EQ((int) 0, (int) log_configure("nonewline", - log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_EMPTY), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE A")); - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE B")); - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); - - EXPECT_EQ((int) 0, (int) log_clear("newline")); - EXPECT_EQ((int) 0, (int) log_clear("nonewline")); + EXPECT_EQ((int)0, (int)log_configure("newline", + log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_NEWLINE), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "NEW LINE A")); + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "NEW LINE B")); + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); + + EXPECT_EQ((int)0, (int)log_configure("nonewline", + log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_EMPTY), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE A")); + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE B")); + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); + + EXPECT_EQ((int)0, (int)log_clear("newline")); + EXPECT_EQ((int)0, (int)log_clear("nonewline")); } } diff --git a/source/tests/log_test/source/main.cpp b/source/tests/log_test/source/main.cpp index 41de036517..ccd0ee35a6 100644 --- a/source/tests/log_test/source/main.cpp +++ b/source/tests/log_test/source/main.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/memcheck/valgrind-dl.supp b/source/tests/memcheck/valgrind-dl.supp new file mode 100644 index 0000000000..706cc1bbdf --- /dev/null +++ b/source/tests/memcheck/valgrind-dl.supp @@ -0,0 +1,25 @@ +# False positives from libdl from GLIBC + +{ + Ignore dlopen allocation memory leak from GLIB. + Memcheck:Leak + fun:?alloc + ... + fun:do_dlopen +} + +{ + Ignore dlopen allocation memory leak from GLIB. + Memcheck:Leak + fun:?alloc + ... + fun:dlopen* +} + +{ + Ignore dlclose allocation memory leak from GLIB. + Memcheck:Leak + fun:malloc + ... + fun:dlclose +} diff --git a/source/tests/memcheck/valgrind-node.supp b/source/tests/memcheck/valgrind-node.supp index f31222652b..30360f9018 100644 --- a/source/tests/memcheck/valgrind-node.supp +++ b/source/tests/memcheck/valgrind-node.supp @@ -63,6 +63,32 @@ ... obj:*libnode* } +{ + + Memcheck:Leak + match-leak-kinds: reachable + fun:realloc + ... + fun:uv__tcp_connect + fun:uv_tcp_connect + ... + fun:uv__async_io + fun:uv__io_poll + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + fun:realloc + ... + fun:uv__tcp_listen + fun:uv_listen + ... + fun:uv__async_io + fun:uv__io_poll + ... +} { Memcheck:Cond @@ -104,3 +130,340 @@ fun:_ZZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler5StartEvENUlPvE_4_FUNES2_ ... } +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:allocate_dtv + fun:_dl_allocate_tls + fun:allocate_stack + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v87tracing23TracingCategoryObserver5SetUpEv + fun:_ZN4node24InitializeOncePerProcessEiPPc + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal20CodeRangeAddressHint20NotifyFreedCodeRangeEmm + fun:_ZN2v88internal15MemoryAllocator8TearDownEv + fun:_ZN2v88internal4Heap8TearDownEv + fun:_ZN2v88internal7Isolate6DeinitEv + fun:_ZN2v88internal7Isolate6DeleteEPS1_ + fun:_ZN4node16NodeMainInstanceD1Ev + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node8Metadata8Versions22InitializeIntlVersionsEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node22PerIsolatePlatformData28FlushForegroundTasksInternalEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:uv_loop_init + fun:uv_default_loop + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node22PerIsolatePlatformData8ShutdownEv + fun:_ZN4node12NodePlatform17UnregisterIsolateEPN2v87IsolateE + fun:_ZN4node16NodeMainInstanceD1Ev + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node22PerIsolatePlatformDataC1EPN2v87IsolateEP9uv_loop_s + fun:_ZN4node12NodePlatform15RegisterIsolateEPN2v87IsolateEP9uv_loop_s + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node12NodePlatform15RegisterIsolateEPN2v87IsolateEP9uv_loop_s + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal14AllocWithRetryEm + fun:_ZN2v88internal19AccountingAllocator15AllocateSegmentEm + fun:_ZN2v88internal4Zone9NewExpandEm + fun:_ZN2v88internal8compiler4Type5RangeEddPNS0_4ZoneE + fun:_ZN2v88internal8compiler9TypeCacheC1Ev + fun:_ZN2v88internal8compiler9TypeCache3GetEv + fun:_ZN2v88internal8compiler29JSNativeContextSpecializationC1EPNS1_15AdvancedReducer6EditorEPNS1_7JSGraphEPNS1_12JSHeapBrokerENS_4base5FlagsINS2_4FlagEiEEPNS1_23CompilationDependenciesEPNS0_4ZoneESH_ + fun:_ZN2v88internal8compiler12PipelineImpl11CreateGraphEv + fun:_ZN2v88internal8compiler22PipelineCompilationJob14PrepareJobImplEPNS0_7IsolateE + fun:_ZN2v88internal23OptimizedCompilationJob10PrepareJobEPNS0_7IsolateE + ... +} +{ + + Memcheck:Leak + ... + fun:napi_module_register + ... +} +{ + + Memcheck:Cond + fun:_ZNK4node11Environment16can_call_into_jsEv + fun:_ZN4node10AsyncHooks20clear_async_id_stackEv + fun:_ZN4node10AsyncHooksC1EPN2v87IsolateEPKNS0_13SerializeInfoE + ... + fun:_ZN4node16NodeMainInstance21CreateMainEnvironmentEPi + fun:_ZN4node16NodeMainInstance3RunEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal4HeapD1Ev + fun:_ZN2v88internal7IsolateD1Ev + fun:_ZN2v88internal7Isolate6DeleteEPS1_ + fun:_ZN4node16NodeMainInstanceD1Ev + fun:_ZN4node22LoadSnapshotDataAndRunEPPKNS_12SnapshotDataEPKNS_20InitializationResultE + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN6icu_7227locale_set_default_internalEPKcR10UErrorCode + fun:_ZN6icu_726Locale10getDefaultEv + fun:_ZN6icu_726Locale4initEPKca + fun:_ZN6icu_726LocaleC1Ev + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:make_unique + fun:ScheduleTimer + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + fun:_ZN2v88internal13MemoryReducer11NotifyTimerERKNS1_5EventE + fun:_ZN2v88internal13MemoryReducer9TimerTask11RunInternalEv + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskESt10unique_ptrIN2v84TaskESt14default_deleteIS3_EE + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskEP10uv_timer_s + fun:uv__run_timers + fun:uv_run + fun:_ZN4node13SpinEventLoopEPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEPiPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEv + fun:_ZN4node22LoadSnapshotDataAndRunEPPKNS_12SnapshotDataEPKNS_20InitializationResultE + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:_ZN4node22PerIsolatePlatformData15PostDelayedTaskESt10unique_ptrIN2v84TaskESt14default_deleteIS3_EEd + fun:ScheduleTimer + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + fun:_ZN2v88internal13MemoryReducer11NotifyTimerERKNS1_5EventE + fun:_ZN2v88internal13MemoryReducer9TimerTask11RunInternalEv + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskESt10unique_ptrIN2v84TaskESt14default_deleteIS3_EE + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskEP10uv_timer_s + fun:uv__run_timers + fun:uv_run + fun:_ZN4node13SpinEventLoopEPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEPiPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node9TaskQueueINS_11DelayedTaskEE6PopAllEv + fun:_ZN4node22PerIsolatePlatformData8ShutdownEv + fun:_ZN4node12NodePlatform17UnregisterIsolateEPN2v87IsolateE + fun:_ZN4node16NodeMainInstanceD1Ev + fun:_ZN4node22LoadSnapshotDataAndRunEPPKNS_12SnapshotDataEPKNS_20InitializationResultE + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node9TaskQueueIN2v84TaskEE6PopAllEv + fun:_ZN4node22PerIsolatePlatformData8ShutdownEv + fun:_ZN4node12NodePlatform17UnregisterIsolateEPN2v87IsolateE + fun:_ZN4node16NodeMainInstanceD1Ev + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal12_GLOBAL__N_133InitializeSharedReadOnlyArtifactsEv + fun:_ZN2v88internal12ReadOnlyHeap5SetUpEPNS0_7IsolateEPNS0_12SnapshotDataEb + fun:_ZN2v88internal7Isolate4InitEPNS0_12SnapshotDataES3_S3_b + fun:Initialize + fun:_ZN2v88internal8Snapshot10InitializeEPNS0_7IsolateE + fun:_ZN2v87Isolate10InitializeEPS0_RKNS0_12CreateParamsE + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:uprv_realloc_72 + fun:_ZN6icu_7210UnicodeSet7compactEv + fun:_ZN6icu_7210UnicodeSet6freezeEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:uprv_malloc_72 + fun:_ZN6icu_727UMemorynwEm + fun:_ZN6icu_7210UnicodeSet6freezeEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal14AllocWithRetryEmPFPvmE + fun:_ZN2v88internal19AccountingAllocator15AllocateSegmentEmb + fun:_ZN2v88internal4Zone9NewExpandEm + fun:Allocate + fun:New + fun:New + fun:New + fun:_ZN2v88internal8compiler4Type5RangeEddPNS0_4ZoneE + fun:CreateRange + fun:CreateRange + fun:_ZN2v88internal8compiler9TypeCacheC1Ev + fun:LeakyObject<> + fun:_ZN2v88internal8compiler9TypeCache3GetEv + fun:_ZN2v88internal8compiler29JSNativeContextSpecializationC1EPNS1_15AdvancedReducer6EditorEPNS1_7JSGraphEPNS1_12JSHeapBrokerENS_4base5FlagsINS2_4FlagEiEEPNS1_23CompilationDependenciesEPNS0_4ZoneESH_ + fun:_ZN2v88internal8compiler13InliningPhase3RunEPNS1_12PipelineDataEPNS0_4ZoneE.isra.0 + fun:Run + fun:_ZN2v88internal8compiler12PipelineImpl11CreateGraphEv + fun:_ZN2v88internal8compiler22PipelineCompilationJob14ExecuteJobImplEPNS0_16RuntimeCallStatsEPNS0_12LocalIsolateE + fun:_ZN2v88internal23OptimizedCompilationJob10ExecuteJobEPNS0_16RuntimeCallStatsEPNS0_12LocalIsolateE + fun:_ZN2v88internal27OptimizingCompileDispatcher11CompileNextEPNS0_22TurbofanCompilationJobEPNS0_12LocalIsolateE + fun:_ZThn32_N2v88internal14CancelableTask3RunEv + fun:_ZN4node12_GLOBAL__N_1L20PlatformWorkerThreadEPv + ... +} +{ + + Helgrind:Race + ... + fun:store + fun:atomic_store_explicit + fun:Release_Store + fun:GetCategoryGroupEnabled + fun:_ZN2v88platform7tracing17TracingController23GetCategoryGroupEnabledEPKc + ... +} +{ + + Helgrind:Race + ... + fun:uv_loop_close + fun:_ZN4node18CheckedUvLoopCloseEP9uv_loop_s + fun:_ZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler3RunEv + fun:_ZZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler5StartEvENKUlPvE_clES2_ + fun:_ZZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler5StartEvENUlPvE_4_FUNES2_ + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:ScheduleTimer + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + fun:_ZN2v88internal4Heap28NotifyOldGenerationExpansionENS0_15AllocationSpaceEPNS0_11MemoryChunkE + fun:_ZN2v88internal10PagedSpace9TryExpandEiNS0_16AllocationOriginE + fun:_ZN2v88internal10PagedSpace16RawRefillLabMainEiNS0_16AllocationOriginE + fun:_ZN2v88internal10PagedSpace13RefillLabMainEiNS0_16AllocationOriginE + fun:_ZN2v88internal19SpaceWithLinearArea20AllocateRawUnalignedEiNS0_16AllocationOriginE + fun:AllocateRawSlow + fun:AllocateRaw + fun:_ZN2v88internal13HeapAllocator11AllocateRawILNS0_14AllocationTypeE1EEENS0_16AllocationResultEiNS0_16AllocationOriginENS0_19AllocationAlignmentE + fun:AllocateRawWith<(v8::internal::HeapAllocator::AllocationRetryMode)1> + fun:_ZN2v88internal7Factory11AllocateRawEiNS0_14AllocationTypeENS0_19AllocationAlignmentE + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE26AllocateRawWithImmortalMapEiNS0_14AllocationTypeENS0_3MapENS0_19AllocationAlignmentE + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE21NewSharedFunctionInfoEv + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE21NewSharedFunctionInfoENS0_11MaybeHandleINS0_6StringEEENS4_INS0_10HeapObjectEEENS0_7BuiltinENS0_12FunctionKindE + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE31NewSharedFunctionInfoForLiteralEPNS0_15FunctionLiteralENS0_6HandleINS0_6ScriptEEEb + fun:_ZN2v88internal11interpreter17BytecodeGenerator25AllocateDeferredConstantsINS0_7IsolateEEEvPT_NS0_6HandleINS0_6ScriptEEE + fun:_ZN2v88internal11interpreter17BytecodeGenerator16FinalizeBytecodeINS0_7IsolateEEENS0_6HandleINS0_13BytecodeArrayEEEPT_NS5_INS0_6ScriptEEE + fun:_ZN2v88internal11interpreter25InterpreterCompilationJob17DoFinalizeJobImplINS0_7IsolateEEENS0_14CompilationJob6StatusENS0_6HandleINS0_18SharedFunctionInfoEEEPT_ + fun:_ZN2v88internal11interpreter25InterpreterCompilationJob15FinalizeJobImplENS0_6HandleINS0_18SharedFunctionInfoEEEPNS0_7IsolateE + fun:_ZN2v88internal25UnoptimizedCompilationJob11FinalizeJobENS0_6HandleINS0_18SharedFunctionInfoEEEPNS0_7IsolateE + fun:_ZN2v88internal12_GLOBAL__N_139FinalizeSingleUnoptimizedCompilationJobINS0_7IsolateEEENS0_14CompilationJob6StatusEPNS0_25UnoptimizedCompilationJobENS0_6HandleINS0_18SharedFunctionInfoEEEPT_PSt6vectorINS0_34FinalizeUnoptimizedCompilationDataESaISE_EE + fun:_ZN2v88internal12_GLOBAL__N_155IterativelyExecuteAndFinalizeUnoptimizedCompilationJobsINS0_7IsolateEEEbPT_NS0_6HandleINS0_18SharedFunctionInfoEEENS6_INS0_6ScriptEEEPNS0_9ParseInfoEPNS0_19AccountingAllocatorEPNS0_15IsCompiledScopeEPSt6vectorINS0_34FinalizeUnoptimizedCompilationDataESaISI_EEPSH_INS0_27DeferredFinalizationJobDataESaISM_EE.constprop.0 + fun:_ZN2v88internal12_GLOBAL__N_115CompileToplevelEPNS0_9ParseInfoENS0_6HandleINS0_6ScriptEEENS0_11MaybeHandleINS0_9ScopeInfoEEEPNS0_7IsolateEPNS0_15IsCompiledScopeE + fun:_ZN2v88internal8Compiler18GetWrappedFunctionENS0_6HandleINS0_6StringEEENS2_INS0_10FixedArrayEEENS2_INS0_7ContextEEERKNS0_13ScriptDetailsEPNS0_17AlignedCachedDataENS_14ScriptCompiler14CompileOptionsENSE_13NoCacheReasonE + fun:_ZN2v814ScriptCompiler23CompileFunctionInternalENS_5LocalINS_7ContextEEEPNS0_6SourceEmPNS1_INS_6StringEEEmPNS1_INS_6ObjectEEENS0_14CompileOptionsENS0_13NoCacheReasonEPNS1_INS_14ScriptOrModuleEEE + fun:_ZN2v814ScriptCompiler15CompileFunctionENS_5LocalINS_7ContextEEEPNS0_6SourceEmPNS1_INS_6StringEEEmPNS1_INS_6ObjectEEENS0_14CompileOptionsENS0_13NoCacheReasonE + fun:_ZN4node8builtins13BuiltinLoader24LookupAndCompileInternalEN2v85LocalINS2_7ContextEEEPKcPSt6vectorINS3_INS2_6StringEEESaISA_EEPNS1_6ResultE + fun:_ZN4node8builtins13BuiltinLoader16LookupAndCompileEN2v85LocalINS2_7ContextEEEPKcPNS_11EnvironmentE + fun:_ZN4node8builtins13BuiltinLoader15CompileFunctionERKN2v820FunctionCallbackInfoINS2_5ValueEEE + ... +} diff --git a/source/tests/memcheck/valgrind-python.supp b/source/tests/memcheck/valgrind-python.supp index 53966a416f..d7feeb8c18 100644 --- a/source/tests/memcheck/valgrind-python.supp +++ b/source/tests/memcheck/valgrind-python.supp @@ -1,4 +1,6 @@ # +# From: https://github.com/python/cpython/blob/master/Misc/valgrind-python.supp +# # This is a valgrind suppression file that should be used when using valgrind. # # Here's an example of running valgrind: diff --git a/source/tests/memcheck/valgrind-ruby.supp b/source/tests/memcheck/valgrind-ruby.supp new file mode 100644 index 0000000000..934aac4943 --- /dev/null +++ b/source/tests/memcheck/valgrind-ruby.supp @@ -0,0 +1,220 @@ +{ + Init Ruby VM + Memcheck:Leak + ... + fun:ruby_setup + ... +} +{ + Ruby false positive eval_string_with_cref + Memcheck:Leak + ... + fun:eval_string_with_cref + ... +} +{ + Ruby false positive rb_add_method_cfunc + Memcheck:Leak + ... + fun:rb_add_method_cfunc + ... +} +{ + Ruby false positive rb_check_funcall + Memcheck:Leak + ... + fun:rb_check_funcall + ... +} +{ + Ruby false positive rb_class_boot: Called for all the different ways to create a Class + Memcheck:Leak + ... + fun:rb_class_boot + ... +} +{ + Ruby false positive rb_enc_raise + Memcheck:Leak + ... + fun:rb_enc_raise + ... +} +{ + Ruby false positive rb_exc_raise + Memcheck:Leak + ... + fun:rb_exc_raise + ... +} +{ + Ruby false positive rb_extend_object + Memcheck:Leak + ... + fun:rb_extend_object + ... +} +{ + Ruby false positive rb_funcall + Memcheck:Leak + ... + fun:rb_funcall + ... +} +{ + Ruby false positive rb_intern + Memcheck:Leak + ... + fun:rb_intern + ... +} +{ + Ruby false positive rb_ivar_set + Memcheck:Leak + ... + fun:rb_ivar_set + ... +} +{ + Ruby false positive rb_module_new + Memcheck:Leak + ... + fun:rb_module_new + ... +} +{ + Ruby false positive rb_raise + Memcheck:Leak + ... + fun:rb_raise + ... +} +{ + Ruby false positive rb_rescue + Memcheck:Leak + ... + fun:rb_rescue + ... +} +{ + Ruby false positive rb_respond_to + Memcheck:Leak + ... + fun:rb_respond_to + ... +} +{ + Ruby false positive rb_thread_create: Threads are relased to a cache, so they may be reported as a leak + Memcheck:Leak + ... + fun:rb_thread_create + ... +} +{ + Ruby false positive rb_yield + Memcheck:Leak + ... + fun:rb_yield + ... +} +{ + Ruby false positive rb_iseq_new_with_opt + Memcheck:Leak + ... + fun:rb_iseq_new_with_opt + ... +} +{ + Ruby false positive rb_str_subseq + Memcheck:Leak + ... + fun:rb_str_subseq + ... +} +{ + Ruby false positive rb_iseq_constant_body_alloc + Memcheck:Leak + ... + fun:rb_iseq_constant_body_alloc + ... +} +{ + Ruby false positive rb_singleton_class + Memcheck:Leak + ... + fun:rb_singleton_class + ... +} +{ + Ruby false positive rb_include_module + Memcheck:Leak + ... + fun:rb_include_module + ... +} +{ + Ruby false positive rb_define_class + Memcheck:Leak + ... + fun:rb_define_class + ... +} +{ + Ruby false positive rb_enc_str_new + Memcheck:Leak + ... + fun:rb_enc_str_new + ... +} +{ + On platforms where memcpy is safe for overlapped memory, the compiler will sometimes replace memmove with memcpy. Valgrind may report a false positive. + Memcheck:Overlap + fun:__memcpy_chk + fun:memmove + ... +} +{ + Requiring a file will add it to the loaded features, which may be reported as a leak. + Memcheck:Leak + ... + fun:require_internal + ... +} +{ + Remove this after Ruby 2.7.7, 3.0.5, 3.1.3 are relased. See: https://github.com/Shopify/ruby_memcheck/issues/6 + Memcheck:Leak + ... + fun:stack_chunk_alloc + ... +} +{ + recursive_list_access creates a hash called `list` that is stored on the threadptr_recursive_hash. This is reported as a memory leak. + Memcheck:Leak + ... + fun:rb_ident_hash_new + fun:recursive_list_access + fun:exec_recursive + ... +} +{ + "Invalid read of size 8" when marking the stack of fibers + Memcheck:Addr8 + fun:each_location* + ... +} +{ + Rust probes for statx(buf), will be fixed in Valgrind >= 3.1.6.0 + Memcheck:Param + statx(buf) + ... + fun:*try_statx* + ... +} +{ + Rust probes for statx(file_name), will be fixed in Valgrind >= 3.1.6.0 + Memcheck:Param + statx(file_name) + ... + fun:*try_statx* + ... +} diff --git a/source/tests/memcheck/valgrind-wasm.supp b/source/tests/memcheck/valgrind-wasm.supp new file mode 100644 index 0000000000..946936357f --- /dev/null +++ b/source/tests/memcheck/valgrind-wasm.supp @@ -0,0 +1,61 @@ +{ + Ignore memory leaked from wasm_engine_new. + Memcheck:Leak + ... + fun:wasm_engine_new + ... +} + +{ + Ignore memory leaked by wasm_instance_new. + Memcheck:Leak + ... + fun:*wasmtime*module*registry*ModuleRegistry*register* + ... + fun:wasm_instance_new + ... +} + +{ + Ignore memory leaked by wasm_store_delete. + Memcheck:Leak + ... + fun:wasm_store_delete + ... +} + +{ + Ignore memory leaked by Wasmtime trap handlers. + Memcheck:Leak + ... + fun:*wasmtime_runtime*traphandlers*trap_handler* + ... +} + +{ + Ignore memory leaked by dlopen when using dynlink_load. + Memcheck:Leak + ... + fun:dlopen + ... + fun:dynlink_load + ... +} + +{ + Ignore memory leaked by dlclose when using dynlink_unload. + Memcheck:Leak + ... + fun:dlclose + ... + fun:dynlink_unload + ... +} + +{ + Ignore memory leaked by dl_open_worker. + Memcheck:Leak + ... + fun:dl_open_worker + ... +} diff --git a/source/tests/memcheck/valgrind.supp b/source/tests/memcheck/valgrind.supp deleted file mode 100644 index e34e359fd4..0000000000 --- a/source/tests/memcheck/valgrind.supp +++ /dev/null @@ -1,7 +0,0 @@ -# GLIBC -{ - Ignore dlopen allocation memory leak from GLIB. - Memcheck:Leak - ... - fun:dlopen@@GLIBC_2.2.5 -} diff --git a/source/tests/metacall_backtrace_plugin_test/CMakeLists.txt b/source/tests/metacall_backtrace_plugin_test/CMakeLists.txt new file mode 100644 index 0000000000..900355faad --- /dev/null +++ b/source/tests/metacall_backtrace_plugin_test/CMakeLists.txt @@ -0,0 +1,166 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_PLUGINS_BACKTRACE) + return() +endif() + +include(Portability) + +if(NOT PROJECT_OS_FAMILY STREQUAL unix) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-backtrace-plugin-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_backtrace_plugin_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + BACKTRACE_PLUGIN_PATH="${PROJECT_OUTPUT_DIR}/plugins/backtrace_plugin/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + backtrace_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + # Disable valgrind when testing backtracing for segfaults + PROPERTY LABELS ${target} MEMCHECK_IGNORE +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_class_test/source/main.cpp b/source/tests/metacall_backtrace_plugin_test/source/main.cpp similarity index 81% rename from source/tests/metacall_python_class_test/source/main.cpp rename to source/tests/metacall_backtrace_plugin_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_python_class_test/source/main.cpp +++ b/source/tests/metacall_backtrace_plugin_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_backtrace_plugin_test/source/metacall_backtrace_plugin_test.cpp b/source/tests/metacall_backtrace_plugin_test/source/metacall_backtrace_plugin_test.cpp new file mode 100644 index 0000000000..ff2844f7ad --- /dev/null +++ b/source/tests/metacall_backtrace_plugin_test/source/metacall_backtrace_plugin_test.cpp @@ -0,0 +1,64 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#include + +void badass_function(void) +{ +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Warray-bounds" +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Warray-bounds" + #pragma GCC diagnostic ignored "-Wstringop-overflow=" +#endif + + char *ptr = (char *)42; + *ptr = 42; + +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif +} + +class metacall_backtrace_plugin_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_backtrace_plugin_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Generate a segmentation fault in order to catch it by backtrace plugin */ + EXPECT_DEATH({ badass_function(); }, ""); + + metacall_destroy(); +} diff --git a/source/tests/metacall_c_lib_test/CMakeLists.txt b/source/tests/metacall_c_lib_test/CMakeLists.txt new file mode 100644 index 0000000000..d3a3750646 --- /dev/null +++ b/source/tests/metacall_c_lib_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_C) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-c-lib-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_c_lib_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + c_loader + loadtest +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/node_loader_test/source/main.cpp b/source/tests/metacall_c_lib_test/source/main.cpp similarity index 81% rename from source/tests/node_loader_test/source/main.cpp rename to source/tests/metacall_c_lib_test/source/main.cpp index 95d7f4b83f..37d4adc23f 100644 --- a/source/tests/node_loader_test/source/main.cpp +++ b/source/tests/metacall_c_lib_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading ruby code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_c_lib_test/source/metacall_c_lib_test.cpp b/source/tests/metacall_c_lib_test/source/metacall_c_lib_test.cpp new file mode 100644 index 0000000000..1f61c2b179 --- /dev/null +++ b/source/tests/metacall_c_lib_test/source/metacall_c_lib_test.cpp @@ -0,0 +1,102 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_c_lib_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_c_lib_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_load_from_package("c", "loadtest", NULL)); + + void *ret = metacall("call_cpp_func"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_LONG); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)323); + + metacall_value_destroy(ret); + + void *pair_list_ptr = metacall_value_create_ptr(NULL); + + void *args_init[] = { + metacall_value_create_ptr(pair_list_ptr), + }; + + std::cout << "pair_list_ptr: " << pair_list_ptr << std::endl; + std::cout << "pair_list_ptr: *(" << metacall_value_to_ptr(pair_list_ptr) << ")" << std::endl; + + ret = metacallv("pair_list_init", args_init); + + std::cout << "pair_list_ptr: " << pair_list_ptr << std::endl; + std::cout << "pair_list_ptr: *(" << metacall_value_to_ptr(pair_list_ptr) << ")" << std::endl; + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_INT); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + + metacall_value_destroy(args_init[0]); + + void *args_value[] = { + pair_list_ptr, + metacall_value_create_int(2) + }; + + ret = metacallv("pair_list_value", args_value); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)2.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(args_value[1]); + + void *args_destroy[] = { + pair_list_ptr, + }; + + ret = metacallv("pair_list_destroy", args_destroy); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + metacall_value_destroy(pair_list_ptr); + + metacall_destroy(); +} diff --git a/source/tests/metacall_c_test/CMakeLists.txt b/source/tests/metacall_c_test/CMakeLists.txt new file mode 100644 index 0000000000..a5c3e0edde --- /dev/null +++ b/source/tests/metacall_c_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_C) + return() +endif() + +# +# External dependencies +# + +find_package(LibFFI) + +if(NOT LIBFFI_FOUND) + message(SEND_ERROR "Foreing Function Interface library not found") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-c-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_c_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + LIBFFI_INCLUDE_DIR="${LIBFFI_INCLUDE_DIR}" + LIBFFI_LIBRARY="${LIBFFI_LIBRARY}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + c_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/rb_loader_test/source/main.cpp b/source/tests/metacall_c_test/source/main.cpp similarity index 81% rename from source/tests/rb_loader_test/source/main.cpp rename to source/tests/metacall_c_test/source/main.cpp index 95d7f4b83f..37d4adc23f 100644 --- a/source/tests/rb_loader_test/source/main.cpp +++ b/source/tests/metacall_c_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading ruby code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_c_test/source/metacall_c_test.cpp b/source/tests/metacall_c_test/source/metacall_c_test.cpp new file mode 100644 index 0000000000..9b4bf3f6bc --- /dev/null +++ b/source/tests/metacall_c_test/source/metacall_c_test.cpp @@ -0,0 +1,435 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_c_test : public testing::Test +{ +protected: +}; + +void *sum_callback(size_t argc, void *args[], void *data) +{ + int a = metacall_value_to_int(args[0]); + int b = metacall_value_to_int(args[1]); + int result = a + b; + + (void)argc; + (void)data; + + printf("sum(%d, %d) == %d\n", a, b, result); + + return metacall_value_create_int(result); +} + +void *test_string_reference(size_t argc, void *args[], void *data) +{ + (void)argc; + (void)data; + + /* Get string from pointer */ + printf("ptr %p\n", args[0]); + fflush(stdout); + + void *string_value = metacall_value_to_ptr(args[0]); + + printf("string ptr %p\n", string_value); + printf("type id %s\n", metacall_value_type_name(string_value)); + fflush(stdout); + + char *str = metacall_value_to_string(string_value); + + printf("native string %s\n", str); + + /* Check it is a valid string */ + EXPECT_STREQ("asd", str); + + /* Replace the string, it will be choped by the previous length */ + static const char yeet[] = "yeet"; + + metacall_value_from_string(string_value, yeet, sizeof(yeet) - 1); + + printf("type id %s\n", metacall_value_type_name(string_value)); + printf("native string %s\n", str); + fflush(stdout); + + EXPECT_STREQ("yee", str); + + /* Define a new string in the pointer value */ + static const char hello[] = "hello world"; + + metacall_value_from_ptr(args[0], metacall_value_create_string(hello, sizeof(hello) - 1)); + + return metacall_value_create_null(); +} + +TEST_F(metacall_c_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* File */ + const char *c_scripts[] = { + "compiled.c" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + + void *ret = metacall("compiled_sum", 3, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); + + metacall_value_destroy(ret); + + /* https://github.com/metacall/core/issues/570 */ + { + /* void apply_blur_filter(int pixels[], int width, int height) */ + + /* Call by array */ + { + void *args[] = { + metacall_value_create_array(NULL, 100), + metacall_value_create_int(10), + metacall_value_create_int(10) + }; + + void **array_ptr = metacall_value_to_array(args[0]); + + for (int i = 0; i < 100; ++i) + { + array_ptr[i] = metacall_value_create_int(i); + } + + std::cout << "value: " << args[0] << std::endl; + std::cout << "array: " << array_ptr << std::endl; + + ret = metacallv("apply_blur_filter", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(args[2]); + } + + /* Call by pointer */ + { + int array[100]; + + void *args[] = { + metacall_value_create_ptr(array), + metacall_value_create_int(10), + metacall_value_create_int(10) + }; + + for (int i = 0; i < 100; ++i) + { + array[i] = i; + } + + ret = metacallv("apply_blur_filter", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(args[2]); + } + + /* double calculate_brightness(int pixels[], int size) */ + + /* Call by array */ + { + void *args[] = { + metacall_value_create_array(NULL, 100), + metacall_value_create_int(100) + }; + + void **array_ptr = metacall_value_to_array(args[0]); + + for (int i = 0; i < 100; ++i) + { + array_ptr[i] = metacall_value_create_int(i); + } + + std::cout << "value: " << args[0] << std::endl; + std::cout << "array: " << array_ptr << std::endl; + + ret = metacallv("calculate_brightness", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_DOUBLE); + + std::cout << "result: " << metacall_value_to_double(ret) << std::endl; + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)49.5); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Call by pointer */ + { + int array[100]; + + void *args[] = { + metacall_value_create_ptr(array), + metacall_value_create_int(100) + }; + + for (int i = 0; i < 100; ++i) + { + array[i] = i; + } + + ret = metacallv("calculate_brightness", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_DOUBLE); + + std::cout << "result: " << metacall_value_to_double(ret) << std::endl; + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + } + + /* File with dependencies */ + const char *c_dep_scripts[] = { + "ffi.c", + "ffi.ld" + }; + + /* Set dependency paths */ + EXPECT_EQ((int)0, (int)metacall_execution_path("c", LIBFFI_INCLUDE_DIR)); + EXPECT_EQ((int)0, (int)metacall_execution_path("c", LIBFFI_LIBRARY)); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_dep_scripts, sizeof(c_dep_scripts) / sizeof(c_dep_scripts[0]), NULL)); + + ret = metacall("call_fp_address"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_PTR); + + EXPECT_NE((void *)metacall_value_to_ptr(ret), (void *)NULL); + + metacall_value_destroy(ret); + + ret = metacall("int_type_renaming"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_INT); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)345); + + metacall_value_destroy(ret); + + /* Native register */ + metacall_register("sum_callback", sum_callback, NULL, METACALL_INT, 2, METACALL_INT, METACALL_INT); + + void *func = metacall_function("sum_callback"); + + EXPECT_NE((void *)NULL, (void *)func); + + void *args[] = { + metacall_value_create_function(func) + }; + + ret = metacallv_s("c_callback", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_INT); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)7); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + + /* Memory */ + { + const char c_buffer[] = { + "int compiled_mult_memory(int a, int b) { return a * b; }" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("c", c_buffer, sizeof(c_buffer), NULL)); + + void *ret = metacall("compiled_mult_memory", 3, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)12); + + metacall_value_destroy(ret); + } + + /* References (Native) */ + { + static const char str[] = "asd"; + void *str_value = metacall_value_create_string(str, sizeof(str) - 1); + void *str_value_ref = metacall_value_reference(str_value); + + printf("ptr %p\n", str_value_ref); + printf("string %p\n", str_value); + printf("string str %s\n", metacall_value_to_string(str_value)); + fflush(stdout); + + { + void *new_str_value = metacall_value_to_ptr(str_value_ref); + char *new_str = metacall_value_to_string(new_str_value); + + EXPECT_STREQ("asd", new_str); + } + + void *args[] = { + str_value_ref + }; + + metacall_register("test_string_reference", test_string_reference, NULL, METACALL_NULL, 1, METACALL_PTR); + + ret = metacallv_s("test_string_reference", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + printf("type id %s\n", metacall_value_type_name(str_value)); + fflush(stdout); + + /* It chops the string because it has a fixed size from 'asd' */ + EXPECT_STREQ(metacall_value_to_string(str_value), "yee"); + + metacall_value_destroy(str_value); + + /* It should contain the new string */ + void *new_str = metacall_value_dereference(str_value_ref); + + EXPECT_STREQ(metacall_value_to_string(new_str), "hello world"); + + metacall_value_destroy(new_str); + metacall_value_destroy(str_value_ref); + } + + /* References (C: string) */ + { + static const char str[] = "asd"; + void *str_value = metacall_value_create_string(str, sizeof(str) - 1); + void *str_value_ref = metacall_value_reference(str_value); + void *str_value_ref_ref = metacall_value_reference(str_value_ref); + + printf("(R) ptr %p\n", str_value_ref); + printf("(R) string ptr %p\n", str_value); + printf("(R) string str %s\n", metacall_value_to_string(str_value)); + fflush(stdout); + + void *args[] = { + str_value_ref_ref + }; + + ret = metacallv_s("modify_str_ptr", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + char *str_value_deref = static_cast(metacall_value_dereference(str_value_ref)); + + EXPECT_STREQ(str_value_deref, "yeet"); + + metacall_value_destroy(str_value); + metacall_value_destroy(str_value_ref); + metacall_value_destroy(str_value_ref_ref); + } + + /* References (C: int) */ + { + void *int_value = metacall_value_create_long(324444L); + void *int_value_ref = metacall_value_reference(int_value); + + printf("(R) ptr %p\n", int_value_ref); + printf("(R) int ptr %p\n", int_value); + printf("(R) int value %ld\n", metacall_value_to_long(int_value)); + fflush(stdout); + + void *args[] = { + int_value_ref + }; + + ret = metacallv_s("modify_int_ptr", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + EXPECT_EQ((long)metacall_value_to_long(int_value), (long)111L); + + metacall_value_destroy(int_value); + metacall_value_destroy(int_value_ref); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_callback_complex_test/CMakeLists.txt b/source/tests/metacall_callback_complex_test/CMakeLists.txt new file mode 100644 index 0000000000..ddf680698a --- /dev/null +++ b/source/tests/metacall_callback_complex_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-callback-complex-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_callback_complex_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/rb_loader_parser_integration_test/source/main.cpp b/source/tests/metacall_callback_complex_test/source/main.cpp similarity index 81% rename from source/tests/rb_loader_parser_integration_test/source/main.cpp rename to source/tests/metacall_callback_complex_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/rb_loader_parser_integration_test/source/main.cpp +++ b/source/tests/metacall_callback_complex_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_callback_complex_test/source/metacall_callback_complex_test.cpp b/source/tests/metacall_callback_complex_test/source/metacall_callback_complex_test.cpp new file mode 100644 index 0000000000..9906b0824e --- /dev/null +++ b/source/tests/metacall_callback_complex_test/source/metacall_callback_complex_test.cpp @@ -0,0 +1,155 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_callback_complex_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_callback_complex_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + /* This is the equivalent code of this test in NodeJS */ + /* + const { function_chain, py_function_factorial } = require('function.py'); + const py_py_factorial = function_chain(py_function_factorial); + assert.notStrictEqual(py_py_factorial, undefined); + assert.strictEqual(py_py_factorial(5), 120); + */ + + const char *py_scripts[] = { + "function.py" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + void *py_function_factorial = metacall_function("py_function_factorial"); + ASSERT_NE((void *)NULL, (void *)py_function_factorial); + + void *v_py_function_factorial = metacall_value_create_function(py_function_factorial); + ASSERT_NE((void *)NULL, (void *)v_py_function_factorial); + + void *args[] = { v_py_function_factorial }; + void *py_py_factorial = metacallv("function_chain", args); + ASSERT_NE((void *)NULL, (void *)py_py_factorial); + + void *args_fact[] = { metacall_value_create_long(5L) }; + void *ret = metacallfv(metacall_value_to_function(py_py_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((long)120L, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + + ret = metacallfv(metacall_value_to_function(py_py_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((long)120L, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + + metacall_value_destroy(args_fact[0]); + + metacall_value_destroy(v_py_function_factorial); + metacall_value_destroy(py_py_factorial); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* This is the equivalent code of this test in NodeJS */ + /* + const { js_function_chain, js_function_factorial } = require('factcallback.js'); + const js_js_factorial = js_function_chain(js_function_factorial); + assert.notStrictEqual(js_js_factorial, undefined); + assert.strictEqual(js_js_factorial(5), 120); + */ + + const char *node_scripts[] = { + "factcallback.js" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + void *js_function_factorial = metacall_function("js_function_factorial"); + ASSERT_NE((void *)NULL, (void *)js_function_factorial); + + void *v_js_function_factorial = metacall_value_create_function(js_function_factorial); + ASSERT_NE((void *)NULL, (void *)v_js_function_factorial); + EXPECT_EQ((enum metacall_value_id)METACALL_FUNCTION, (enum metacall_value_id)metacall_value_id(v_js_function_factorial)); + + void *args[] = { v_js_function_factorial }; + void *js_js_factorial = metacallv("js_function_chain", args); + ASSERT_NE((void *)NULL, (void *)js_js_factorial); + EXPECT_EQ((enum metacall_value_id)METACALL_FUNCTION, (enum metacall_value_id)metacall_value_id(js_js_factorial)); + + void *args_fact[] = { metacall_value_create_long(5L) }; + void *ret = metacallfv(metacall_value_to_function(js_js_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((double)120.0, (double)metacall_value_to_double(ret)); + metacall_value_destroy(ret); + + ret = metacallfv(metacall_value_to_function(js_js_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((double)120.0, (double)metacall_value_to_double(ret)); + metacall_value_destroy(ret); + + metacall_value_destroy(args_fact[0]); + + metacall_value_destroy(v_js_function_factorial); + metacall_value_destroy(js_js_factorial); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_callback_test/source/metacall_callback_test.cpp b/source/tests/metacall_callback_test/source/metacall_callback_test.cpp deleted file mode 100644 index 247bce2358..0000000000 --- a/source/tests/metacall_callback_test/source/metacall_callback_test.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include - -class metacall_callback_test : public testing::Test -{ -public: -}; - -TEST_F(metacall_callback_test, DefaultConstructor) -{ - metacall_print_info(); - - metacall_log_stdio_type log_stdio = { stdout }; - - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - // TODO: Python: Solve incompatibility with NodeJS on host script name after clearing it - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) - #if 0 - { - const char * py_scripts[] = - { - "host.py" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - - ret = metacall("a"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 3.0); - - metacall_value_destroy(ret); - - void * handle = metacall_handle("py", "host"); - - EXPECT_NE((void *) NULL, (void *) handle); - - EXPECT_EQ((int) 0, (int) metacall_clear(handle)); - } - #endif - #endif /* OPTION_BUILD_LOADERS_PY */ - - // TODO: NodeJS: Solve deadlock at the end of execution and with the callback - - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) - #if 0 - { - const char * node_scripts[] = - { - "host.js" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); - - ret = metacall("a"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 3.0); - - metacall_value_destroy(ret); - - void * handle = metacall_handle("node", "host"); - - EXPECT_NE((void *) NULL, (void *) handle); - - EXPECT_EQ((int) 0, (int) metacall_clear(handle)); - } - #endif - #endif /* OPTION_BUILD_LOADERS_NODE */ - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/metacall_cast_test/CMakeLists.txt b/source/tests/metacall_cast_test/CMakeLists.txt index 2f84078b48..674a145164 100644 --- a/source/tests/metacall_cast_test/CMakeLists.txt +++ b/source/tests/metacall_cast_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,6 +132,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_cast_test/source/main.cpp b/source/tests/metacall_cast_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_cast_test/source/main.cpp +++ b/source/tests/metacall_cast_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_cast_test/source/metacall_cast_test.cpp b/source/tests/metacall_cast_test/source/metacall_cast_test.cpp index 676911c95c..9b73a35de2 100644 --- a/source/tests/metacall_cast_test/source/metacall_cast_test.cpp +++ b/source/tests/metacall_cast_test/source/metacall_cast_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,16 +18,11 @@ * */ -#include +#include #include #include -#include -#include - -#include - class metacall_cast_test : public testing::Test { public: @@ -35,21 +30,18 @@ class metacall_cast_test : public testing::Test TEST_F(metacall_cast_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; @@ -57,70 +49,62 @@ TEST_F(metacall_cast_test, DefaultConstructor) int iterator; - value ret = NULL; + void *ret = NULL; - value args[2]; + void *args[2]; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - args[0] = value_create_int(5); - args[1] = value_create_int(15); + args[0] = metacall_value_create_long(5L); + args[1] = metacall_value_create_long(15L); ret = metacallv("multiply", args); - EXPECT_EQ((int) 75, (int) value_to_int(ret)); - - value_destroy(ret); + EXPECT_EQ((int)75L, (int)metacall_value_to_long(ret)); - log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); + metacall_value_destroy(ret); - args[0] = value_from_int(args[0], 7); + args[0] = metacall_value_from_int(args[0], 7); for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) { - args[1] = value_from_int(args[1], iterator); + args[1] = metacall_value_from_int(args[1], iterator); ret = metacallv("multiply", args); - EXPECT_NE((value) NULL, (value) ret); - - ret = value_type_cast(ret, TYPE_INT); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) (7 * iterator), (int) value_to_int(ret)); + EXPECT_EQ((int)(7 * iterator), (int)metacall_value_cast_int(&ret)); - value_destroy(ret); + metacall_value_destroy(ret); } - args[0] = value_from_float(value_type_cast(args[0], TYPE_FLOAT), 64.0f); - args[1] = value_from_float(value_type_cast(args[1], TYPE_FLOAT), 2.0f); + args[0] = metacall_value_from_float(metacall_value_cast(args[0], METACALL_FLOAT), 64.0f); + args[1] = metacall_value_from_float(metacall_value_cast(args[1], METACALL_FLOAT), 2.0f); ret = metacallv("divide", args); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - ret = value_type_cast(ret, TYPE_FLOAT); + EXPECT_EQ((float)32.0f, (float)metacall_value_cast_float(&ret)); - EXPECT_EQ((float) 32.0f, (float) value_to_float(ret)); + metacall_value_destroy(ret); - value_destroy(ret); - - args[0] = value_from_int(value_type_cast(args[0], TYPE_INT), 1000); - args[1] = value_from_int(value_type_cast(args[1], TYPE_INT), 3500); + args[0] = metacall_value_from_int(metacall_value_cast(args[0], METACALL_INT), 1000); + args[1] = metacall_value_from_int(metacall_value_cast(args[1], METACALL_INT), 3500); ret = metacallv("sum", args); - EXPECT_NE((value) NULL, (value) ret); - - ret = value_type_cast(ret, TYPE_INT); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 4500, (int) value_to_int(ret)); + EXPECT_EQ((int)4500, (int)metacall_value_cast_int(&ret)); - value_destroy(ret); + metacall_value_destroy(ret); - value_destroy(args[0]); - value_destroy(args[1]); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_clear_test/CMakeLists.txt b/source/tests/metacall_clear_test/CMakeLists.txt index 75264a6d49..859807d95c 100644 --- a/source/tests/metacall_clear_test/CMakeLists.txt +++ b/source/tests/metacall_clear_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_clear_test/source/main.cpp b/source/tests/metacall_clear_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_clear_test/source/main.cpp +++ b/source/tests/metacall_clear_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_clear_test/source/metacall_clear_test.cpp b/source/tests/metacall_clear_test/source/metacall_clear_test.cpp index ee4dcdc84a..f6dbc7fcb1 100644 --- a/source/tests/metacall_clear_test/source/metacall_clear_test.cpp +++ b/source/tests/metacall_clear_test/source/metacall_clear_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,10 +32,10 @@ TEST_F(metacall_clear_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char buffer[] = "#!/usr/bin/env python3\n" @@ -46,23 +46,35 @@ TEST_F(metacall_clear_test, DefaultConstructor) static const char tag[] = "py"; - void * handle = NULL; + void *handle = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), &handle)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), &handle)); - void * ret = metacall("multmem", 5, 15); + void *args[2] = { + metacall_value_create_long(5), + metacall_value_create_long(15) + }; - EXPECT_NE((void *) NULL, (void *) ret); + void *ret = metacallhv(handle, "multmem", args); - EXPECT_EQ((long) 75, (long) metacall_value_to_long(ret)); + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)75, (long)metacall_value_to_long(ret)); metacall_value_destroy(ret); - EXPECT_EQ((int) 0, (int) metacall_clear(handle)); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + + EXPECT_EQ((void *)NULL, (void *)metacall_function("multmem")); + + EXPECT_NE((void *)NULL, (void *)metacall_handle_function(handle, "multmem")); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); - EXPECT_EQ((void *) NULL, (void *) metacall_function("multmem")); + EXPECT_EQ((void *)NULL, (void *)metacall_function("multmem")); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_cli_core_plugin_await_test/CMakeLists.txt b/source/tests/metacall_cli_core_plugin_await_test/CMakeLists.txt new file mode 100644 index 0000000000..13284fb395 --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/CMakeLists.txt @@ -0,0 +1,176 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cli-core-plugin-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_cli_core_plugin_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +set(METACALL_PLUGIN_PATH "${PROJECT_OUTPUT_DIR}/metacall_cli_core_plugin_await_test") + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Plugin path + METACALL_PLUGIN_PATH="${METACALL_PLUGIN_PATH}" +) + +# Isolate the plugin into a different directory in order to make it fully reproducible +add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}/cli_core_plugin" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/repl/cli_core_plugin" "${METACALL_PLUGIN_PATH}/cli_core_plugin" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/plugins/await_test" "${METACALL_PLUGIN_PATH}/await_test" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + plugin_extension + node_port + cli_core_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # NodeJS Port path + "METACALL_NODE_PORT_PATH=${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) diff --git a/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/await_test.js b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/await_test.js new file mode 100644 index 0000000000..9332bc90d4 --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/await_test.js @@ -0,0 +1,14 @@ +const { metacall_load_from_file } = require(process.env['METACALL_NODE_PORT_PATH']); + +function await__test(await_cb) { + metacall_load_from_file("node", ["nod.js"]); + hello_await = 'hello_boy_await(1,2)'; + console.log(await_cb); + + // TODO: This call generates a deadlock + // await_cb(hello_await); + + return 22; +} + +module.exports = await__test; diff --git a/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/metacall.json b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/metacall.json new file mode 100644 index 0000000000..c39f3d7e6b --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "await_test.js" + ] +} diff --git a/source/tests/metacall_callback_test/source/main.cpp b/source/tests/metacall_cli_core_plugin_await_test/source/main.cpp similarity index 81% rename from source/tests/metacall_callback_test/source/main.cpp rename to source/tests/metacall_cli_core_plugin_await_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_callback_test/source/main.cpp +++ b/source/tests/metacall_cli_core_plugin_await_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_cli_core_plugin_await_test/source/metacall_cli_core_plugin_await_test.cpp b/source/tests/metacall_cli_core_plugin_await_test/source/metacall_cli_core_plugin_await_test.cpp new file mode 100644 index 0000000000..04461e788e --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/source/metacall_cli_core_plugin_await_test.cpp @@ -0,0 +1,127 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_cli_core_plugin_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_cli_core_plugin_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *plugin_extension_handle = metacall_plugin_extension(); + void *cli_plugin_handle = NULL; + + ASSERT_NE((void *)NULL, (void *)plugin_extension_handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&cli_plugin_handle) + }; + + void *result = metacallhv_s(plugin_extension_handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + + /* Test eval */ + void *func = metacall_handle_function(cli_plugin_handle, "eval"); + + ASSERT_NE((void *)func, (void *)NULL); + + static const char eval_loader_str[] = "node"; + static const char eval_str[] = "console.log('hello world')"; + + void *args_eval[] = { + metacall_value_create_string(eval_loader_str, sizeof(eval_loader_str) - 1), + metacall_value_create_string(eval_str, sizeof(eval_str) - 1) + }; + + result = metacallfv_s(func, args_eval, sizeof(args_eval) / sizeof(args_eval[0])); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args_eval[0]); + metacall_value_destroy(args_eval[1]); + metacall_value_destroy(result); + + /* Test await */ + func = metacall_handle_function(cli_plugin_handle, "await"); + + ASSERT_NE((void *)func, (void *)NULL); + + void *args_test[] = { + metacall_value_create_function(func) + }; + + result = metacallhv_s(cli_plugin_handle, "await__test", args_test, sizeof(args_test) / sizeof(args_test[0])); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((double)22.0, (double)metacall_value_to_double(result)); + + metacall_value_destroy(args_test[0]); + metacall_value_destroy(result); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_cli_core_plugin_test/CMakeLists.txt b/source/tests/metacall_cli_core_plugin_test/CMakeLists.txt new file mode 100644 index 0000000000..f0ecb936bc --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_EXT OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cli-core-plugin-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_cli_core_plugin_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + CLI_CORE_PLUGIN_PATH="${PROJECT_OUTPUT_DIR}/plugins/cli/repl/cli_core_plugin/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + py_loader + cli_core_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/file_loader_test/source/main.cpp b/source/tests/metacall_cli_core_plugin_test/source/main.cpp similarity index 81% rename from source/tests/file_loader_test/source/main.cpp rename to source/tests/metacall_cli_core_plugin_test/source/main.cpp index 95d7f4b83f..37d4adc23f 100644 --- a/source/tests/file_loader_test/source/main.cpp +++ b/source/tests/metacall_cli_core_plugin_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading ruby code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp b/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp new file mode 100644 index 0000000000..7b116647c6 --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp @@ -0,0 +1,390 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include + +class metacall_cli_core_plugin_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_cli_core_plugin_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + void *handle = NULL; + EXPECT_EQ((int)0, (int)metacall_load_from_configuration(CLI_CORE_PLUGIN_PATH, &handle, allocator)); + + /* Test load */ + { + void *args[2]; + + args[0] = metacall_value_create_string("py", 2); + + char test_script[] = "example.py"; + void *test_script_v = metacall_value_create_string(test_script, strlen(test_script)); + args[1] = metacall_value_create_array((const void **)&test_script_v, 1); + + void *ret = metacallhv_s(handle, "load", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test load */ + { + void *args[2]; + args[0] = metacall_value_create_string("node", 4); + + char test_script[] = "nod.js"; + void *test_script_v = metacall_value_create_string(test_script, strlen(test_script)); + args[1] = metacall_value_create_array((const void **)&test_script_v, 1); + + void *ret = metacallhv_s(handle, "load", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test eval */ + { + void *args[2]; + + args[0] = metacall_value_create_string("py", 2); + + char func_call[] = "print('Testing core_plugin...')\n"; + args[1] = (void **)metacall_value_create_string(func_call, strlen(func_call)); + + void *ret = metacallhv_s(handle, "eval", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test call */ + { + char func_call[] = "multiply(7, 3)"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)21); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test call without args */ + { + char func_call[] = "hello()"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without last parenthesis */ + { + char func_call[] = "multiply(7, 3"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without last parenthesis but another character instead */ + { + char func_call[] = "multiply(7, 3-"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call super tricky multiply("()", "eee"- */ + { + char func_call[] = "multiply(\"()\", \"eee\"~"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call with only first parenthesis */ + { + char func_call[] = "multiply("; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without any parenthesis */ + { + char func_call[] = "multiply"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without name */ + { + char func_call[] = "()"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without name and with only one parenthesis */ + { + char func_call[] = "("; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without name and with only one parenthesis (the oposite) */ + { + char func_call[] = ")"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call completely empty */ + { + char func_call[] = ""; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test await */ + { + char func_call[] = "hello_boy_await(2, 2)"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_double(ret), (double)4); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test await without args */ + { + char func_call[] = "return_await()"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_STREQ((const char *)metacall_value_to_string(ret), (const char *)"Hello World"); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without last parenthesis */ + { + char func_call[] = "hello_boy_await(2, 2"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call with only first parenthesis */ + { + char func_call[] = "hello_boy_await("; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without any parenthesis */ + { + char func_call[] = "hello_boy_await"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test clear */ + { + void *args[2]; + + args[0] = metacall_value_create_string("py", 2); + + char test_script[] = "example.py"; + args[1] = metacall_value_create_string(test_script, strlen(test_script)); + + void *ret = metacallhv_s(handle, "clear", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test inspect */ + { + void *ret = metacallhv_s(handle, "inspect", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + } + + /* Test debug */ + { + void *args[] = { metacall_value_create_array(NULL, 3) }; + void **array = metacall_value_to_array(args[0]); + + array[0] = metacall_value_create_string("abc", sizeof("abc") - 1); + array[1] = metacall_value_create_string("cbd", sizeof("cbd") - 1); + array[2] = metacall_value_create_string("thc", sizeof("thc") - 1); + + void *ret = metacallhv_s(handle, "debug", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_STREQ((const char *)"{\n\t[0]: abc\n\t[1]: cbd\n\t[2]: thc\n}", (const char *)metacall_value_to_string(ret)); + + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + } + + metacall_allocator_destroy(allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_cobol_test/CMakeLists.txt b/source/tests/metacall_cobol_test/CMakeLists.txt index 50878c95c7..3667141bca 100644 --- a/source/tests/metacall_cobol_test/CMakeLists.txt +++ b/source/tests/metacall_cobol_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -120,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + cob_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_cobol_test/source/main.cpp b/source/tests/metacall_cobol_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_cobol_test/source/main.cpp +++ b/source/tests/metacall_cobol_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_cobol_test/source/metacall_cobol_test.cpp b/source/tests/metacall_cobol_test/source/metacall_cobol_test.cpp index d46a5b25e5..5f74c003ce 100644 --- a/source/tests/metacall_cobol_test/source/metacall_cobol_test.cpp +++ b/source/tests/metacall_cobol_test/source/metacall_cobol_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_cobol_test : public testing::Test { @@ -33,34 +33,44 @@ TEST_F(metacall_cobol_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Cobol */ - #if defined(OPTION_BUILD_LOADERS_COB) +/* Cobol */ +#if defined(OPTION_BUILD_LOADERS_COB) { - const char * cob_scripts[] = - { + const char *cob_scripts[] = { "say.cob" }; - const enum metacall_value_id hello_string_ids[] = - { + const enum metacall_value_id hello_string_ids[] = { METACALL_STRING, METACALL_STRING }; - void * ret = NULL; + static const char tag[] = "cob"; + + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_file("cob", cob_scripts, sizeof(cob_scripts) / sizeof(cob_scripts[0]), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, cob_scripts, sizeof(cob_scripts) / sizeof(cob_scripts[0]), NULL)); ret = metacallt_s("say", hello_string_ids, 2, "hello", "world"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 0); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); metacall_value_destroy(ret); + + /* This is a Python script on purpose, in order to test Cobol when it fails */ + static const char buffer[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); } - #endif /* OPTION_BUILD_LOADERS_COB */ +#endif /* OPTION_BUILD_LOADERS_COB */ /* Print inspect information */ { @@ -68,18 +78,18 @@ TEST_F(metacall_cobol_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); metacall_allocator_free(allocator, inspect_str); metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_configuration_default_test/CMakeLists.txt b/source/tests/metacall_configuration_default_test/CMakeLists.txt new file mode 100644 index 0000000000..8cf0d40070 --- /dev/null +++ b/source/tests/metacall_configuration_default_test/CMakeLists.txt @@ -0,0 +1,168 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-configuration-default-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_configuration_default_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +# Create a custom directory different from default for forcing metacall not to find the configuration +set(CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/configuration-default-test") + +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}) + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" + + # Define custom build output directory + LIBRARY_OUTPUT_DIRECTORY "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + + RUNTIME_OUTPUT_DIRECTORY "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + + ARCHIVE_OUTPUT_DIRECTORY "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +if(WIN32) + # Windows requires metacall to be in the same folder as the executable in order to run the test properly + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ ${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY} + ) +endif() + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/metacall_configuration_default_test/source/main.cpp b/source/tests/metacall_configuration_default_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_configuration_default_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_configuration_default_test/source/metacall_configuration_default_test.cpp b/source/tests/metacall_configuration_default_test/source/metacall_configuration_default_test.cpp new file mode 100644 index 0000000000..5cee9a0e7d --- /dev/null +++ b/source/tests/metacall_configuration_default_test/source/metacall_configuration_default_test.cpp @@ -0,0 +1,37 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_configuration_default_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_configuration_default_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + metacall_destroy(); +} diff --git a/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt b/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt index da8ff0eb62..04c9d805e4 100644 --- a/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt +++ b/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS OR NOT OPTION_BUILD_LOADERS_PY) +# Check if python loader is enabled +if(NOT OPTION_BUILD_LOADERS_PY) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -97,8 +92,8 @@ target_link_libraries(${target} ${DEFAULT_LIBRARIES} GTest - ${CMAKE_DL_LIBS} - ${META_PROJECT_NAME}::metacall_distributable + + ${META_PROJECT_NAME}::metacall ) # @@ -119,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Set test variables # @@ -152,6 +164,34 @@ set_property(TEST ${target} PROPERTY LABELS ${target} ) +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # ERROR: LeakSanitizer: detected memory leaks + # + # Direct leak of 551991 byte(s) in 221 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # Direct leak of 1344 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e388d5 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85 + # #1 0x7f38162370d4 in _PyObject_GC_Resize (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x2040d4) + # + # Direct leak of 64 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f381622e105 in PyThread_allocate_lock (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1fb105) + # + # Indirect leak of 238277 byte(s) in 249 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # SUMMARY: AddressSanitizer: 791676 byte(s) leaked in 474 allocation(s). + # + # For solving this, we should enable Python support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} @@ -161,8 +201,52 @@ test_environment_variables(${target} "CONFIGURATION_PATH=${PY_CONFIGURATION_PATH}/global.json" "SERIAL_LIBRARY_PATH=${SERIAL_LIBRARY_PATH}" "DETOUR_LIBRARY_PATH=${DETOUR_LIBRARY_PATH}" + "${TESTS_SANITIZER_ENVIRONMENT_VARIABLES}" ) +# +# External dependencies +# + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(Python3_FIND_ABI "ON" "ANY" "ANY") + find_package(Python3 COMPONENTS Development) + + # Fallback to release if not found + if(NOT Python3_Development_FOUND) + set(Python3_FIND_ABI) + find_package(Python3 COMPONENTS Development REQUIRED) + endif() +else() + find_package(Python3 COMPONENTS Development REQUIRED) +endif() + +# Find Python DLL +include(Portability) + +if(PROJECT_OS_FAMILY STREQUAL win32 AND Python3_LIBRARIES AND Python3_ROOT_DIR AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + foreach(library ${Python3_LIBRARIES}) + if(${library} MATCHES "[^_d][.]lib$") + # Get the library path with dll suffix + string(REGEX REPLACE "[.]lib$" ".dll" LIB_PATH ${library}) + # Get the library name + get_filename_component(LIB_NAME "${LIB_PATH}" NAME) + # Find the library in the Python3 root path + find_file(Python3_LIBRARY_NAME_PATH ${LIB_NAME} + PATHS ${Python3_ROOT_DIR} + NO_DEFAULT_PATH + ) + if(Python3_LIBRARY_NAME_PATH) + break() + endif() + endif() + endforeach() +endif() + +if(NOT Python3_LIBRARY_NAME_PATH) + set(Python3_LIBRARY_NAME_PATH "${Python3_LIBRARIES}") +endif() + # # Configure test data # diff --git a/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in b/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in index 4978ee0b2b..02a8e81f9c 100644 --- a/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in +++ b/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in @@ -1,5 +1,8 @@ { "execution_paths": [ "@PY_EXECUTION_PATH@" - ] + ], + "dependencies": { + "python": ["@Python3_LIBRARY_NAME_PATH@"] + } } diff --git a/source/tests/metacall_configuration_exec_path_test/source/main.cpp b/source/tests/metacall_configuration_exec_path_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_configuration_exec_path_test/source/main.cpp +++ b/source/tests/metacall_configuration_exec_path_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp b/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp index 6c88d4609c..0219e58aaf 100644 --- a/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp +++ b/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,43 +18,42 @@ * */ -#include +#include #include #include -class metacall_distributable_test : public testing::Test +class metacall_configuration_exec_path_test : public testing::Test { public: }; -TEST_F(metacall_distributable_test, DefaultConstructor) +TEST_F(metacall_configuration_exec_path_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "main.py" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); ret = metacall("main"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Python hello_world: test")); + EXPECT_STREQ(metacall_value_to_string(ret), "Python hello_world: test"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_configuration_exec_relative_path_test/CMakeLists.txt b/source/tests/metacall_configuration_exec_relative_path_test/CMakeLists.txt new file mode 100644 index 0000000000..93ad2822f8 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/CMakeLists.txt @@ -0,0 +1,265 @@ +# Check if python loader is enabled +if(NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-configuration-exec-relative-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_configuration_exec_relative_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Dependecies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Set test variables +# + +set(PY_LOADER_SCRIPT_PATH "${CMAKE_CURRENT_BINARY_DIR}/scripts") +set(PY_CONFIGURATION_PATH "${CMAKE_CURRENT_BINARY_DIR}/configurations") +set(PY_EXECUTION_PATH "${PY_LOADER_SCRIPT_PATH}/a/b/c/d/e") + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # ERROR: LeakSanitizer: detected memory leaks + # + # Direct leak of 551991 byte(s) in 221 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # Direct leak of 1344 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e388d5 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85 + # #1 0x7f38162370d4 in _PyObject_GC_Resize (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x2040d4) + # + # Direct leak of 64 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f381622e105 in PyThread_allocate_lock (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1fb105) + # + # Indirect leak of 238277 byte(s) in 249 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # SUMMARY: AddressSanitizer: 791676 byte(s) leaked in 474 allocation(s). + # + # For solving this, we should enable Python support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + "LOADER_LIBRARY_PATH=${LOADER_LIBRARY_PATH}" + "LOADER_SCRIPT_PATH=${PY_LOADER_SCRIPT_PATH}" + "CONFIGURATION_PATH=${PY_CONFIGURATION_PATH}/global.json" + "SERIAL_LIBRARY_PATH=${SERIAL_LIBRARY_PATH}" + "DETOUR_LIBRARY_PATH=${DETOUR_LIBRARY_PATH}" + "${TESTS_SANITIZER_ENVIRONMENT_VARIABLES}" +) + +# +# External dependencies +# + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(Python3_FIND_ABI "ON" "ANY" "ANY") + find_package(Python3 COMPONENTS Development) + + # Fallback to release if not found + if(NOT Python3_Development_FOUND) + set(Python3_FIND_ABI) + find_package(Python3 COMPONENTS Development REQUIRED) + endif() +else() + find_package(Python3 COMPONENTS Development REQUIRED) +endif() + +# Find Python DLL +include(Portability) + +if(PROJECT_OS_FAMILY STREQUAL win32 AND Python3_LIBRARIES AND Python3_ROOT_DIR AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + foreach(library ${Python3_LIBRARIES}) + if(${library} MATCHES "[^_d][.]lib$") + # Get the library path with dll suffix + string(REGEX REPLACE "[.]lib$" ".dll" LIB_PATH ${library}) + # Get the library name + get_filename_component(LIB_NAME "${LIB_PATH}" NAME) + # Find the library in the Python3 root path + find_file(Python3_LIBRARY_NAME_PATH ${LIB_NAME} + PATHS ${Python3_ROOT_DIR} + NO_DEFAULT_PATH + ) + if(Python3_LIBRARY_NAME_PATH) + break() + endif() + endif() + endforeach() +endif() + +if(NOT Python3_LIBRARY_NAME_PATH) + set(Python3_LIBRARY_NAME_PATH "${Python3_LIBRARIES}") +endif() + +# +# Configure test data +# + +file(COPY data/scripts/main.py DESTINATION ${PY_LOADER_SCRIPT_PATH}) + +file(COPY data/scripts/metacall_configuration_exec_relative_path_test.py DESTINATION ${PY_EXECUTION_PATH}) + +# Set relative paths +set(PY_CONFIGURATION_OUTPUT_PATH "${PY_CONFIGURATION_PATH}") +set(PY_CONFIGURATION_PATH ".") +set(PY_EXECUTION_PATH "../scripts/a/b/c/d/e") + +configure_file(data/configurations/global.json.in ${PY_CONFIGURATION_OUTPUT_PATH}/global.json @ONLY) + +configure_file(data/configurations/py_loader.json.in ${PY_CONFIGURATION_OUTPUT_PATH}/py_loader.json @ONLY) diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/global.json.in b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/global.json.in new file mode 100644 index 0000000000..ca417e6a73 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/global.json.in @@ -0,0 +1,3 @@ +{ + "py_loader":"@PY_CONFIGURATION_PATH@/py_loader.json" +} diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/py_loader.json.in b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/py_loader.json.in new file mode 100644 index 0000000000..02a8e81f9c --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/py_loader.json.in @@ -0,0 +1,8 @@ +{ + "execution_paths": [ + "@PY_EXECUTION_PATH@" + ], + "dependencies": { + "python": ["@Python3_LIBRARY_NAME_PATH@"] + } +} diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/main.py b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/main.py new file mode 100644 index 0000000000..a34daba892 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/main.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import metacall_configuration_exec_relative_path_test + +def main(): + return metacall_configuration_exec_relative_path_test.hello_world('test') diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/metacall_configuration_exec_relative_path_test.py b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/metacall_configuration_exec_relative_path_test.py new file mode 100644 index 0000000000..5a8d8d1560 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/metacall_configuration_exec_relative_path_test.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 + +def hello_world(text): + return 'Python hello_world: ' + text diff --git a/source/tests/metacall_configuration_exec_relative_path_test/source/main.cpp b/source/tests/metacall_configuration_exec_relative_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/py_django_integration_test/source/py_django_integration_test.cpp b/source/tests/metacall_configuration_exec_relative_path_test/source/metacall_configuration_exec_relative_path_test.cpp similarity index 53% rename from source/tests/py_django_integration_test/source/py_django_integration_test.cpp rename to source/tests/metacall_configuration_exec_relative_path_test/source/metacall_configuration_exec_relative_path_test.cpp index 581000f7a0..cd56aa446e 100644 --- a/source/tests/py_django_integration_test/source/py_django_integration_test.cpp +++ b/source/tests/metacall_configuration_exec_relative_path_test/source/metacall_configuration_exec_relative_path_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,43 +18,42 @@ * */ -#include +#include #include #include -class py_django_integration_test : public testing::Test +class metacall_configuration_exec_relative_path_test : public testing::Test { public: }; -TEST_F(py_django_integration_test, DefaultConstructor) +TEST_F(metacall_configuration_exec_relative_path_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { - "manage.py" + const char *py_scripts[] = { + "main.py" }; - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - ret = metacall("frontend_initialize", 8080); + ret = metacall("main"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) 0, (long) metacall_value_to_long(ret)); + EXPECT_STREQ(metacall_value_to_string(ret), "Python hello_world: test"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_cs_test/CMakeLists.txt b/source/tests/metacall_cs_test/CMakeLists.txt new file mode 100644 index 0000000000..6eb981ca3c --- /dev/null +++ b/source/tests/metacall_cs_test/CMakeLists.txt @@ -0,0 +1,209 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_CS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cs-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/environment.hpp +) + +set(sources + ${source_path}/main.cpp + ${source_path}/environment.cpp + ${source_path}/metacall_cs_test.cpp +) + +# Group source files +set(header_group "Header Files") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} + ${headers} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizer: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13983) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 environment::SetUp() /usr/local/metacall/source/tests/metacall_cs_test/source/environment.cpp:38 (metacall-cs-testd+0x21968) + # #18 testing::internal::UnitTestImpl::RunAllTests() (metacall-cs-testd+0x483c1) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x81e000278 pc=0x7f21968e07c8 sp=0x7ee064c83d20 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +# +# Define test properties +# + +set_property(TEST ${target} + # TODO: Valgrind also fails with C# + PROPERTY LABELS ${target} MEMCHECK_IGNORE +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/cs_loader_test/include/cs-loader-test/environment.hpp b/source/tests/metacall_cs_test/include/metacall-cs-test/environment.hpp similarity index 81% rename from source/tests/cs_loader_test/include/cs-loader-test/environment.hpp rename to source/tests/metacall_cs_test/include/metacall-cs-test/environment.hpp index 61e7f4b9dc..b2efb0121c 100644 --- a/source/tests/cs_loader_test/include/cs-loader-test/environment.hpp +++ b/source/tests/metacall_cs_test/include/metacall-cs-test/environment.hpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,9 @@ * */ -#include +#include -#include - -class environment : public testing::Environment +class environment : public testing::Environment { public: void SetUp(); diff --git a/source/tests/cs_loader_test/source/environment.cpp b/source/tests/metacall_cs_test/source/environment.cpp similarity index 69% rename from source/tests/cs_loader_test/source/environment.cpp rename to source/tests/metacall_cs_test/source/environment.cpp index a39b3b051d..acd33b3c52 100644 --- a/source/tests/cs_loader_test/source/environment.cpp +++ b/source/tests/metacall_cs_test/source/environment.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,16 +17,15 @@ * limitations under the License. * */ -#include +#include -#include +#include #include void environment::SetUp() { - const char * cs_scripts[] = - { + const char *cs_scripts[] = { "hello.cs", "IJump.cs", "JumpMaster.cs", @@ -34,12 +33,12 @@ void environment::SetUp() "TinyJump.cs" }; - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); } void environment::TearDown() { - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/cs_loader_test/source/main.cpp b/source/tests/metacall_cs_test/source/main.cpp similarity index 78% rename from source/tests/cs_loader_test/source/main.cpp rename to source/tests/metacall_cs_test/source/main.cpp index 559c6b795b..41f8024b41 100644 --- a/source/tests/cs_loader_test/source/main.cpp +++ b/source/tests/metacall_cs_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,12 @@ * */ -#include -#include +#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new environment()); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_cs_test/source/metacall_cs_test.cpp b/source/tests/metacall_cs_test/source/metacall_cs_test.cpp new file mode 100644 index 0000000000..799b24ec00 --- /dev/null +++ b/source/tests/metacall_cs_test/source/metacall_cs_test.cpp @@ -0,0 +1,111 @@ +/* + * MetaCall Library by Parra Studios + * A library for dynamic loading and linking shared objects at run-time. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_cs_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_cs_test, SayHello) +{ + ASSERT_NE((void *)NULL, (void *)metacall_function("SayHello")); + + metacall("SayHello"); +} + +TEST_F(metacall_cs_test, SayAny) +{ + ASSERT_NE((void *)NULL, (void *)metacall_function("Say")); + + metacall("Say", "Any"); +} + +TEST_F(metacall_cs_test, Jump) +{ + void *ret = NULL; + + ASSERT_NE((void *)NULL, (void *)metacall_function("SuperJump")); + + ret = metacall("SuperJump"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)2, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); +} + +TEST_F(metacall_cs_test, Sum) +{ + void *ret = NULL; + + ASSERT_NE((void *)NULL, (void *)metacall_function("Sum")); + + ret = metacall("Sum", 5, 10); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); +} + +TEST_F(metacall_cs_test, Concat) +{ + void *ret = NULL; + + ASSERT_NE((void *)NULL, (void *)metacall_function("Concat")); + + ret = metacall("Concat", "Hello ", "World"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ((const char *)metacall_value_to_string(ret), "Hello World"); + + metacall_value_destroy(ret); +} + +TEST_F(metacall_cs_test, Fail) +{ + /* This is a Python script on purpose, in order to test C# when it fails */ + static const char buffer[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("cs", buffer, sizeof(buffer), NULL)); +} + +TEST_F(metacall_cs_test, FailRelativePath) +{ + /* TODO: Review source/ports/node_port/test/index.js in the fail/require section for more information */ + /* This test is not reproducing the same bug (segmentation fault), we should investigate why */ + static const char *scripts[] = { + "./asd.invalid" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("cs", scripts, sizeof(scripts) / sizeof(scripts[0]), NULL)); +} diff --git a/source/tests/cs_loader_test/CMakeLists.txt b/source/tests/metacall_csharp_function_test/CMakeLists.txt similarity index 77% rename from source/tests/cs_loader_test/CMakeLists.txt rename to source/tests/metacall_csharp_function_test/CMakeLists.txt index 7732fb5523..0a70cedb5e 100644 --- a/source/tests/cs_loader_test/CMakeLists.txt +++ b/source/tests/metacall_csharp_function_test/CMakeLists.txt @@ -3,12 +3,27 @@ if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_S return() endif() +# +# External dependencies +# + +find_package(DotNET) + +if(NOT DOTNET_FOUND) + message(SEND_ERROR "DotNET command not found") + return() +endif() + +if(DOTNET_VERSION VERSION_LESS "5.0") + return() +endif() + # # Executable name and options # # Target name -set(target cs-loader-test) +set(target metacall-csharp-function-test) message(STATUS "Test ${target}") # @@ -30,18 +45,13 @@ include(SecurityFlags) set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") -set(headers - ${include_path}/environment.hpp -) - set(sources ${source_path}/main.cpp - ${source_path}/environment.cpp - ${source_path}/cs_loader_test.cpp + ${source_path}/metacall_csharp_function_test.cpp ) # Group source files -set(header_group "Header Files") +set(header_group "Header Files (API)") set(source_group "Source Files") source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" ${header_group} ${headers}) @@ -55,7 +65,6 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Build executable add_executable(${target} ${sources} - ${headers} ) # Create namespaced alias @@ -78,7 +87,6 @@ set_target_properties(${target} target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} - ${CMAKE_CURRENT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/source/include ) @@ -92,13 +100,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::configuration ${META_PROJECT_NAME}::metacall ) @@ -120,11 +121,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +147,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_csharp_function_test/source/main.cpp b/source/tests/metacall_csharp_function_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_csharp_function_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_csharp_function_test/source/metacall_csharp_function_test.cpp b/source/tests/metacall_csharp_function_test/source/metacall_csharp_function_test.cpp new file mode 100644 index 0000000000..541ff5476b --- /dev/null +++ b/source/tests/metacall_csharp_function_test/source/metacall_csharp_function_test.cpp @@ -0,0 +1,79 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_csharp_function_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_csharp_function_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) + { + const char *cs_scripts[] = { + "function.cs" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + + void *ret = metacall("TopLevelFunc"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)8, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_CS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_csharp_static_class_test/CMakeLists.txt b/source/tests/metacall_csharp_static_class_test/CMakeLists.txt new file mode 100644 index 0000000000..7c2289c3dd --- /dev/null +++ b/source/tests/metacall_csharp_static_class_test/CMakeLists.txt @@ -0,0 +1,216 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_CS) + return() +endif() + +# +# External dependencies +# + +find_package(DotNET) + +if(NOT DOTNET_FOUND) + message(SEND_ERROR "DotNET command not found") + return() +endif() + +if(DOTNET_VERSION VERSION_LESS "5.0") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-csharp-static-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_csharp_static_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizer: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13974) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 metacall_csharp_static_class_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp:45 (metacall-csharp-static-class-testd+0x20d33) + # #18 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-csharp-static-class-testd+0x55fd6) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # Tracer caught signal 11: addr=0x500000330 pc=0x7fb0d9aa10f0 sp=0x7fb02658bd10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_csharp_static_class_test/source/main.cpp b/source/tests/metacall_csharp_static_class_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_csharp_static_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp b/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp new file mode 100644 index 0000000000..bfaf4f744b --- /dev/null +++ b/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp @@ -0,0 +1,83 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_csharp_static_class_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_csharp_static_class_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) + { + const char *cs_scripts[] = { + "static.cs" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + + void *ret = metacall("Said", "Hello World"); + + EXPECT_EQ((void *)NULL, (void *)ret); // TODO: NULL_TYPE not implemented yet + + ret = metacall("Three"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)3, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_CS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_cxx_port_test/CMakeLists.txt b/source/tests/metacall_cxx_port_test/CMakeLists.txt new file mode 100644 index 0000000000..1a4b07292f --- /dev/null +++ b/source/tests/metacall_cxx_port_test/CMakeLists.txt @@ -0,0 +1,151 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_CXX) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cxx-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_cxx_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::cxx_port +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + LIBFFI_INCLUDE_DIR="${LIBFFI_INCLUDE_DIR}" + LIBFFI_LIBRARY="${LIBFFI_LIBRARY}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_cxx_port_test/source/main.cpp b/source/tests/metacall_cxx_port_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_cxx_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_cxx_port_test/source/metacall_cxx_port_test.cpp b/source/tests/metacall_cxx_port_test/source/metacall_cxx_port_test.cpp new file mode 100644 index 0000000000..23998054e4 --- /dev/null +++ b/source/tests/metacall_cxx_port_test/source/metacall_cxx_port_test.cpp @@ -0,0 +1,198 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +using namespace metacall; + +class metacall_cxx_port_test : public testing::Test +{ +protected: +}; + +void *cxx_map_test(size_t argc, void *args[], void *data) +{ + map m(args[0]); + + (void)argc; + (void)data; + + EXPECT_EQ((float)m["hello"], (float)3.0f); + EXPECT_EQ((float)m["world"], (float)4.0f); + + printf("hello => %f\n", m["hello"]); + printf("world => %f\n", m["world"]); + fflush(stdout); + + return metacall_value_create_null(); +} + +void *cxx_array_test(size_t argc, void *args[], void *data) +{ + array a(args[0]); + + (void)argc; + (void)data; + + EXPECT_EQ((float)a[0].as(), (int)3); + EXPECT_EQ((float)a[1].as(), (float)4.0f); + + EXPECT_EQ((float)a.get(0), (int)3); + EXPECT_EQ((float)a.get(1), (float)4.0f); + + printf("a[0] => %d\n", a[0].as()); + printf("a[1] => %f\n", a[1].as()); + fflush(stdout); + + return metacall_value_create_null(); +} + +void *cxx_map_array_test(size_t argc, void *args[], void *data) +{ + map m(args[0]); + + (void)argc; + (void)data; + + EXPECT_STREQ(m["includes"][0].as().c_str(), "/a/path"); + EXPECT_STREQ(m["includes"][1].as().c_str(), "/another/path"); + + EXPECT_STREQ(m["libraries"][0].as().c_str(), "/a/path"); + EXPECT_STREQ(m["libraries"][1].as().c_str(), "/another/path"); + + printf("m['includes'][0] => %s\n", m["includes"][0].as().c_str()); + printf("m['includes'][1] => %s\n", m["includes"][1].as().c_str()); + + printf("m['libraries'][0] => %s\n", m["libraries"][0].as().c_str()); + printf("m['libraries'][1] => %s\n", m["libraries"][1].as().c_str()); + + return metacall_value_create_null(); +} + +// TODO: +/* +void *cxx_recursive_map_test(size_t argc, void *args[], void *data) +{ + map> m(args[0]); + + (void)argc; + (void)data; + + EXPECT_EQ((float)m["hello"]["world"], (float)4.0f); + + printf("hello => %f\n", m["hello"]["world"]); + fflush(stdout); + + return metacall_value_create_null(); +} +*/ + +void *cxx_float_int_int_test(size_t argc, void *args[], void *data) +{ + value a0(args[0]); + value a1(args[1]); + + (void)argc; + (void)data; + + EXPECT_EQ(a0.to_value(), 7); + EXPECT_EQ(a1.to_value(), 8); + + return metacall_value_create_float(3.0f); +} + +TEST_F(metacall_cxx_port_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + { + map m = { + { "hello", 3.0f }, + { "world", 4.0f } + }; + + metacall_register("cxx_map_test", cxx_map_test, NULL, METACALL_NULL, 1, METACALL_MAP); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_map_test", m)); + } + + { + array a(3, 4.0f); + + metacall_register("cxx_array_test", cxx_array_test, NULL, METACALL_NULL, 1, METACALL_ARRAY); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_array_test", a)); + } + + { + map m = { + { "includes", array("/a/path", "/another/path") }, + { "libraries", array("/a/path", "/another/path") } + }; + + metacall_register("cxx_map_array_test", cxx_map_array_test, NULL, METACALL_NULL, 1, METACALL_MAP); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_map_array_test", m)); + } + + // TODO: + /* + { + map> m = { + { "hello", { "world", 4.0f } } + }; + + metacall_register("cxx_recursive_map_test", cxx_recursive_map_test, NULL, METACALL_NULL, 1, METACALL_MAP); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_recursive_map_test", m)); + } + */ + + { + metacall_register("cxx_float_int_int_test", cxx_float_int_int_test, NULL, METACALL_FLOAT, 2, METACALL_INT, METACALL_INT); + + EXPECT_EQ(3.0f, metacall::metacall("cxx_float_int_int_test", 7, 8)); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_depends_test/CMakeLists.txt b/source/tests/metacall_depends_test/CMakeLists.txt index 09b9f944ad..46fe418c77 100644 --- a/source/tests/metacall_depends_test/CMakeLists.txt +++ b/source/tests/metacall_depends_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) +# Check if python loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_depends_test/source/main.cpp b/source/tests/metacall_depends_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_depends_test/source/main.cpp +++ b/source/tests/metacall_depends_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_depends_test/source/metacall_depends_test.cpp b/source/tests/metacall_depends_test/source/metacall_depends_test.cpp index 37170c4b76..4ec81006c5 100644 --- a/source/tests/metacall_depends_test/source/metacall_depends_test.cpp +++ b/source/tests/metacall_depends_test/source/metacall_depends_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,37 +32,36 @@ TEST_F(metacall_depends_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "rsasample.py", "sample/rsa_strings.py" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_NE((void *) NULL, (void *) metacall_function("main")); + EXPECT_NE((void *)NULL, (void *)metacall_function("main")); ret = metacall("main"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); metacall_value_destroy(ret); - EXPECT_NE((void *) NULL, (void *) metacall_function("encript_decript_strings")); + EXPECT_NE((void *)NULL, (void *)metacall_function("encript_decript_strings")); ret = metacall("encript_decript_strings"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -70,18 +69,18 @@ TEST_F(metacall_depends_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t)size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); metacall_allocator_free(allocator, inspect_str); metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_distributable_test/CMakeLists.txt b/source/tests/metacall_distributable_test/CMakeLists.txt index 60d437d539..07d5581616 100644 --- a/source/tests/metacall_distributable_test/CMakeLists.txt +++ b/source/tests/metacall_distributable_test/CMakeLists.txt @@ -1,12 +1,3 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) - return() -endif() - # # Executable name and options # @@ -63,9 +54,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +88,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +109,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -134,10 +131,48 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13717) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #9 metacall_distributable_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp:262 (metacall-distributable-testd+0x23e3d) + # #10 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-distributable-testd+0x58686) + # #11 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader + rb_loader + cs_loader + jsm_loader + js_loader + mock_loader + c_loader +) + # # Define test properties # @@ -146,6 +181,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x600000690 pc=0x7f3a7b6710f0 sp=0x7f3a75e32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_distributable_test/source/main.cpp b/source/tests/metacall_distributable_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_distributable_test/source/main.cpp +++ b/source/tests/metacall_distributable_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp b/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp index 1d91f40275..30b2033cb5 100644 --- a/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp +++ b/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,13 +32,12 @@ TEST_F(metacall_distributable_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; @@ -46,15 +45,15 @@ TEST_F(metacall_distributable_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); ret = metacall("multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -62,226 +61,220 @@ TEST_F(metacall_distributable_test, DefaultConstructor) { ret = metacall("multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); ret = metacall("sum", 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 4500); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)4500); metacall_value_destroy(ret); ret = metacall("sum", 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 7); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); metacall_value_destroy(ret); ret = metacall("hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret)); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); metacall_value_destroy(ret); ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "hello.rb", "second.rb" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); ret = metacall("say_multiply", 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 35); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)35); metacall_value_destroy(ret); ret = metacall("say_null"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); metacall_value_destroy(ret); ret = metacall("say_hello", "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello meta-programmer!"); metacall_value_destroy(ret); ret = metacall("get_second", 5, 12); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 12); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)12); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript SpiderMonkey */ - #if defined(OPTION_BUILD_LOADERS_JSM) +/* JavaScript SpiderMonkey */ +#if defined(OPTION_BUILD_LOADERS_JSM) { - const char * jsm_scripts[] = - { + const char *jsm_scripts[] = { "spider.jsm" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("say_spider", 8, 4)); + EXPECT_EQ((void *)NULL, (void *)metacall("say_spider", 8, 4)); } - #endif /* OPTION_BUILD_LOADERS_JSM */ +#endif /* OPTION_BUILD_LOADERS_JSM */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { - const char * js_scripts[] = - { + const char *js_scripts[] = { "divide.js", "third.js" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); ret = metacall("say_divide", 32.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 8.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)8.0); metacall_value_destroy(ret); ret = metacall("some_text", "abc", "def"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "abcdef")); + EXPECT_STREQ(metacall_value_to_string(ret), "abcdef"); metacall_value_destroy(ret); ret = metacall("third_value", "abc", "def", "efg"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "efg")); + EXPECT_STREQ(metacall_value_to_string(ret), "efg"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); ret = metacall("my_empty_func"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 1234); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)1234); metacall_value_destroy(ret); ret = metacall("two_doubles", 3.14, 68.3); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 3.1416); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.1416); metacall_value_destroy(ret); ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((char) metacall_value_to_char(ret), (char) 'A'); + EXPECT_EQ((char)metacall_value_to_char(ret), (char)'A'); metacall_value_destroy(ret); ret = metacall("new_args", "goodbye"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello World")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello World"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - /* C# Netcore */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) { - const char * cs_scripts[] = - { + const char *cs_scripts[] = { "hello.cs" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("Say", "Hello para with params!")); + EXPECT_EQ((void *)NULL, (void *)metacall("Say", "Hello para with params!")); } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ - /* C */ - #if defined(OPTION_BUILD_LOADERS_C) +/* C */ +#if defined(OPTION_BUILD_LOADERS_C) { - const char * c_scripts[] = - { + const char *c_scripts[] = { "compiled.c" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_C */ +#endif /* OPTION_BUILD_LOADERS_C */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_ducktype_test/CMakeLists.txt b/source/tests/metacall_ducktype_test/CMakeLists.txt index cb51537903..c8d507e6f0 100644 --- a/source/tests/metacall_ducktype_test/CMakeLists.txt +++ b/source/tests/metacall_ducktype_test/CMakeLists.txt @@ -80,20 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,6 +127,16 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + js_loader +) + # # Define test properties # diff --git a/source/tests/metacall_ducktype_test/source/main.cpp b/source/tests/metacall_ducktype_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_ducktype_test/source/main.cpp +++ b/source/tests/metacall_ducktype_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp b/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp index 1fe77a7750..0b8e2e2e84 100644 --- a/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp +++ b/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_ducktype_test : public testing::Test { @@ -33,13 +33,12 @@ TEST_F(metacall_ducktype_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "ducktype.py" }; @@ -47,20 +46,19 @@ TEST_F(metacall_ducktype_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - const enum metacall_value_id multiply_ids[] = - { + const enum metacall_value_id multiply_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("multiply", multiply_ids, 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 75); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)75); metacall_value_destroy(ret); @@ -68,335 +66,325 @@ TEST_F(metacall_ducktype_test, DefaultConstructor) { ret = metacallt("multiply", multiply_ids, 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)(7 * iterator)); metacall_value_destroy(ret); } - const enum metacall_value_id divide_ids[] = - { + const enum metacall_value_id divide_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("divide", divide_ids, 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)32.0); metacall_value_destroy(ret); - const enum metacall_value_id sum_int_ids[] = - { + const enum metacall_value_id sum_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("sum", sum_int_ids, 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 4500); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)4500); metacall_value_destroy(ret); ret = metacallt("sum", sum_int_ids, 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 7); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)7); metacall_value_destroy(ret); ret = metacall("hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret)); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); metacall_value_destroy(ret); - const enum metacall_value_id strcat_str_ids[] = - { + const enum metacall_value_id strcat_str_ids[] = { METACALL_STRING, METACALL_STRING }; ret = metacallt("strcat", strcat_str_ids, "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "Hello Universe"); metacall_value_destroy(ret); - const enum metacall_value_id strcat_float_ids[] = - { + const enum metacall_value_id strcat_float_ids[] = { METACALL_FLOAT, METACALL_FLOAT }; ret = metacallt("strcat", strcat_float_ids, 10.0f, 20.0f); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((float) metacall_value_cast_float(&ret), (float) 30.0f); + EXPECT_EQ((float)metacall_value_cast_float(&ret), (float)30.0f); metacall_value_destroy(ret); ret = metacall("old_style", 1, 2); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 3); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)3); metacall_value_destroy(ret); - const enum metacall_value_id mixed_int_ids[] = - { + const enum metacall_value_id mixed_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("mixed_style", mixed_int_ids, 200, 100); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 300); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)300); metacall_value_destroy(ret); - const enum metacall_value_id mixed_noret_int_ids[] = - { + const enum metacall_value_id mixed_noret_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("mixed_style_noreturn", mixed_noret_int_ids, 2, 6); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 8); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)8); metacall_value_destroy(ret); - void * args[2]; + void *args[2]; args[0] = metacall_value_create_int(5); args[1] = metacall_value_create_int(15); ret = metacallv("multiply", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 75); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)75); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); args[0] = metacall_value_create_double(15.0); args[1] = metacall_value_create_double(5.0); ret = metacallv("divide", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_double(&ret), (int) 3.0); + EXPECT_EQ((int)metacall_value_cast_double(&ret), (int)3.0); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); - const char pepico_str[] = "Pepico"; - const char walas_str[] = "Walas"; + static const char pepico_str[] = "Pepico"; + static const char walas_str[] = "Walas"; - args[0] = metacall_value_create_string(pepico_str, sizeof(pepico_str)); - args[1] = metacall_value_create_string(walas_str, sizeof(walas_str)); + args[0] = metacall_value_create_string(pepico_str, sizeof(pepico_str) - 1); + args[1] = metacall_value_create_string(walas_str, sizeof(walas_str) - 1); ret = metacallv("strcat", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "PepicoWalas")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "PepicoWalas"); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "ducktype.rb" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); - const enum metacall_value_id say_multiply_int_ids[] = - { + const enum metacall_value_id say_multiply_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("say_multiply", say_multiply_int_ids, 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 35); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)35); metacall_value_destroy(ret); - const enum metacall_value_id say_null_invalid_ids[] = - { + const enum metacall_value_id say_null_invalid_ids[] = { METACALL_INVALID }; ret = metacallt("say_null", say_null_invalid_ids); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); metacall_value_destroy(ret); - const enum metacall_value_id say_hello_str_ids[] = - { + const enum metacall_value_id say_hello_str_ids[] = { METACALL_STRING }; ret = metacallt("say_hello", say_hello_str_ids, "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "Hello meta-programmer!"); metacall_value_destroy(ret); - const enum metacall_value_id mixed_int_ids[] = - { + const enum metacall_value_id mixed_int_ids[] = { METACALL_INT, METACALL_INT, METACALL_INT, METACALL_INT }; ret = metacallt("mixed", mixed_int_ids, 1, 2, 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 10); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)10); metacall_value_destroy(ret); ret = metacall("map_style", 1, 2); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 3); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)3); metacall_value_destroy(ret); - void * args[2]; + void *args[2]; args[0] = metacall_value_create_int(5); args[1] = metacall_value_create_int(12); ret = metacallv("get_second", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 12); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)12); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { - const char * js_scripts[] = - { + const char *js_scripts[] = { "ducktype.js" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); - const enum metacall_value_id say_divide_double_ids[] = - { + const enum metacall_value_id say_divide_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("say_divide", say_divide_double_ids, 32.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 8.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)8.0); metacall_value_destroy(ret); - const enum metacall_value_id say_divide_float_ids[] = - { + const enum metacall_value_id say_divide_float_ids[] = { METACALL_FLOAT, METACALL_FLOAT }; ret = metacallt("say_divide", say_divide_float_ids, 32.0f, 4.0f); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((float) metacall_value_cast_float(&ret), (float) 8.0f); + EXPECT_EQ((float)metacall_value_cast_float(&ret), (float)8.0f); metacall_value_destroy(ret); - const enum metacall_value_id some_text_str_ids[] = - { + const enum metacall_value_id some_text_str_ids[] = { METACALL_STRING, METACALL_STRING }; ret = metacallt("some_text", some_text_str_ids, "abc", "def"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "abcdef")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "abcdef"); metacall_value_destroy(ret); - const enum metacall_value_id mixed_double_ids[] = - { + const enum metacall_value_id mixed_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("mixed_js", mixed_double_ids, 1.0, 2.0, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 10.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)10.0); metacall_value_destroy(ret); - const enum metacall_value_id mixed_noreturn_double_ids[] = - { + const enum metacall_value_id mixed_noreturn_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("mixed_noreturn", mixed_noreturn_double_ids, 1.0, 2.0, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 10.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)10.0); metacall_value_destroy(ret); - const enum metacall_value_id mixed_noreturn_float_ids[] = - { + const enum metacall_value_id mixed_noreturn_float_ids[] = { METACALL_FLOAT, METACALL_FLOAT, METACALL_FLOAT, METACALL_FLOAT }; ret = metacallt("mixed_noreturn", mixed_noreturn_float_ids, 1.0f, 2.0f, 3.0f, 4.0f); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((float) metacall_value_cast_float(&ret), (float) 10.0f); + EXPECT_EQ((float)metacall_value_cast_float(&ret), (float)10.0f); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_duplicated_handle_test/CMakeLists.txt b/source/tests/metacall_duplicated_handle_test/CMakeLists.txt index 96c21ff0fc..35032c7c5e 100644 --- a/source/tests/metacall_duplicated_handle_test/CMakeLists.txt +++ b/source/tests/metacall_duplicated_handle_test/CMakeLists.txt @@ -1,3 +1,9 @@ + +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + # # Executable name and options # @@ -80,20 +86,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -104,6 +96,9 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/duplicated-in-subfolder-config/metacall.json" ) # @@ -115,11 +110,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,6 +136,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + # # Define test properties # diff --git a/source/tests/metacall_duplicated_handle_test/source/main.cpp b/source/tests/metacall_duplicated_handle_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_duplicated_handle_test/source/main.cpp +++ b/source/tests/metacall_duplicated_handle_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp b/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp index ece33700d5..ebd9e57322 100644 --- a/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp +++ b/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,21 +32,78 @@ TEST_F(metacall_duplicated_handle_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_NE((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + ASSERT_NE((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts_0[] = { + "duplicated.js" + }; + + const char *node_scripts_1[] = { + "duplicated-in-subfolder/duplicated.js" + }; + + const enum metacall_value_id double_id[] = { + METACALL_DOUBLE + }; + + /* First script */ + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts_0, sizeof(node_scripts_0) / sizeof(node_scripts_0[0]), NULL)); + + void *ret = metacallt("three_times", double_id, 3.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)9.0); + + metacall_value_destroy(ret); + + /* Second script */ + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts_1, sizeof(node_scripts_1) / sizeof(node_scripts_1[0]), NULL)); + + ret = metacallt("two_times", double_id, 3.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); + + metacall_value_destroy(ret); + + /* Script with config */ + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(METACALL_TEST_CONFIG_PATH, NULL, config_allocator)); + + ret = metacallt("one_time", double_id, 3.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + metacall_allocator_destroy(config_allocator); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); } diff --git a/source/tests/metacall_duplicated_symbols_test/CMakeLists.txt b/source/tests/metacall_duplicated_symbols_test/CMakeLists.txt index eb5b3129c0..c5612d9658 100644 --- a/source/tests/metacall_duplicated_symbols_test/CMakeLists.txt +++ b/source/tests/metacall_duplicated_symbols_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + # # Executable name and options # @@ -80,7 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -101,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -118,6 +132,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + rb_loader +) + # # Define test properties # diff --git a/source/tests/metacall_duplicated_symbols_test/source/main.cpp b/source/tests/metacall_duplicated_symbols_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_duplicated_symbols_test/source/main.cpp +++ b/source/tests/metacall_duplicated_symbols_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_duplicated_symbols_test/source/metacall_duplicated_symbols_test.cpp b/source/tests/metacall_duplicated_symbols_test/source/metacall_duplicated_symbols_test.cpp index 6c3e23e620..07fb659c97 100644 --- a/source/tests/metacall_duplicated_symbols_test/source/metacall_duplicated_symbols_test.cpp +++ b/source/tests/metacall_duplicated_symbols_test/source/metacall_duplicated_symbols_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,28 +32,128 @@ TEST_F(metacall_duplicated_symbols_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * rb_ducktype_scripts[] = - { + static const char bufferA[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + static const char bufferB[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", bufferA, sizeof(bufferA), NULL)); + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("py", bufferB, sizeof(bufferB), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_ducktype_scripts[] = { "ducktype.rb" }; - const char * rb_second_scripts[] = - { + const char *rb_second_scripts[] = { "second.rb" }; /* Both scripts have get_second function which should fail when trying to load */ - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_ducktype_scripts, sizeof(rb_ducktype_scripts) / sizeof(rb_ducktype_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_ducktype_scripts, sizeof(rb_ducktype_scripts) / sizeof(rb_ducktype_scripts[0]), NULL)); + + EXPECT_EQ((int)1, (int)metacall_load_from_file("rb", rb_second_scripts, sizeof(rb_second_scripts) / sizeof(rb_second_scripts[0]), NULL)); + + void *handleA = NULL; + + static const char bufferA[] = + "def get_second(first, second)\n" + " puts('Second value is', second)\n" + " return 4\n" + "end\n"; + + void *handleB = NULL; + + static const char bufferB[] = + "def get_second(first, second)\n" + " puts('Second value is', second)\n" + " return 6\n" + "end\n"; + + const enum metacall_value_id say_multiply_int_ids[] = { + METACALL_INT, METACALL_INT + }; + + /* Test global scope get_second which belongs to ducktype.rb */ + + void *ret = metacallt("get_second", say_multiply_int_ids, 5, 7); + + EXPECT_EQ((int)7, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", bufferA, sizeof(bufferA), &handleA)); + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", bufferB, sizeof(bufferB), &handleB)); + + void *args[2] = { + metacall_value_create_int(234), + metacall_value_create_int(432) + }; + + /* Test handleA get_second which belongs to bufferA */ + + ret = metacallhv_s(handleA, "get_second", args, 2); + + EXPECT_EQ((int)4, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + /* Test handleB get_second which belongs to bufferB */ + + ret = metacallhv_s(handleB, "get_second", args, 2); + + EXPECT_EQ((int)6, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* Python + Ruby */ +#if defined(OPTION_BUILD_LOADERS_PY) && defined(OPTION_BUILD_LOADERS_RB) + { + /* Test duplicated symbols between languages */ + static const char bufferA[] = + "#!/usr/bin/env python3\n" + "def betweenlangs(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + static const char bufferB[] = + "def betweenlangs(first, second)\n" + " puts('Second value is', second)\n" + " return 6\n" + "end\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", bufferA, sizeof(bufferA), NULL)); - EXPECT_NE((int) 0, (int) metacall_load_from_file("rb", rb_second_scripts, sizeof(rb_second_scripts) / sizeof(rb_second_scripts[0]), NULL)); + EXPECT_EQ((int)1, (int)metacall_load_from_memory("rb", bufferB, sizeof(bufferB), NULL)); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_PY + OPTION_BUILD_LOADERS_RB */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_dynlink_path_test/CMakeLists.txt b/source/tests/metacall_dynlink_path_test/CMakeLists.txt new file mode 100644 index 0000000000..c1c9a76c42 --- /dev/null +++ b/source/tests/metacall_dynlink_path_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-dynlink-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_dynlink_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include + + $ # MetaCall includes +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +if(WIN32) + string(REPLACE "/" "\\\\" METACALL_LIBRARY_PATH "${PROJECT_OUTPUT_DIR}") +else() + set(METACALL_LIBRARY_PATH "${PROJECT_OUTPUT_DIR}") +endif() + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + # For metacall library path test + METACALL_LIBRARY_PATH="${METACALL_LIBRARY_PATH}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_dynlink_path_test/source/main.cpp b/source/tests/metacall_dynlink_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_dynlink_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_dynlink_path_test/source/metacall_dynlink_path_test.cpp b/source/tests/metacall_dynlink_path_test/source/metacall_dynlink_path_test.cpp new file mode 100644 index 0000000000..a989448f45 --- /dev/null +++ b/source/tests/metacall_dynlink_path_test/source/metacall_dynlink_path_test.cpp @@ -0,0 +1,55 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +class metacall_dynlink_path_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_dynlink_path_test, DefaultConstructor) +{ + metacall_print_info(); + + dynlink_path path; + + const char name[] = "metacall" +#if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) + "d" +#endif + ; + + size_t length = 0; + + printf(METACALL_LIBRARY_PATH "\n"); + fflush(stdout); + + ASSERT_EQ((int)0, (int)dynlink_library_path(name, path, &length)); + + printf("%s == %s\n", path, METACALL_LIBRARY_PATH); + fflush(stdout); + + ASSERT_EQ((int)0, (int)portability_path_compare(path, METACALL_LIBRARY_PATH)); +} diff --git a/source/tests/metacall_ext_test/CMakeLists.txt b/source/tests/metacall_ext_test/CMakeLists.txt new file mode 100644 index 0000000000..3c6b986981 --- /dev/null +++ b/source/tests/metacall_ext_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_EXT) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ext-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ext_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ext_test/source/main.cpp b/source/tests/metacall_ext_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_ext_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ext_test/source/metacall_ext_test.cpp b/source/tests/metacall_ext_test/source/metacall_ext_test.cpp new file mode 100644 index 0000000000..dde1efd0f7 --- /dev/null +++ b/source/tests/metacall_ext_test/source/metacall_ext_test.cpp @@ -0,0 +1,79 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_ext_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_ext_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + const char *ext_scripts[] = { + "sum_extension" /* The library extension (dll, so, dylib) is crossplatform so we should not add it here */ + }; + + void *handle = NULL; + + /* Test reload of the extension */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ext", ext_scripts, sizeof(ext_scripts) / sizeof(ext_scripts[0]), &handle)); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + + /* Test extension load and call */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ext", ext_scripts, sizeof(ext_scripts) / sizeof(ext_scripts[0]), NULL)); + + void *ret = metacall("sum", 3, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); + + metacall_value_destroy(ret); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/file_loader_test/CMakeLists.txt b/source/tests/metacall_file_fail_test/CMakeLists.txt similarity index 83% rename from source/tests/file_loader_test/CMakeLists.txt rename to source/tests/metacall_file_fail_test/CMakeLists.txt index 59caf4a9e9..ab6236fe16 100644 --- a/source/tests/file_loader_test/CMakeLists.txt +++ b/source/tests/metacall_file_fail_test/CMakeLists.txt @@ -8,7 +8,7 @@ endif() # # Target name -set(target file-loader-test) +set(target metacall-file-fail-test) message(STATUS "Test ${target}") # @@ -32,7 +32,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/file_loader_test.cpp + ${source_path}/metacall_file_fail_test.cpp ) # Group source files @@ -85,13 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::configuration + ${META_PROJECT_NAME}::metacall ) # @@ -112,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -126,10 +129,17 @@ target_link_libraries(${target} # add_test(NAME ${target} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + file_loader +) + # # Define test properties # diff --git a/source/tests/metacall_file_fail_test/source/main.cpp b/source/tests/metacall_file_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_file_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_class_test/source/metacall_python_class_test.cpp b/source/tests/metacall_file_fail_test/source/metacall_file_fail_test.cpp similarity index 55% rename from source/tests/metacall_python_class_test/source/metacall_python_class_test.cpp rename to source/tests/metacall_file_fail_test/source/metacall_file_fail_test.cpp index 232a8d9b64..426e44002d 100644 --- a/source/tests/metacall_python_class_test/source/metacall_python_class_test.cpp +++ b/source/tests/metacall_file_fail_test/source/metacall_file_fail_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,35 +18,35 @@ * */ -#include +#include #include #include +#include -class metacall_python_class_test : public testing::Test +class metacall_file_fail_test : public testing::Test { public: }; -TEST_F(metacall_python_class_test, DefaultConstructor) +TEST_F(metacall_file_fail_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* File */ +#if defined(OPTION_BUILD_LOADERS_FILE) { - const char * py_scripts[] = - { - "classname.py" + const char *scripts[] = { + "this-file-does-not-exists.yeet" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + const size_t size = sizeof(scripts) / sizeof(scripts[0]); - /* TODO: Implement properly class and object reflection and methods */ + EXPECT_NE((int)0, (int)metacall_load_from_file("file", scripts, size, NULL)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_FILE */ /* Print inspect information */ { @@ -54,13 +54,13 @@ TEST_F(metacall_python_class_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -69,5 +69,5 @@ TEST_F(metacall_python_class_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_file_glob_test/CMakeLists.txt b/source/tests/metacall_file_glob_test/CMakeLists.txt new file mode 100644 index 0000000000..ffc5a1c274 --- /dev/null +++ b/source/tests/metacall_file_glob_test/CMakeLists.txt @@ -0,0 +1,169 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_FILE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_FILE) + return() +endif() + +# TODO: This test is not passing properly in Windows. +# Glob is not implemented properly on the file_loader for Windows, +# so we disable it until we support glob properly. +include(Portability) + +if(PROJECT_OS_WIN) + message(WARNING "Loader file_loader with glob support does not work on Windows with MSVC compiler") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-file-glob-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_file_glob_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/metacall-globfile.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + file_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_file_glob_test/source/main.cpp b/source/tests/metacall_file_glob_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_file_glob_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_file_glob_test/source/metacall_file_glob_test.cpp b/source/tests/metacall_file_glob_test/source/metacall_file_glob_test.cpp new file mode 100644 index 0000000000..85377d72d6 --- /dev/null +++ b/source/tests/metacall_file_glob_test/source/metacall_file_glob_test.cpp @@ -0,0 +1,74 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_file_glob_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_file_glob_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(METACALL_TEST_CONFIG_PATH, NULL, config_allocator)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("glob/a.txt")); + EXPECT_NE((void *)NULL, (void *)metacall_function("glob/b.txt")); + EXPECT_EQ((void *)NULL, (void *)metacall_function("glob/c.json")); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_allocator_destroy(config_allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_file_test/CMakeLists.txt b/source/tests/metacall_file_test/CMakeLists.txt index e8629fc64a..9dd2a4fb04 100644 --- a/source/tests/metacall_file_test/CMakeLists.txt +++ b/source/tests/metacall_file_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -120,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + file_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_file_test/source/main.cpp b/source/tests/metacall_file_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_file_test/source/main.cpp +++ b/source/tests/metacall_file_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_file_test/source/metacall_file_test.cpp b/source/tests/metacall_file_test/source/metacall_file_test.cpp index e228c998fe..15658125fd 100644 --- a/source/tests/metacall_file_test/source/metacall_file_test.cpp +++ b/source/tests/metacall_file_test/source/metacall_file_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_file_test : public testing::Test { @@ -33,29 +33,28 @@ TEST_F(metacall_file_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* File */ - #if defined(OPTION_BUILD_LOADERS_FILE) +/* File */ +#if defined(OPTION_BUILD_LOADERS_FILE) { - const char * scripts[] = - { + const char *scripts[] = { "favicon.ico", "a/a.txt" - }; - + }; + const size_t size = sizeof(scripts) / sizeof(scripts[0]); - EXPECT_EQ((int) 0, (int) metacall_load_from_file("file", scripts, size, NULL)); - - for (size_t i = 0; i < size; ++i) + EXPECT_EQ((int)0, (int)metacall_load_from_file("file", scripts, size, NULL)); + + for (size_t i = 0; i < size; ++i) { - void * f = metacall_function(scripts[i]); - - EXPECT_NE((void *) NULL, (void *) f); + void *f = metacall_function(scripts[i]); + + EXPECT_NE((void *)NULL, (void *)f); } } - #endif /* OPTION_BUILD_LOADERS_FILE */ +#endif /* OPTION_BUILD_LOADERS_FILE */ /* Print inspect information */ { @@ -63,13 +62,13 @@ TEST_F(metacall_file_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -78,5 +77,5 @@ TEST_F(metacall_file_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_fork_test/CMakeLists.txt b/source/tests/metacall_fork_test/CMakeLists.txt index 18b17d3f0b..90c96bdef3 100644 --- a/source/tests/metacall_fork_test/CMakeLists.txt +++ b/source/tests/metacall_fork_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if detours are enabled -if(NOT OPTION_BUILD_DETOURS OR NOT OPTION_FORK_SAFE) +if(NOT OPTION_FORK_SAFE OR NOT OPTION_BUILD_DETOURS) return() endif() @@ -85,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + plthook_detour +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_fork_test/source/main.cpp b/source/tests/metacall_fork_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_fork_test/source/main.cpp +++ b/source/tests/metacall_fork_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_fork_test/source/metacall_fork_test.cpp b/source/tests/metacall_fork_test/source/metacall_fork_test.cpp index 6db299e439..ab784ff942 100644 --- a/source/tests/metacall_fork_test/source/metacall_fork_test.cpp +++ b/source/tests/metacall_fork_test/source/metacall_fork_test.cpp @@ -1,16 +1,16 @@ /* * MetaCall Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A library for providing a foreign function interface calls. * */ -#include +#include #include -#include +#include class metacall_fork_test : public testing::Test { @@ -21,28 +21,30 @@ static int pre_callback_fired = 0; static int post_callback_fired = 0; #if defined(WIN32) || defined(_WIN32) || \ - defined(__CYGWIN__) || defined(__CYGWIN32__) || \ - defined(__MINGW32__) || defined(__MINGW64__) + defined(__CYGWIN__) || defined(__CYGWIN32__) || \ + defined(__MINGW32__) || defined(__MINGW64__) -#define _WIN32_WINNT 0x0600 -#define WIN32_LEAN_AND_MEAN -#include + #define _WIN32_WINNT 0x0600 + #define WIN32_LEAN_AND_MEAN + #include -#define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001 -#define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002 -#define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004 + #define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001 + #define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002 + #define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004 -#define RTL_CLONE_PARENT 0 -#define RTL_CLONE_CHILD 297 + #define RTL_CLONE_PARENT 0 + #define RTL_CLONE_CHILD 297 typedef long NTSTATUS; -typedef struct _CLIENT_ID { +typedef struct _CLIENT_ID +{ PVOID UniqueProcess; PVOID UniqueThread; } CLIENT_ID, *PCLIENT_ID; -typedef struct _SECTION_IMAGE_INFORMATION { +typedef struct _SECTION_IMAGE_INFORMATION +{ PVOID EntryPoint; ULONG StackZeroBits; ULONG StackReserved; @@ -56,7 +58,8 @@ typedef struct _SECTION_IMAGE_INFORMATION { ULONG Unknown2[3]; } SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; -typedef struct _RTL_USER_PROCESS_INFORMATION { +typedef struct _RTL_USER_PROCESS_INFORMATION +{ ULONG Size; HANDLE Process; HANDLE Thread; @@ -64,7 +67,7 @@ typedef struct _RTL_USER_PROCESS_INFORMATION { SECTION_IMAGE_INFORMATION ImageInformation; } RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION; -typedef NTSTATUS(NTAPI * RtlCloneUserProcessPtr)(ULONG ProcessFlags, +typedef NTSTATUS(NTAPI *RtlCloneUserProcessPtr)(ULONG ProcessFlags, PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, HANDLE DebugPort, @@ -99,10 +102,7 @@ pid_t fork() if (result == RTL_CLONE_PARENT) { - HANDLE me = GetCurrentProcess(); - pid_t child_pid; - - child_pid = GetProcessId(process_info.Process); + pid_t child_pid = GetProcessId(process_info.Process); ResumeThread(process_info.Thread); CloseHandle(process_info.Process); @@ -112,7 +112,7 @@ pid_t fork() } else if (result == RTL_CLONE_CHILD) { - /* fix stdio */ + /* Fix stdio */ AllocConsole(); return 0; } @@ -122,22 +122,22 @@ pid_t fork() #endif -int pre_callback_test(void * ctx) +int pre_callback_test(void *ctx) { (void)ctx; - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall pre fork callback test"); + std::cout << "MetaCall pre fork callback test" << std::endl; pre_callback_fired = 1; return 0; } -int post_callback_test(metacall_pid pid, void * ctx) +int post_callback_test(metacall_pid pid, void *ctx) { (void)ctx; - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall post fork callback test %d", (int)pid); + std::cout << "MetaCall post fork callback test " << (int)pid << std::endl; post_callback_fired = 1; @@ -150,21 +150,21 @@ TEST_F(metacall_fork_test, DefaultConstructor) metacall_flags(METACALL_FLAGS_FORK_SAFE); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); metacall_fork(&pre_callback_test, &post_callback_test); if (fork() == 0) { - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall fork child"); + std::cout << "MetaCall fork child" << std::endl; } else { - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall fork parent"); + std::cout << "MetaCall fork parent" << std::endl; } - EXPECT_EQ((int) 1, (int) pre_callback_fired); - EXPECT_EQ((int) 1, (int) post_callback_fired); + EXPECT_EQ((int)1, (int)pre_callback_fired); + EXPECT_EQ((int)1, (int)post_callback_fired); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_function_test/CMakeLists.txt b/source/tests/metacall_function_test/CMakeLists.txt index d0d5c0eb14..9ac6fe47dc 100644 --- a/source/tests/metacall_function_test/CMakeLists.txt +++ b/source/tests/metacall_function_test/CMakeLists.txt @@ -86,20 +86,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -121,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,12 +133,21 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_function_test/source/main.cpp b/source/tests/metacall_function_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_function_test/source/main.cpp +++ b/source/tests/metacall_function_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_function_test/source/metacall_function_test.cpp b/source/tests/metacall_function_test/source/metacall_function_test.cpp index b1e313aadb..8b837151a1 100644 --- a/source/tests/metacall_function_test/source/metacall_function_test.cpp +++ b/source/tests/metacall_function_test/source/metacall_function_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +18,13 @@ * */ -#include +#include #include -#include #include +#include -void * c_callback(size_t argc, void * args[], void * data) +void *c_callback(size_t argc, void *args[], void *data) { (void)argc; (void)args; @@ -35,7 +35,7 @@ void * c_callback(size_t argc, void * args[], void * data) return metacall_value_create_long(32L); } -void * c_callback_with_args(size_t argc, void * args[], void * data) +void *c_callback_with_args(size_t argc, void *args[], void *data) { long left = metacall_value_to_long(args[0]), right = metacall_value_to_long(args[1]); @@ -47,7 +47,7 @@ void * c_callback_with_args(size_t argc, void * args[], void * data) return metacall_value_create_long(left + right); } -void * c_callback_factorial_impl(size_t argc, void * args[], void * data) +void *c_callback_factorial_impl(size_t argc, void *args[], void *data) { (void)argc; (void)data; @@ -62,9 +62,9 @@ void * c_callback_factorial_impl(size_t argc, void * args[], void * data) } } -void * c_callback_factorial(size_t argc, void * args[], void * data) +void *c_callback_factorial(size_t argc, void *args[], void *data) { - void * c = metacall_value_to_function(args[0]); + void *c = metacall_value_to_function(args[0]); // function c_callback_factorial(c) { // return function c_callback_factorial_impl(v) { return v <= 0 ? 1 : v }; @@ -83,59 +83,66 @@ class metacall_function_test : public testing::Test }; TEST_F(metacall_function_test, DefaultConstructor) -{ +{ metacall_print_info(); metacall_log_stdio_type log_stdio = { stdout }; - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); /* Native register */ - ASSERT_EQ((int) 0, (int) metacall_register("c_callback", c_callback, NULL, METACALL_LONG, 0)); - ASSERT_EQ((int) 0, (int) metacall_register("c_callback_with_args", c_callback_with_args, NULL, METACALL_LONG, 2, METACALL_LONG, METACALL_LONG)); - ASSERT_EQ((int) 0, (int) metacall_register("c_callback_factorial_impl", c_callback_factorial_impl, NULL, METACALL_LONG, 1, METACALL_LONG)); - ASSERT_EQ((int) 0, (int) metacall_register("c_callback_factorial", c_callback_factorial, NULL, METACALL_FUNCTION, 1, METACALL_FUNCTION)); + ASSERT_EQ((int)0, (int)metacall_register("c_callback", c_callback, NULL, METACALL_LONG, 0)); + ASSERT_EQ((int)0, (int)metacall_register("c_callback_with_args", c_callback_with_args, NULL, METACALL_LONG, 2, METACALL_LONG, METACALL_LONG)); + ASSERT_EQ((int)0, (int)metacall_register("c_callback_factorial_impl", c_callback_factorial_impl, NULL, METACALL_LONG, 1, METACALL_LONG)); + ASSERT_EQ((int)0, (int)metacall_register("c_callback_factorial", c_callback_factorial, NULL, METACALL_FUNCTION, 1, METACALL_FUNCTION)); /* Create function types */ - void * c_callback_value = metacall_value_create_function(metacall_function("c_callback")); - void * c_callback_with_args_value = metacall_value_create_function(metacall_function("c_callback_with_args")); - void * c_callback_factorial_impl_value = metacall_value_create_function(metacall_function("c_callback_factorial_impl")); - void * c_callback_factorial_value = metacall_value_create_function_closure(metacall_function("c_callback_factorial"), c_callback_factorial_impl_value); - - ASSERT_NE((void *) NULL, (void *) c_callback_value); - ASSERT_NE((void *) NULL, (void *) c_callback_with_args_value); - ASSERT_NE((void *) NULL, (void *) c_callback_factorial_impl_value); - ASSERT_NE((void *) NULL, (void *) c_callback_factorial_value); - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) + void *c_callback_value = metacall_value_create_function(metacall_function("c_callback")); + void *c_callback_with_args_value = metacall_value_create_function(metacall_function("c_callback_with_args")); + void *c_callback_factorial_impl_value = metacall_value_create_function(metacall_function("c_callback_factorial_impl")); + void *c_callback_factorial_value = metacall_value_create_function_closure(metacall_function("c_callback_factorial"), c_callback_factorial_impl_value); + + ASSERT_NE((void *)NULL, (void *)c_callback_value); + ASSERT_NE((void *)NULL, (void *)c_callback_with_args_value); + ASSERT_NE((void *)NULL, (void *)c_callback_factorial_impl_value); + ASSERT_NE((void *)NULL, (void *)c_callback_factorial_value); + + /* Test function data */ + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback"))); + EXPECT_EQ((size_t)0, (size_t)metacall_function_size(metacall_function("c_callback"))); + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback_with_args"))); + EXPECT_EQ((size_t)2, (size_t)metacall_function_size(metacall_function("c_callback_with_args"))); + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback_factorial_impl"))); + EXPECT_EQ((size_t)1, (size_t)metacall_function_size(metacall_function("c_callback_factorial_impl"))); + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback_factorial"))); + EXPECT_EQ((size_t)1, (size_t)metacall_function_size(metacall_function("c_callback_factorial"))); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "function.py" }; - void * ret = NULL, * cb_ret = NULL; + void *ret = NULL, *cb_ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * function_args[] = - { + void *function_args[] = { c_callback_value, }; ret = metacallv("function_cb", function_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 32L); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)32L); metacall_value_destroy(ret); - void * function_with_args_args[] = - { + void *function_with_args_args[] = { c_callback_with_args_value, metacall_value_create_long(5L), metacall_value_create_long(9L) @@ -143,29 +150,28 @@ TEST_F(metacall_function_test, DefaultConstructor) ret = metacallv("function_with_args", function_with_args_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 14L); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)14L); metacall_value_destroy(ret); metacall_value_destroy(function_with_args_args[1]); metacall_value_destroy(function_with_args_args[2]); - void * function_ret_lambda_args[] = - { + void *function_ret_lambda_args[] = { metacall_value_create_long(5L) }; ret = metacallv("function_ret_lambda", function_ret_lambda_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - void * f = metacall_value_to_function(ret); + void *f = metacall_value_to_function(ret); cb_ret = metacallfv(f, function_ret_lambda_args); - EXPECT_EQ((long) metacall_value_to_long(cb_ret), (long) 25L); + EXPECT_EQ((long)metacall_value_to_long(cb_ret), (long)25L); metacall_value_destroy(cb_ret); @@ -175,13 +181,13 @@ TEST_F(metacall_function_test, DefaultConstructor) ret = metacallv("function_return", metacall_null_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); f = metacall_value_to_function(ret); cb_ret = metacallfv(f, metacall_null_args); - EXPECT_EQ((long) metacall_value_to_long(cb_ret), (long) 4L); + EXPECT_EQ((long)metacall_value_to_long(cb_ret), (long)4L); metacall_value_destroy(cb_ret); @@ -189,84 +195,76 @@ TEST_F(metacall_function_test, DefaultConstructor) ret = metacallv("function_pass", metacall_null_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret)); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); metacall_value_destroy(ret); - /* TODO: This is a workaround to achieve class / object callbacks between languages. */ - /* It provides interoperatibility but without proper reflection. */ - /* Enough to implement callbacks with opaque pointers between languages. */ - - ret = metacallv("function_capsule_new_class", metacall_null_args); + ret = metacallv("function_myclass_new_class", metacall_null_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_PTR, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(ret)); - void * function_capsule_method_args[] = - { + void *function_myclass_method_args[] = { ret }; - ret = metacallv("function_capsule_method", function_capsule_method_args); - - metacall_value_destroy(function_capsule_method_args[0]); + ret = metacallv("function_myclass_method", function_myclass_method_args); - EXPECT_NE((void *) NULL, (void *) ret); + metacall_value_destroy(function_myclass_method_args[0]); - EXPECT_EQ((enum metacall_value_id) METACALL_STRING, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp("hello world", metacall_value_to_string(ret))); + EXPECT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_STREQ("hello world", metacall_value_to_string(ret)); metacall_value_destroy(ret); - + /* Test Complex Callbakcs */ - void * function_factorial_args[] = - { + void *function_factorial_args[] = { c_callback_factorial_value }; ret = metacallv("function_factorial", function_factorial_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_FUNCTION, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_FUNCTION, (enum metacall_value_id)metacall_value_id(ret)); - void * fact = metacall_value_to_function(ret); + void *fact = metacall_value_to_function(ret); - void * cb_function_factorial_args[] = - { + void *cb_function_factorial_args[] = { metacall_value_create_long(12L) }; cb_ret = metacallfv(fact, cb_function_factorial_args); - EXPECT_NE((void *) NULL, (void *) cb_ret); + EXPECT_NE((void *)NULL, (void *)cb_ret); - EXPECT_EQ((enum metacall_value_id) METACALL_LONG, (enum metacall_value_id) metacall_value_id(cb_ret)); - - /* I have no clue why this returns 132, the correct value for factorial of 12 is 479001600L */ - EXPECT_EQ((long) 132L, (long) metacall_value_to_long(cb_ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(cb_ret)); + + /* TODO: I have no clue why this returns 132, the correct value for factorial of 12 is 479001600L */ + EXPECT_EQ((long)132L, (long)metacall_value_to_long(cb_ret)); metacall_value_destroy(cb_function_factorial_args[0]); metacall_value_destroy(ret); metacall_value_destroy(cb_ret); - } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { /* TODO */ } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ /* Clear function values */ metacall_value_destroy(c_callback_value); @@ -274,5 +272,5 @@ TEST_F(metacall_function_test, DefaultConstructor) metacall_value_destroy(c_callback_factorial_impl_value); metacall_value_destroy(c_callback_factorial_value); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_handle_export_test/CMakeLists.txt b/source/tests/metacall_handle_export_test/CMakeLists.txt index 732a50c7d3..fb1a708563 100644 --- a/source/tests/metacall_handle_export_test/CMakeLists.txt +++ b/source/tests/metacall_handle_export_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,12 +140,21 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_handle_export_test/source/main.cpp b/source/tests/metacall_handle_export_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_handle_export_test/source/main.cpp +++ b/source/tests/metacall_handle_export_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_handle_export_test/source/metacall_handle_export_test.cpp b/source/tests/metacall_handle_export_test/source/metacall_handle_export_test.cpp index 22ab111b8a..3d1afe184a 100644 --- a/source/tests/metacall_handle_export_test/source/metacall_handle_export_test.cpp +++ b/source/tests/metacall_handle_export_test/source/metacall_handle_export_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,89 +32,105 @@ TEST_F(metacall_handle_export_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; - void * v, * handle = NULL; + void *v, *handle = NULL; - char * value_str = NULL; + char *value_str = NULL; size_t size = 0; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), &handle)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), &handle)); - ASSERT_NE((void *) NULL, (void *) handle); + ASSERT_NE((void *)NULL, (void *)handle); v = metacall_handle_export(handle); - EXPECT_NE((void *) NULL, (void *) v); + EXPECT_NE((void *)NULL, (void *)v); value_str = metacall_serialize(metacall_serial(), v, &size, allocator); - EXPECT_NE((char *) NULL, (char *) value_str); + EXPECT_NE((char *)NULL, (char *)value_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << value_str << std::endl; metacall_value_destroy(v); + + metacall_allocator_free(allocator, value_str); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - void * v, * handle = NULL; + void *v, *handle = NULL; - char * value_str = NULL; + char *value_str = NULL; size_t size = 0; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), &handle)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), &handle)); - ASSERT_NE((void *) NULL, (void *) handle); + ASSERT_NE((void *)NULL, (void *)handle); v = metacall_handle_export(handle); - EXPECT_NE((void *) NULL, (void *) v); + EXPECT_NE((void *)NULL, (void *)v); value_str = metacall_serialize(metacall_serial(), v, &size, allocator); - EXPECT_NE((char *) NULL, (char *) value_str); + EXPECT_NE((char *)NULL, (char *)value_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << value_str << std::endl; metacall_value_destroy(v); + + metacall_allocator_free(allocator, value_str); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Test bad handle allocation */ + { + struct + { + int random; + int broken; + int handle; + int yeet; + char padding[600]; + } broken_handle = { 0, 0, 0, 0, { 0 } }; + + EXPECT_EQ((void *)NULL, (void *)metacall_handle_export((void *)&broken_handle)); } - #endif /* OPTION_BUILD_LOADERS_NODE */ /* Print inspect information */ { size_t size = 0; - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -123,5 +139,5 @@ TEST_F(metacall_handle_export_test, DefaultConstructor) metacall_allocator_destroy(allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_handle_get_test/CMakeLists.txt b/source/tests/metacall_handle_get_test/CMakeLists.txt new file mode 100644 index 0000000000..c99af8c34e --- /dev/null +++ b/source/tests/metacall_handle_get_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-handle-get-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_handle_get_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Dependecies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_handle_get_test/source/main.cpp b/source/tests/metacall_handle_get_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_handle_get_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_handle_get_test/source/metacall_handle_get_test.cpp b/source/tests/metacall_handle_get_test/source/metacall_handle_get_test.cpp new file mode 100644 index 0000000000..2e80130384 --- /dev/null +++ b/source/tests/metacall_handle_get_test/source/metacall_handle_get_test.cpp @@ -0,0 +1,162 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_handle_get_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_handle_get_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), &handle)); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *func = metacall_handle_function(handle, "call_test"); + + ASSERT_NE((void *)NULL, (void *)func); + + const enum metacall_value_id double_ids[] = { + METACALL_DOUBLE, METACALL_DOUBLE + }; + + void *ret = metacallht_s(handle, "call_test", double_ids, 2, 10.0, 2.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); + + metacall_value_destroy(ret); + + void *args[] = { + metacall_value_create_double(255.0), + metacall_value_create_double(5.0) + }; + + ret = metacallhv(handle, "call_test", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)1275.0); + + metacall_value_destroy(ret); + + ret = metacallhv_s(handle, "call_test", args, sizeof(args) / sizeof(args[0])); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)1275.0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts_s1[] = { + "s1.py" + }; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts_s1, sizeof(py_scripts_s1) / sizeof(py_scripts_s1[0]), &handle1)); + + ASSERT_NE((void *)NULL, (void *)handle1); + + void *func = metacall_handle_function(handle1, "shared_in_s1_and_s2"); + + ASSERT_NE((void *)NULL, (void *)func); + + void *ret = metacallhv_s(handle1, "shared_in_s1_and_s2", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "Hello from s1"); + + metacall_value_destroy(ret); + + const char *py_scripts_s2[] = { + "s2.py" + }; + + void *handle2 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts_s2, sizeof(py_scripts_s2) / sizeof(py_scripts_s2[0]), &handle2)); + + ASSERT_NE((void *)NULL, (void *)handle2); + + func = metacall_handle_function(handle2, "shared_in_s1_and_s2"); + + ASSERT_NE((void *)NULL, (void *)func); + + ret = metacallhv_s(handle2, "shared_in_s1_and_s2", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "Hello from s2"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + } + + metacall_allocator_destroy(allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_init_fini_test/CMakeLists.txt b/source/tests/metacall_init_fini_test/CMakeLists.txt index bfb277a407..c757cffb72 100644 --- a/source/tests/metacall_init_fini_test/CMakeLists.txt +++ b/source/tests/metacall_init_fini_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_init_fini_test/source/main.cpp b/source/tests/metacall_init_fini_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_init_fini_test/source/main.cpp +++ b/source/tests/metacall_init_fini_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp b/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp index 94561223d0..b7c64132b2 100644 --- a/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp +++ b/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,29 +32,28 @@ TEST_F(metacall_init_fini_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "initfini.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = NULL; + void *ret = NULL; ret = metacall("intermediate_function"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 15, (int) metacall_value_cast_int(&ret)); + EXPECT_EQ((int)15, (int)metacall_value_cast_int(&ret)); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/node_loader_test/CMakeLists.txt b/source/tests/metacall_initialize_destroy_multiple_node_test/CMakeLists.txt similarity index 83% rename from source/tests/node_loader_test/CMakeLists.txt rename to source/tests/metacall_initialize_destroy_multiple_node_test/CMakeLists.txt index d1077ffe55..9b06663fda 100644 --- a/source/tests/node_loader_test/CMakeLists.txt +++ b/source/tests/metacall_initialize_destroy_multiple_node_test/CMakeLists.txt @@ -8,7 +8,7 @@ endif() # # Target name -set(target node-loader-test) +set(target metacall-initialize-destroy-multiple-node-test) message(STATUS "Test ${target}") # @@ -32,7 +32,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/node_loader_test.cpp + ${source_path}/metacall_initialize_destroy_multiple_node_test.cpp ) # Group source files @@ -85,13 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::configuration + ${META_PROJECT_NAME}::metacall ) # @@ -112,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -126,10 +129,17 @@ target_link_libraries(${target} # add_test(NAME ${target} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # diff --git a/source/tests/metacall_initialize_destroy_multiple_node_test/source/main.cpp b/source/tests/metacall_initialize_destroy_multiple_node_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_node_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_initialize_destroy_multiple_node_test/source/metacall_initialize_destroy_multiple_node_test.cpp b/source/tests/metacall_initialize_destroy_multiple_node_test/source/metacall_initialize_destroy_multiple_node_test.cpp new file mode 100644 index 0000000000..3435254d1f --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_node_test/source/metacall_initialize_destroy_multiple_node_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_initialize_destroy_multiple_node_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_initialize_destroy_multiple_node_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = { + "console.log('a')" + }; + + static const char tag[] = "node"; + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + + EXPECT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + + ASSERT_EQ((int)0, (int)metacall_is_initialized(tag)); + + metacall_destroy(); + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + + metacall_destroy(); +} diff --git a/source/tests/metacall_initialize_destroy_multiple_test/CMakeLists.txt b/source/tests/metacall_initialize_destroy_multiple_test/CMakeLists.txt new file mode 100644 index 0000000000..3424ea8817 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-initialize-destroy-multiple-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_initialize_destroy_multiple_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_initialize_destroy_multiple_test/source/main.cpp b/source/tests/metacall_initialize_destroy_multiple_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_initialize_destroy_multiple_test/source/metacall_initialize_destroy_multiple_test.cpp b/source/tests/metacall_initialize_destroy_multiple_test/source/metacall_initialize_destroy_multiple_test.cpp new file mode 100644 index 0000000000..8a57d13521 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_test/source/metacall_initialize_destroy_multiple_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_initialize_destroy_multiple_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_initialize_destroy_multiple_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) + { + static const char *mock_scripts[] = { + "empty.mock" + }; + + static const char tag[] = "mock"; + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + + EXPECT_EQ((int)0, (int)metacall_load_from_file(tag, mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + + ASSERT_EQ((int)0, (int)metacall_is_initialized(tag)); + + metacall_destroy(); + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + } +#endif /* OPTION_BUILD_LOADERS_MOCK */ + + metacall_destroy(); + + metacall_destroy(); +} diff --git a/source/tests/metacall_initialize_ex_test/CMakeLists.txt b/source/tests/metacall_initialize_ex_test/CMakeLists.txt index 6bf3a5e520..714d7ce5b3 100644 --- a/source/tests/metacall_initialize_ex_test/CMakeLists.txt +++ b/source/tests/metacall_initialize_ex_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_initialize_ex_test/source/main.cpp b/source/tests/metacall_initialize_ex_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_initialize_ex_test/source/main.cpp +++ b/source/tests/metacall_initialize_ex_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp b/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp index ab47dbbbc9..d2d9a2ce0a 100644 --- a/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp +++ b/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -30,42 +30,36 @@ class metacall_initialize_ex_test : public testing::Test TEST_F(metacall_initialize_ex_test, DefaultConstructor) { - #if defined(OPTION_BUILD_LOADERS_MOCK) - static char loader_name[] = "mock"; - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#if defined(OPTION_BUILD_LOADERS_MOCK) + static char loader_name[] = "mock"; +#endif /* OPTION_BUILD_LOADERS_MOCK */ - static struct metacall_initialize_configuration_type initialize_config[] = - { - #if defined(OPTION_BUILD_LOADERS_MOCK) - { - loader_name, NULL - }, - #endif /* OPTION_BUILD_LOADERS_MOCK */ + static struct metacall_initialize_configuration_type initialize_config[] = { +#if defined(OPTION_BUILD_LOADERS_MOCK) + { loader_name, NULL }, +#endif /* OPTION_BUILD_LOADERS_MOCK */ - { - NULL, NULL - } + { NULL, NULL } }; metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize_ex(initialize_config)); + ASSERT_EQ((int)0, (int)metacall_initialize_ex(initialize_config)); - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - ASSERT_EQ((int) 1, (int) metacall_is_initialized(loader_name)); + ASSERT_EQ((int)1, (int)metacall_is_initialized(loader_name)); - EXPECT_EQ((int) 0, (int) metacall_load_from_file(loader_name, mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file(loader_name, mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); - ASSERT_EQ((int) 0, (int) metacall_is_initialized(loader_name)); + ASSERT_EQ((int)0, (int)metacall_is_initialized(loader_name)); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - ASSERT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_initialize_test/CMakeLists.txt b/source/tests/metacall_initialize_test/CMakeLists.txt index 2393d238b8..e66bf472e5 100644 --- a/source/tests/metacall_initialize_test/CMakeLists.txt +++ b/source/tests/metacall_initialize_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_initialize_test/source/main.cpp b/source/tests/metacall_initialize_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_initialize_test/source/main.cpp +++ b/source/tests/metacall_initialize_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp b/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp index 8df2fb1aed..598f6f1ea6 100644 --- a/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp +++ b/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,23 +32,22 @@ TEST_F(metacall_initialize_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - ASSERT_EQ((int) 1, (int) metacall_is_initialized("mock")); + ASSERT_EQ((int)1, (int)metacall_is_initialized("mock")); - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); - ASSERT_EQ((int) 0, (int) metacall_is_initialized("mock")); + ASSERT_EQ((int)0, (int)metacall_is_initialized("mock")); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - ASSERT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_inspect_test/CMakeLists.txt b/source/tests/metacall_inspect_test/CMakeLists.txt index e1c97617f0..8a97a0c8b8 100644 --- a/source/tests/metacall_inspect_test/CMakeLists.txt +++ b/source/tests/metacall_inspect_test/CMakeLists.txt @@ -1,12 +1,3 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) - return() -endif() - # # Executable name and options # @@ -63,9 +54,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +88,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +109,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -134,10 +131,47 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13749) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #9 metacall_inspect_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp:101 (metacall-inspect-testd+0x20eaa) + # #10 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-inspect-testd+0x54156) + # #11 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + cs_loader + jsm_loader + js_loader + mock_loader + c_loader +) + # # Define test properties # @@ -146,6 +180,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x600000590 pc=0x7fd0975400f0 sp=0x7fd091d32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_inspect_test/source/main.cpp b/source/tests/metacall_inspect_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_inspect_test/source/main.cpp +++ b/source/tests/metacall_inspect_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp b/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp index 712601318b..36e95f5f7d 100644 --- a/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp +++ b/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -34,91 +34,84 @@ TEST_F(metacall_inspect_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "hello.rb", "second.rb" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript SpiderMonkey */ - #if defined(OPTION_BUILD_LOADERS_JSM) +/* JavaScript SpiderMonkey */ +#if defined(OPTION_BUILD_LOADERS_JSM) { - const char * jsm_scripts[] = - { + const char *jsm_scripts[] = { "spider.jsm" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_JSM */ +#endif /* OPTION_BUILD_LOADERS_JSM */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { - const char * js_scripts[] = - { + const char *js_scripts[] = { "divide.js", "third.js" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - /* C# Netcore */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) { - const char * cs_scripts[] = - { + const char *cs_scripts[] = { "hello.cs" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ - /* C */ - #if defined(OPTION_BUILD_LOADERS_C) +/* C */ +#if defined(OPTION_BUILD_LOADERS_C) { - const char * c_scripts[] = - { + const char *c_scripts[] = { "compiled.c" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_C */ +#endif /* OPTION_BUILD_LOADERS_C */ /* Print inspect information */ { @@ -126,13 +119,13 @@ TEST_F(metacall_inspect_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t)size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); printf("%s\n", inspect_str); @@ -141,5 +134,5 @@ TEST_F(metacall_inspect_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_integration_test/CMakeLists.txt b/source/tests/metacall_integration_test/CMakeLists.txt index bf54e50a05..bf2cc13b9f 100644 --- a/source/tests/metacall_integration_test/CMakeLists.txt +++ b/source/tests/metacall_integration_test/CMakeLists.txt @@ -92,13 +92,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::configuration ${META_PROJECT_NAME}::metacall ) @@ -120,11 +113,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -133,10 +135,51 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13698) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 environment::SetUp() /usr/local/metacall/source/tests/metacall_integration_test/source/environment.cpp:34 (metacall-integration-testd+0x21967) + # #18 testing::internal::UnitTestImpl::RunAllTests() (metacall-integration-testd+0x49791) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader + py_loader +) + # # Define test properties # @@ -145,6 +188,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x600000690 pc=0x7f3a7b6710f0 sp=0x7f3a75e32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp b/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp index 61e7f4b9dc..b2efb0121c 100644 --- a/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp +++ b/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,9 @@ * */ -#include +#include -#include - -class environment : public testing::Environment +class environment : public testing::Environment { public: void SetUp(); diff --git a/source/tests/metacall_integration_test/source/environment.cpp b/source/tests/metacall_integration_test/source/environment.cpp index bbb81cfa82..810c4af687 100644 --- a/source/tests/metacall_integration_test/source/environment.cpp +++ b/source/tests/metacall_integration_test/source/environment.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,22 +19,22 @@ */ #include -#include +#include #include void environment::SetUp() { - const char * py_scripts[] = { "example.py" }; - const char * cs_scripts[] = { "hello.cs" }; + const char *py_scripts[] = { "example.py" }; + const char *cs_scripts[] = { "hello.cs" }; - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); } void environment::TearDown() { - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_integration_test/source/main.cpp b/source/tests/metacall_integration_test/source/main.cpp index 34564df14c..7205061c4c 100644 --- a/source/tests/metacall_integration_test/source/main.cpp +++ b/source/tests/metacall_integration_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,12 @@ * */ -#include +#include #include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new environment()); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_integration_test/source/metacall_integration_test.cpp b/source/tests/metacall_integration_test/source/metacall_integration_test.cpp index 9cd4815bad..629f2b6672 100644 --- a/source/tests/metacall_integration_test/source/metacall_integration_test.cpp +++ b/source/tests/metacall_integration_test/source/metacall_integration_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,16 +18,10 @@ * */ -#include - -#include +#include #include -#include - -#include - class metacall_integration_test : public testing::Test { protected: @@ -45,29 +39,26 @@ TEST_F(metacall_integration_test, CsSayAny) TEST_F(metacall_integration_test, PyMultiply) { - value ret = NULL; + void *ret = NULL; ret = metacall("multiply", 5, 15); - EXPECT_NE((value)NULL, (value)ret); - - EXPECT_EQ((long)value_to_long(ret), (long)75); - - value_destroy(ret); + EXPECT_NE((void *)NULL, (void *)ret); - log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75L); + metacall_value_destroy(ret); } TEST_F(metacall_integration_test, Sum) { - value ret = NULL; + void *ret = NULL; ret = metacall("Sum", 5, 10); - EXPECT_NE((value)NULL, (value)ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int)value_to_long(ret), (int)15); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)15); - value_destroy(ret); + metacall_value_destroy(ret); } diff --git a/source/tests/metacall_invalid_loader_test/CMakeLists.txt b/source/tests/metacall_invalid_loader_test/CMakeLists.txt new file mode 100644 index 0000000000..cef1167c5a --- /dev/null +++ b/source/tests/metacall_invalid_loader_test/CMakeLists.txt @@ -0,0 +1,143 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-invalid-loader-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_invalid_loader_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_invalid_loader_test/source/main.cpp b/source/tests/metacall_invalid_loader_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_invalid_loader_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_invalid_loader_test/source/metacall_invalid_loader_test.cpp b/source/tests/metacall_invalid_loader_test/source/metacall_invalid_loader_test.cpp new file mode 100644 index 0000000000..3ae84d1469 --- /dev/null +++ b/source/tests/metacall_invalid_loader_test/source/metacall_invalid_loader_test.cpp @@ -0,0 +1,52 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_invalid_loader_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_invalid_loader_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char *invalid_scripts[] = { + "a.invalid" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("invalid", invalid_scripts, sizeof(invalid_scripts) / sizeof(invalid_scripts[0]), NULL)); + + static const char invalid_buffer[] = { + "invalid" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("invalid", invalid_buffer, sizeof(invalid_buffer), NULL)); + + ASSERT_EQ((int)1, (int)metacall_is_initialized("invalid")); + + metacall_destroy(); +} diff --git a/source/tests/metacall_java_test/CMakeLists.txt b/source/tests/metacall_java_test/CMakeLists.txt new file mode 100644 index 0000000000..fb814afc1f --- /dev/null +++ b/source/tests/metacall_java_test/CMakeLists.txt @@ -0,0 +1,169 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_JAVA) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-java-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_java_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + java_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # Tracer caught signal 11: addr=0x100000688 pc=0x7fd2e80790f0 sp=0x7fd2489b7d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable Java support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_java_test/source/main.cpp b/source/tests/metacall_java_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_java_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_java_test/source/metacall_java_test.cpp b/source/tests/metacall_java_test/source/metacall_java_test.cpp new file mode 100644 index 0000000000..5eb6141cc5 --- /dev/null +++ b/source/tests/metacall_java_test/source/metacall_java_test.cpp @@ -0,0 +1,416 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_java_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_java_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Java */ +#if defined(OPTION_BUILD_LOADERS_JAVA) + { + { /* TEST LOAD FROM FILE */ + const char *java_scripts[] = { + "Fibonacci.java", "Test.java" + }; + + static const char tag[] = "java"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, java_scripts, sizeof(java_scripts) / sizeof(java_scripts[0]), NULL)); + } + + { /* TEST STATIC GET, SET AND INVOKE */ + void *myclass = metacall_class("Test"); + + void *constructor_params[] = { + metacall_value_create_bool(false), + metacall_value_create_char('H'), + metacall_value_create_short(200), + metacall_value_create_int(10), + metacall_value_create_long(20000007), + metacall_value_create_float(20.321), + metacall_value_create_double(200.123456789), + metacall_value_create_string("Test String", 11) + }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + ASSERT_NE((void *)NULL, (void *)new_object_v); + + metacall_value_destroy(new_object_v); + + { //GET AND SET + void *param1 = metacall_class_static_get(myclass, "LONG_TEST"); + ASSERT_EQ((long)20000007, (long)metacall_value_to_long(param1)); + metacall_value_destroy(param1); + + int longSet = metacall_class_static_set(myclass, "LONG_TEST", metacall_value_create_long(2354363575)); + ASSERT_EQ((int)0, int(longSet)); + + param1 = metacall_class_static_get(myclass, "LONG_TEST"); + ASSERT_EQ((long)2354363575, (long)metacall_value_to_long(param1)); + metacall_value_destroy(param1); + } + + { //GET AND SET + void *param1 = metacall_class_static_get(myclass, "STRING_TEST"); + ASSERT_EQ((std::string) "Test String", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + + int stringSet = metacall_class_static_set(myclass, "STRING_TEST", metacall_value_create_string("ketangupta34", 12)); + ASSERT_EQ((int)0, int(stringSet)); + + param1 = metacall_class_static_get(myclass, "STRING_TEST"); + ASSERT_EQ((std::string) "ketangupta34", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + } + + { //GET CLASS + void *class_test = metacall_class_static_get(myclass, "CLASS_TEST"); + ASSERT_EQ((void *)metacall_class("Test"), (void *)metacall_value_to_class(class_test)); + metacall_value_destroy(class_test); + } + + { //GET ARRAYS + void *str_test = metacall_class_static_get(myclass, "STRING_TEST_Arr"); + void **str_test_arr = metacall_value_to_array(str_test); + ASSERT_STREQ(metacall_value_to_string(str_test_arr[0]), "Hello"); + ASSERT_STREQ(metacall_value_to_string(str_test_arr[1]), "world"); + metacall_value_destroy(str_test); + + void *class_test = metacall_class_static_get(myclass, "CLASS_TEST_Arr"); + void **class_test_arr = metacall_value_to_array(class_test); + ASSERT_EQ((void *)metacall_class("Test"), (void *)metacall_value_to_class(class_test_arr[0])); + metacall_value_destroy(class_test); + } + + { //Invoke + void *args[] = { + metacall_value_create_string("Metacall", 8), + metacall_value_create_int(8) + }; + void *ret = metacallt_class(myclass, "testFunct", METACALL_INT, args, 2); + ASSERT_EQ((int)10, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } + } + + { /* TEST NON STATIC GET, SET and INVOKE FROM CLASS OBJECT */ + void *myclass = metacall_class("Test"); + + void *constructor_params[] = { + metacall_value_create_int(10), + metacall_value_create_int(20) + }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + void *new_object = metacall_value_to_object(new_object_v); + + { + void *param1 = metacall_object_get(new_object, "INT_NS"); + ASSERT_EQ((int)231, (int)metacall_value_to_int(param1)); + metacall_value_destroy(param1); + + int intobjset = metacall_object_set(new_object, "INT_NS", metacall_value_create_int(4321)); + ASSERT_EQ((int)0, int(intobjset)); + + param1 = metacall_object_get(new_object, "INT_NS"); + ASSERT_EQ((int)4321, (int)metacall_value_to_int(param1)); + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_object_get(new_object, "CHAR_NS"); + ASSERT_EQ((char)'Z', (char)metacall_value_to_char(param1)); + metacall_value_destroy(param1); + + int charobjset = metacall_object_set(new_object, "CHAR_NS", metacall_value_create_char('Y')); + ASSERT_EQ((int)0, int(charobjset)); + + param1 = metacall_object_get(new_object, "CHAR_NS"); + ASSERT_EQ((char)'Y', (char)metacall_value_to_char(param1)); + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_object_get(new_object, "STRING_NS"); + ASSERT_EQ((std::string) "NS string", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + + int strobjset = metacall_object_set(new_object, "STRING_NS", metacall_value_create_string("UPDATED", 7)); + ASSERT_EQ((int)0, int(strobjset)); + + param1 = metacall_object_get(new_object, "STRING_NS"); + ASSERT_EQ((std::string) "UPDATED", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + } + + { //Non Static function Invoke + void *args[] = { + metacall_value_create_int(8) + }; + void *ret = metacallt_object(new_object, "TestNonStaticInt", METACALL_INT, args, 1); + ASSERT_EQ((int)80, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } + + metacall_value_destroy(new_object_v); + } + + { /* TEST String Array Constructor, GET, SET and Invoke for STATIC */ + void *myclass = metacall_class("Test"); + + const void *constructor_params12[] = { + metacall_value_create_string("Constructor", 11), + metacall_value_create_string("Called", 6) + }; + void *val = metacall_value_create_array(constructor_params12, 2); + void *constructor_params[] = { val }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + ASSERT_NE((void *)NULL, (void *)new_object_v); + + metacall_value_destroy(new_object_v); + + { + void *param1 = metacall_class_static_get(myclass, "INT_TEST_Arr"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((int)30, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)12, (int)metacall_value_to_int(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_array[] = { + metacall_value_create_int(10), + metacall_value_create_int(20), + metacall_value_create_int(30) + }; + void *val = metacall_value_create_array(Array_test_array, 3); + + int intArrTest = metacall_class_static_set(myclass, "INT_TEST_Arr", val); + ASSERT_EQ((int)0, int(intArrTest)); + + param1 = metacall_class_static_get(myclass, "INT_TEST_Arr"); + array = metacall_value_to_array(param1); + EXPECT_EQ((int)10, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)20, (int)metacall_value_to_int(array[1])); + EXPECT_EQ((int)30, (int)metacall_value_to_int(array[2])); + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_class_static_get(myclass, "CHAR_TEST_Arr"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((char)'G', (char)metacall_value_to_char(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_char[] = { + metacall_value_create_bool('G'), + metacall_value_create_bool('K') + }; + void *val = metacall_value_create_array(Array_test_char, 2); + + int boolset = metacall_class_static_set(myclass, "CHAR_TEST_Arr", val); + ASSERT_EQ((int)0, int(boolset)); + + param1 = metacall_class_static_get(myclass, "CHAR_TEST_Arr"); + array = metacall_value_to_array(param1); + EXPECT_EQ((char)'K', (char)metacall_value_to_char(array[1])); + + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_class_static_get(myclass, "STRING_TEST_Arr"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((std::string) "Hello", (std::string)metacall_value_to_string(array[0])); + EXPECT_EQ((std::string) "world", (std::string)metacall_value_to_string(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_string[] = { + metacall_value_create_string("abc", 3), + metacall_value_create_string("def", 3), + metacall_value_create_string("ghi", 3) + }; + void *val = metacall_value_create_array(Array_test_string, 3); + + int boolset = metacall_class_static_set(myclass, "STRING_TEST_Arr", val); + ASSERT_EQ((int)0, int(boolset)); + + param1 = metacall_class_static_get(myclass, "STRING_TEST_Arr"); + array = metacall_value_to_array(param1); + EXPECT_EQ((std::string) "abc", (std::string)metacall_value_to_string(array[0])); + EXPECT_EQ((std::string) "def", (std::string)metacall_value_to_string(array[1])); + EXPECT_EQ((std::string) "ghi", (std::string)metacall_value_to_string(array[2])); + + metacall_value_destroy(param1); + } + + { // Testing MAIN function call + void *args[] = { + metacall_value_create_array({}, 0) + }; + + void *ret = metacallv_class(myclass, "main", args, 1); + metacall_value_destroy(ret); + } + + { + const void *Array_test_int[] = { + metacall_value_create_int(2), + metacall_value_create_int(3), + metacall_value_create_int(5) + }; + + void *args[] = { + metacall_value_create_array(Array_test_int, 3) + }; + + void *ret = metacallt_class(myclass, "testIntArrayAdd", METACALL_INT, args, 1); + EXPECT_EQ((int)10, (int)metacall_value_to_int(ret)); + } + + // { + // const void *Array_test_string[] = { + // metacall_value_create_string("aaaa", 4), + // metacall_value_create_string("bbbb", 4), + // metacall_value_create_string("cccc", 4) + // }; + // void *args[] = { + // metacall_value_create_array(Array_test_string, 3) + // }; + + // void *ret = metacallt_class(myclass, "testStringArrayFunction", METACALL_ARRAY, args, 1); + // void **array = metacall_value_to_array(ret); + // EXPECT_EQ((std::string) "test", (std::string)metacall_value_to_string(array[0])); + // EXPECT_EQ((std::string) "worked", (std::string)metacall_value_to_string(array[1])); + // } + } + + { /* TEST Array GET, SET and Invoke for NON STATIC */ + void *myclass = metacall_class("Test"); + + const void *constructor_params12[] = { + metacall_value_create_string("Constructor", 11), + metacall_value_create_string("Called", 6) + }; + void *val = metacall_value_create_array(constructor_params12, 2); + void *constructor_params[] = { val }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + void *new_object = metacall_value_to_object(new_object_v); + + { + void *param1 = metacall_object_get(new_object, "INT_TEST_Arr_NS"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((int)50, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)100, (int)metacall_value_to_int(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_array[] = { + metacall_value_create_int(10), + metacall_value_create_int(20), + metacall_value_create_int(30) + }; + void *val = metacall_value_create_array(Array_test_array, 3); + + int intArrTest = metacall_object_set(new_object, "INT_TEST_Arr_NS", val); + ASSERT_EQ((int)0, int(intArrTest)); + + param1 = metacall_object_get(new_object, "INT_TEST_Arr_NS"); + array = metacall_value_to_array(param1); + EXPECT_EQ((int)10, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)20, (int)metacall_value_to_int(array[1])); + EXPECT_EQ((int)30, (int)metacall_value_to_int(array[2])); + metacall_value_destroy(param1); + } + + metacall_value_destroy(new_object_v); + } + + { /* TEST LOAD FROM PACKAGE get, set and invoke for Static and Non Static */ + ASSERT_EQ((int)0, (int)metacall_load_from_package("java", "JarTest.jar", NULL)); + + void *myclass = metacall_class("src.JarTest.TestJar"); + + void *new_object_v = metacall_class_new(myclass, "src.JarTest.TestJar", {}, 0); + + { + void *param = metacall_class_static_get(myclass, "jarTestString"); + ASSERT_EQ((std::string) "This is a static Jar String", (std::string)metacall_value_to_string(param)); + metacall_value_destroy(param); + + int stringSet = metacall_class_static_set(myclass, "jarTestString", metacall_value_create_string("ketangupta34", 12)); + ASSERT_EQ((int)0, int(stringSet)); + + param = metacall_class_static_get(myclass, "jarTestString"); + ASSERT_EQ((std::string) "ketangupta34", (std::string)metacall_value_to_string(param)); + metacall_value_destroy(param); + } + + metacall_value_destroy(new_object_v); + } + + // { // Test load from memory + // static const char buffer[] = + // "public class memoryTest{" + // "public static String memoryString = \"Memory test string\";" + // "}"; + + // EXPECT_EQ((int)0, (int)metacall_load_from_memory("java", buffer, sizeof(buffer), NULL)); + // } + } +#endif /* OPTION_BUILD_LOADERS_JAVA */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_julia_test/CMakeLists.txt b/source/tests/metacall_julia_test/CMakeLists.txt new file mode 100644 index 0000000000..0bfe22f233 --- /dev/null +++ b/source/tests/metacall_julia_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_JL OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_JL) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-julia-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_julia_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + julia_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_julia_test/source/main.cpp b/source/tests/metacall_julia_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_julia_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_julia_test/source/metacall_julia_test.cpp b/source/tests/metacall_julia_test/source/metacall_julia_test.cpp new file mode 100644 index 0000000000..d85bd9ad0d --- /dev/null +++ b/source/tests/metacall_julia_test/source/metacall_julia_test.cpp @@ -0,0 +1,102 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_julia_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_julia_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Julia */ +#if defined(OPTION_BUILD_LOADERS_JL) + { + const char *julia_scripts[] = { + "new.jl" + }; + + const enum metacall_value_id plus_string_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + const size_t args_size = sizeof(plus_string_ids) / sizeof(plus_string_ids[0]); + + static const char tag[] = "jl"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, julia_scripts, sizeof(julia_scripts) / sizeof(julia_scripts[0]), NULL)); + + /* TODO: Uncomment this when calls are implemented */ + /* + void *ret = metacallt_s("plus", hello_string_ids, args_size, 3.4f, 12.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)15.4f); + + metacall_value_destroy(ret); + */ + + /* TODO: Test load from memory */ + /* + static const char buffer[] = + "function plus_ten(a)\n" + " return a + 10\n" + "end\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + */ + + /* TODO: Call to the functions of the load from memory script */ + + /* TODO: Test load from package (in case Julia supports compiled binaries, otherwise delete this line) */ + } +#endif /* OPTION_BUILD_LOADERS_JL */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_library_path_without_env_vars_test/CMakeLists.txt b/source/tests/metacall_library_path_without_env_vars_test/CMakeLists.txt new file mode 100644 index 0000000000..33428a51b8 --- /dev/null +++ b/source/tests/metacall_library_path_without_env_vars_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-library-path-without-env-vars-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_library_path_without_env_vars_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_CONFIGURATION_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_library_path_without_env_vars_test/source/main.cpp b/source/tests/metacall_library_path_without_env_vars_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_library_path_without_env_vars_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_library_path_without_env_vars_test/source/metacall_library_path_without_env_vars_test.cpp b/source/tests/metacall_library_path_without_env_vars_test/source/metacall_library_path_without_env_vars_test.cpp new file mode 100644 index 0000000000..f971670af2 --- /dev/null +++ b/source/tests/metacall_library_path_without_env_vars_test/source/metacall_library_path_without_env_vars_test.cpp @@ -0,0 +1,49 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_library_path_without_env_vars_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_library_path_without_env_vars_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) + { + const char *mock_scripts[] = { + "empty.mock" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_MOCK */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_llvm_test/CMakeLists.txt b/source/tests/metacall_llvm_test/CMakeLists.txt new file mode 100644 index 0000000000..cbe63c93f1 --- /dev/null +++ b/source/tests/metacall_llvm_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_LLVM OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_LLVM) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-llvm-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_llvm_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + llvm_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_llvm_test/source/main.cpp b/source/tests/metacall_llvm_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_llvm_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_llvm_test/source/metacall_llvm_test.cpp b/source/tests/metacall_llvm_test/source/metacall_llvm_test.cpp new file mode 100644 index 0000000000..fe1e961250 --- /dev/null +++ b/source/tests/metacall_llvm_test/source/metacall_llvm_test.cpp @@ -0,0 +1,103 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_llvm_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_llvm_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* LLVM */ +#if defined(OPTION_BUILD_LOADERS_LLVM) + { + const char *llvm_scripts[] = { + "input.ll" + }; + + const enum metacall_value_id hello_string_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + const size_t args_size = sizeof(hello_string_ids) / sizeof(hello_string_ids[0]); + + static const char tag[] = "llvm"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, llvm_scripts, sizeof(llvm_scripts) / sizeof(llvm_scripts[0]), NULL)); + + /* TODO: Uncomment this when calls are implemented */ + /* + void *ret = ret = metacallt_s("adder", hello_string_ids, args_size, 3.4f, 12.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)15.4f); + + metacall_value_destroy(ret); + */ + + /* TODO: Test load from memory */ + /* + static const char buffer[] = + "define dso_local float @whatever(float, float) #0 {\n" + "...\n" + "...\n" + "...\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + */ + + /* TODO: Call to the functions of the load from memory script */ + + /* TODO: Test load from package */ + } +#endif /* OPTION_BUILD_LOADERS_LLVM */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_fail_test/CMakeLists.txt b/source/tests/metacall_load_configuration_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..c49bf0e10d --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/CMakeLists.txt @@ -0,0 +1,160 @@ + +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-load-configuration-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_configuration_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Configuration path + METACALL_TEST_CONFIG_PATH="${CMAKE_CURRENT_SOURCE_DIR}/data/" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-object.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-object.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-object.json @@ -0,0 +1 @@ +{} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-scripts.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-scripts.json new file mode 100644 index 0000000000..b0caa4d695 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-scripts.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-empty.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-without-language-id.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-language-id.json new file mode 100644 index 0000000000..0ddfb1e908 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-language-id.json @@ -0,0 +1,6 @@ +{ + "path": ".", + "scripts": [ + "pluginA.js" + ] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-without-scripts.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-scripts.json new file mode 100644 index 0000000000..cbd571bd89 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-scripts.json @@ -0,0 +1,4 @@ +{ + "language_id": "node", + "path": "." +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id-type.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id-type.json new file mode 100644 index 0000000000..f8e6caa10a --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id-type.json @@ -0,0 +1,5 @@ +{ + "language_id": [], + "path": "./js", + "scripts": [] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id.json new file mode 100644 index 0000000000..d00203bc53 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id.json @@ -0,0 +1,7 @@ +{ + "language_id": "", + "path": ".", + "scripts": [ + "pluginA.js" + ] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path-type.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path-type.json new file mode 100644 index 0000000000..bef54c140f --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path-type.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": 3, + "scripts": [] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path.json new file mode 100644 index 0000000000..18da4b3db6 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": "", + "scripts": [ + "pluginA.js" + ] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts-type.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts-type.json new file mode 100644 index 0000000000..6ddb5d86b3 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts-type.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": "./js", + "scripts": "hello" +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts.json new file mode 100644 index 0000000000..30bea4e448 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [""] +} diff --git a/source/tests/metacall_load_configuration_fail_test/source/main.cpp b/source/tests/metacall_load_configuration_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_configuration_fail_test/source/metacall_load_configuration_fail_test.cpp b/source/tests/metacall_load_configuration_fail_test/source/metacall_load_configuration_fail_test.cpp new file mode 100644 index 0000000000..0fe69549c7 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/source/metacall_load_configuration_fail_test.cpp @@ -0,0 +1,76 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_configuration_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_configuration_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + #define CONFIG_PATH(path) METACALL_TEST_CONFIG_PATH path + + /* Script with config */ + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-language-id.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-path.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-scripts.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-empty-scripts.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-without-language-id.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-without-scripts.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-empty-object.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-empty.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-scripts-type.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-path-type.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-language-id-type.json"), NULL, config_allocator)); + + metacall_allocator_destroy(config_allocator); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_node_python_test/CMakeLists.txt b/source/tests/metacall_load_configuration_node_python_test/CMakeLists.txt new file mode 100644 index 0000000000..6653f9010f --- /dev/null +++ b/source/tests/metacall_load_configuration_node_python_test/CMakeLists.txt @@ -0,0 +1,166 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-load-configuration-node-python-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_configuration_node_python_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/auth-function-mesh/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_configuration_node_python_test/source/main.cpp b/source/tests/metacall_load_configuration_node_python_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_configuration_node_python_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_configuration_node_python_test/source/metacall_load_configuration_node_python_test.cpp b/source/tests/metacall_load_configuration_node_python_test/source/metacall_load_configuration_node_python_test.cpp new file mode 100644 index 0000000000..09d50c5931 --- /dev/null +++ b/source/tests/metacall_load_configuration_node_python_test/source/metacall_load_configuration_node_python_test.cpp @@ -0,0 +1,59 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_configuration_node_python_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_configuration_node_python_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_execution_path("py", METACALL_PYTHON_PORT_PATH)); + + static const char buffer[] = + "const { metacall_load_from_configuration_export } = require('" METACALL_NODE_PORT_PATH "');\n" + "const config = metacall_load_from_configuration_export('" METACALL_TEST_CONFIG_PATH "');\n" + "module.exports = { enc: (v) => config.encrypt(v) }\n"; + + static const char tag[] = "node"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + + const enum metacall_value_id node_memory_enc_ids[] = { + METACALL_DOUBLE + }; + + void *ret = metacallt("enc", node_memory_enc_ids, 15.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)5.0, (double)metacall_value_to_double(ret)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_python_node_test/CMakeLists.txt b/source/tests/metacall_load_configuration_python_node_test/CMakeLists.txt new file mode 100644 index 0000000000..f8dd16c325 --- /dev/null +++ b/source/tests/metacall_load_configuration_python_node_test/CMakeLists.txt @@ -0,0 +1,163 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-load-configuration-python-node-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_configuration_python_node_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/auth-function-mesh/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_configuration_python_node_test/source/main.cpp b/source/tests/metacall_load_configuration_python_node_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_configuration_python_node_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_configuration_python_node_test/source/metacall_load_configuration_python_node_test.cpp b/source/tests/metacall_load_configuration_python_node_test/source/metacall_load_configuration_python_node_test.cpp new file mode 100644 index 0000000000..ece10c5b20 --- /dev/null +++ b/source/tests/metacall_load_configuration_python_node_test/source/metacall_load_configuration_python_node_test.cpp @@ -0,0 +1,66 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_configuration_python_node_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_configuration_python_node_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)0, (int)metacall_execution_path("py", METACALL_PYTHON_PORT_PATH)); + + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(METACALL_TEST_CONFIG_PATH, NULL, config_allocator)); + + void *ret = metacall("encrypt", 15.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)5.0); + + metacall_value_destroy(ret); + + ret = metacall("decrypt", 15.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + metacall_allocator_destroy(config_allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt b/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt index 3feb0a0109..a16c0c46f7 100644 --- a/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt +++ b/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + # # Executable name and options # @@ -81,20 +86,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -116,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -133,6 +133,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Configure test data # diff --git a/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in b/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in index dc601c23b6..79701d0f7b 100644 --- a/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in +++ b/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/source/tests/metacall_load_configuration_relative_test/source/main.cpp b/source/tests/metacall_load_configuration_relative_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_load_configuration_relative_test/source/main.cpp +++ b/source/tests/metacall_load_configuration_relative_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp b/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp index eb096c0ae8..be9556f5e1 100644 --- a/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp +++ b/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,11 @@ #include -#include +#include #include #include -#include - -#include - class metacall_load_configuration_relative_test : public testing::Test { public: @@ -38,35 +34,36 @@ TEST_F(metacall_load_configuration_relative_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - ASSERT_NE((memory_allocator) NULL, (memory_allocator) allocator); + ASSERT_NE((void *)NULL, (void *)config_allocator); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration(RELATIVE_CONFIGURATION_PATH "metacall_load_from_configuration_relative_node_test.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(RELATIVE_CONFIGURATION_PATH "metacall_load_from_configuration_relative_node_test.json", NULL, config_allocator)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - memory_allocator_destroy(allocator); + metacall_allocator_destroy(config_allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_load_configuration_test/CMakeLists.txt b/source/tests/metacall_load_configuration_test/CMakeLists.txt index 6723b8d49d..3c42adeb57 100644 --- a/source/tests/metacall_load_configuration_test/CMakeLists.txt +++ b/source/tests/metacall_load_configuration_test/CMakeLists.txt @@ -80,20 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,15 +127,25 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader + rb_loader +) + # # Configure test data # if(MSVC) - configure_file(data/metacall_load_from_configuration_py_test_a.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_py_test_a.json) - configure_file(data/metacall_load_from_configuration_py_test_b.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_py_test_b.json) - configure_file(data/metacall_load_from_configuration_rb_test.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_rb_test.json) - configure_file(data/metacall_load_from_configuration_node_test.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_node_test.json) + configure_file(data/metacall_load_from_configuration_py_test_a.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_py_test_a.json) + configure_file(data/metacall_load_from_configuration_py_test_b.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_py_test_b.json) + configure_file(data/metacall_load_from_configuration_rb_test.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_rb_test.json) + configure_file(data/metacall_load_from_configuration_node_test.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_node_test.json) endif() configure_file(data/metacall_load_from_configuration_py_test_a.json.in ${CMAKE_CURRENT_BINARY_DIR}/metacall_load_from_configuration_py_test_a.json) diff --git a/source/tests/metacall_load_configuration_test/source/main.cpp b/source/tests/metacall_load_configuration_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_load_configuration_test/source/main.cpp +++ b/source/tests/metacall_load_configuration_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp b/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp index 9f5879a727..21657b9c8e 100644 --- a/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp +++ b/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,11 @@ * */ -#include +#include #include #include -#include - -#include - class metacall_load_configuration_test : public testing::Test { public: @@ -36,28 +32,30 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - ASSERT_NE((memory_allocator) NULL, (memory_allocator) allocator); + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) + ASSERT_NE((void *)NULL, (void *)config_allocator); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { const long seven_multiples_limit = 10; long iterator; - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_py_test_a.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_py_test_a.json", NULL, config_allocator)); ret = metacall("multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -65,56 +63,56 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) { ret = metacall("multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); ret = metacall("sum", 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 4500); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)4500); metacall_value_destroy(ret); ret = metacall("sum", 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 7); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); metacall_value_destroy(ret); ret = metacall("hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret)); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); metacall_value_destroy(ret); ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_py_test_b.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_py_test_b.json", NULL, config_allocator)); /* Print inspect information */ { @@ -122,13 +120,13 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -139,9 +137,9 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) ret = metacall("s_multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -149,113 +147,112 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) { ret = metacall("s_multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("s_divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); ret = metacall("s_sum", 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 4500); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)4500); metacall_value_destroy(ret); ret = metacall("s_sum", 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 7); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); metacall_value_destroy(ret); ret = metacall("s_hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret)); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); metacall_value_destroy(ret); ret = metacall("s_strcat", "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_rb_test.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_rb_test.json", NULL, config_allocator)); ret = metacall("say_multiply", 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 35); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)35); metacall_value_destroy(ret); ret = metacall("say_null"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); metacall_value_destroy(ret); ret = metacall("say_hello", "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello meta-programmer!"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_node_test.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_node_test.json", NULL, config_allocator)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - memory_allocator_destroy(allocator); + metacall_allocator_destroy(config_allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_load_memory_empty_test/CMakeLists.txt b/source/tests/metacall_load_memory_empty_test/CMakeLists.txt new file mode 100644 index 0000000000..b98f18bca6 --- /dev/null +++ b/source/tests/metacall_load_memory_empty_test/CMakeLists.txt @@ -0,0 +1,154 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-load-memory-empty-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_memory_empty_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_memory_empty_test/source/main.cpp b/source/tests/metacall_load_memory_empty_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_memory_empty_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_memory_empty_test/source/metacall_load_memory_empty_test.cpp b/source/tests/metacall_load_memory_empty_test/source/metacall_load_memory_empty_test.cpp new file mode 100644 index 0000000000..4adffb8e4a --- /dev/null +++ b/source/tests/metacall_load_memory_empty_test/source/metacall_load_memory_empty_test.cpp @@ -0,0 +1,71 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_memory_empty_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_memory_empty_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char buffer[] = ""; + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("ts", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Non existent loader */ + ASSERT_EQ((int)1, (int)metacall_load_from_memory("asdfghjk", buffer, sizeof(buffer), NULL)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_memory_test/CMakeLists.txt b/source/tests/metacall_load_memory_test/CMakeLists.txt index 8d300db96e..9203a34ca2 100644 --- a/source/tests/metacall_load_memory_test/CMakeLists.txt +++ b/source/tests/metacall_load_memory_test/CMakeLists.txt @@ -80,20 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,6 +127,16 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + js_loader +) + # # Define test properties # diff --git a/source/tests/metacall_load_memory_test/source/main.cpp b/source/tests/metacall_load_memory_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_load_memory_test/source/main.cpp +++ b/source/tests/metacall_load_memory_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp b/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp index 726170eb5c..9610ddf73c 100644 --- a/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp +++ b/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,11 @@ * */ -#include +#include #include #include -#include - -#include - class metacall_load_memory_test : public testing::Test { public: @@ -34,16 +30,16 @@ class metacall_load_memory_test : public testing::Test TEST_F(metacall_load_memory_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - metacall_print_info(); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char buffer[] = "#!/usr/bin/env python3\n" @@ -58,35 +54,33 @@ TEST_F(metacall_load_memory_test, DefaultConstructor) long iterator; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); - value ret = NULL; + void *ret = NULL; ret = metacall("multmem", 5, 15); - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((long) value_to_long(ret), (long) 75); + EXPECT_NE((void *)NULL, (void *)ret); - value_destroy(ret); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); - log_write("metacall", LOG_LEVEL_DEBUG, "5's multiples dude!"); + metacall_value_destroy(ret); for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) { ret = metacall("multmem", 5, iterator); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) value_to_long(ret), (long) (5 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(5 * iterator)); - value_destroy(ret); + metacall_value_destroy(ret); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { static const char buffer[] = "#!/usr/bin/ruby\n" @@ -108,30 +102,30 @@ TEST_F(metacall_load_memory_test, DefaultConstructor) static const char extension[] = "rb"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); - value ret = NULL; + void *ret = NULL; ret = metacall("mem_multiply", 5, 5); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) value_to_int(ret), (int) 25); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)25); - value_destroy(ret); + metacall_value_destroy(ret); ret = metacall("comment_line", 15); - EXPECT_EQ((value) NULL, (value) ret); + EXPECT_EQ((void *)NULL, (void *)ret); ret = metacall("comment_multi_line", 25); - EXPECT_EQ((value) NULL, (value) ret); + EXPECT_EQ((void *)NULL, (void *)ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { static const char buffer[] = "#!/usr/bin/env sh\n" @@ -145,23 +139,23 @@ TEST_F(metacall_load_memory_test, DefaultConstructor) static const char extension[] = "js"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); - value ret = NULL; + void *ret = NULL; ret = metacall("mem_divide", 10.0, 5.0); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 2.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)2.0); - value_destroy(ret); + metacall_value_destroy(ret); ret = metacall("mem_comment", 10.0); - EXPECT_EQ((value) NULL, (value) ret); + EXPECT_EQ((void *)NULL, (void *)ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_logs_test/CMakeLists.txt b/source/tests/metacall_logs_test/CMakeLists.txt index 2dde68c796..2a56bf6371 100644 --- a/source/tests/metacall_logs_test/CMakeLists.txt +++ b/source/tests/metacall_logs_test/CMakeLists.txt @@ -80,20 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/metacall_logs_test/source/main.cpp b/source/tests/metacall_logs_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_logs_test/source/main.cpp +++ b/source/tests/metacall_logs_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_logs_test/source/metacall_logs_test.cpp b/source/tests/metacall_logs_test/source/metacall_logs_test.cpp index a9ccc8129c..179554ab25 100644 --- a/source/tests/metacall_logs_test/source/metacall_logs_test.cpp +++ b/source/tests/metacall_logs_test/source/metacall_logs_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -35,10 +35,10 @@ TEST_F(metacall_logs_test, DefaultConstructor) for (int i = 0; i < 50; ++i) { - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); } - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_lua_test/CMakeLists.txt b/source/tests/metacall_lua_test/CMakeLists.txt new file mode 100644 index 0000000000..d356553b5c --- /dev/null +++ b/source/tests/metacall_lua_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_LUA OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_LUA) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-lua-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_lua_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + lua_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/py_loader_port_test/source/main.cpp b/source/tests/metacall_lua_test/source/main.cpp similarity index 81% rename from source/tests/py_loader_port_test/source/main.cpp rename to source/tests/metacall_lua_test/source/main.cpp index 8371fcd178..7832a63230 100644 --- a/source/tests/py_loader_port_test/source/main.cpp +++ b/source/tests/metacall_lua_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading python code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_lua_test/source/metacall_lua_test.cpp b/source/tests/metacall_lua_test/source/metacall_lua_test.cpp new file mode 100644 index 0000000000..63c84b4375 --- /dev/null +++ b/source/tests/metacall_lua_test/source/metacall_lua_test.cpp @@ -0,0 +1,114 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_lua_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_lua_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Lua */ +#if defined(OPTION_BUILD_LOADERS_LUA) + { + static const char tag[] = "lua"; + + /* Load from memory */ + { + static const char buffer[] = + "function luamin(left, right)\n" + " if (left > right) then\n" + " return right;\n" + " else\n" + " return left;\n" + " end\n" + "end\n"; + + const enum metacall_value_id min_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + + void *ret = metacallt_s("luamin", min_ids, 2, 3.0f, 6.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)3.0f); + + metacall_value_destroy(ret); + } + + /* Load from file */ + { + const char *lua_scripts[] = { + "max.lua" + }; + + const enum metacall_value_id max_ids[] = { + METACALL_INT, METACALL_INT + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, lua_scripts, sizeof(lua_scripts) / sizeof(lua_scripts[0]), NULL)); + + void *ret = metacallt_s("luamax", max_ids, 2, 3, 6); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)6); + + metacall_value_destroy(ret); + } + } +#endif /* OPTION_BUILD_LOADERS_LUA */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_map_await_test/CMakeLists.txt b/source/tests/metacall_map_await_test/CMakeLists.txt index 8b909e1093..6b1df3e956 100644 --- a/source/tests/metacall_map_await_test/CMakeLists.txt +++ b/source/tests/metacall_map_await_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,6 +132,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # diff --git a/source/tests/metacall_map_await_test/source/main.cpp b/source/tests/metacall_map_await_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_map_await_test/source/main.cpp +++ b/source/tests/metacall_map_await_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_map_await_test/source/metacall_map_await_test.cpp b/source/tests/metacall_map_await_test/source/metacall_map_await_test.cpp index 44e39acd15..d3f1d4eb76 100644 --- a/source/tests/metacall_map_await_test/source/metacall_map_await_test.cpp +++ b/source/tests/metacall_map_await_test/source/metacall_map_await_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,79 +18,87 @@ * */ -#include +#include #include -#include #include +#include + +#include + +#include -#include +std::atomic success_callbacks{}; class metacall_map_await_test : public testing::Test { public: }; -static void * hello_boy_await_ok(void * result, void * data) +static void *hello_boy_await_ok(void *result, void *data) { - double * it = static_cast(data); + double *it = static_cast(data); - EXPECT_NE((void *) NULL, (void *) result); + EXPECT_NE((void *)NULL, (void *)result); - EXPECT_NE((void *) NULL, (void *) data); + EXPECT_NE((void *)NULL, (void *)data); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); printf("hello_boy_await future (from map) callback: %f\n", metacall_value_to_double(result)); fflush(stdout); - EXPECT_EQ((double) metacall_value_to_double(result), (double) (7.0 + *it)); + EXPECT_EQ((double)metacall_value_to_double(result), (double)(7.0 + *it)); delete it; + ++success_callbacks; + return NULL; } -static void * hello_boy_await_fail(void *, void * data) +static void *hello_boy_await_fail(void *, void *data) { - double * it = static_cast(data); + double *it = static_cast(data); int this_should_never_happen = 1; - EXPECT_NE((void *) NULL, (void *) data); + EXPECT_NE((void *)NULL, (void *)data); delete it; - EXPECT_NE((int) 0, (int) this_should_never_happen); + EXPECT_NE((int)0, (int)this_should_never_happen); return NULL; } -static void * hello_world_await_fail(void *, void * data) +static void *hello_world_await_fail(void *, void *data) { int this_should_never_happen = 1; - EXPECT_EQ((void *) NULL, (void *) data); + EXPECT_EQ((void *)NULL, (void *)data); - EXPECT_NE((int) 0, (int) this_should_never_happen); + EXPECT_NE((int)0, (int)this_should_never_happen); return NULL; } -static void * hello_world_await_ok(void * result, void * data) +static void *hello_world_await_ok(void *result, void *data) { - EXPECT_NE((void *) NULL, (void *) result); + EXPECT_NE((void *)NULL, (void *)result); - EXPECT_EQ((void *) NULL, (void *) data); + EXPECT_EQ((void *)NULL, (void *)data); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_STRING); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_STRING); printf("hello_world callback: %s\n", metacall_value_to_string(result)); fflush(stdout); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(result), "Hello World")); + EXPECT_STREQ(metacall_value_to_string(result), "Hello World"); + + ++success_callbacks; return NULL; } @@ -99,99 +107,130 @@ TEST_F(metacall_map_await_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); static const char left[] = "a"; static const char right[] = "b"; - void * keys[] = - { + void *keys[] = { metacall_value_create_string(left, sizeof(left) - 1), metacall_value_create_string(right, sizeof(right) - 1) }; - void * values[] = - { + void *values[] = { metacall_value_create_double(7.0), metacall_value_create_double(0.0) }; static const char args_map[] = "{\"a\":10,\"b\":2}"; - void * func = metacall_function("hello_boy_await"); + void *func = metacall_function("hello_boy_await"); - ASSERT_NE((void *) NULL, (void *) func); + ASSERT_NE((void *)NULL, (void *)func); /* Call by map using arrays */ for (double iterator = 0.0; iterator <= 10.0; iterator += 1.0) { - double * context = new double(iterator); + double *context = new double(iterator); values[1] = metacall_value_from_double(values[1], iterator); ret = metacallfmv_await(func, keys, values, hello_boy_await_ok, hello_boy_await_fail, (void *)context); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(ret), (enum metacall_value_id) METACALL_FUTURE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); metacall_value_destroy(ret); } /* Call by map using serial */ - ret = metacallfms_await(func, args_map, sizeof(args_map), allocator, [](void * result, void *) -> void * { - EXPECT_NE((void *) NULL, (void *) result); + ret = metacallfms_await( + func, args_map, sizeof(args_map), allocator, [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)metacall_value_to_double(result), (double)12.0); + + printf("hello_boy_await future (from map serial) callback: %f\n", metacall_value_to_double(result)); + + fflush(stdout); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + ++success_callbacks; - EXPECT_EQ((double) metacall_value_to_double(result), (double) 12.0); + return NULL; + }, + NULL, NULL); - printf("hello_boy_await future (from map serial) callback: %f\n", metacall_value_to_double(result)); + EXPECT_NE((void *)NULL, (void *)ret); - fflush(stdout); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); - return NULL; + metacall_value_destroy(ret); + + /* Call by map using serial (segmentation fault on vaule destroy) */ + static const char args_random_map[] = "{\"token\":\"abc\",\"serial\":[\"XYZ\",\"ABC\"],\"range\":\"eee\"}"; + + func = metacall_function("get_random_data"); + + ASSERT_NE((void *)NULL, (void *)func); + + ret = metacallfms_await( + func, args_random_map, sizeof(args_random_map), allocator, [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)metacall_value_to_double(result), (double)12.0); - }, NULL, NULL); + printf("get_random_data future (from map serial) callback: %f\n", metacall_value_to_double(result)); - EXPECT_NE((void *) NULL, (void *) ret); + fflush(stdout); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(ret), (enum metacall_value_id) METACALL_FUTURE); + ++success_callbacks; + + return NULL; + }, + NULL, NULL); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); metacall_value_destroy(ret); /* Call by map using arrays (nested await) */ func = metacall_function("hello_boy_nested_await"); - ASSERT_NE((void *) NULL, (void *) func); + ASSERT_NE((void *)NULL, (void *)func); for (double iterator = 0.0; iterator <= 10.0; iterator += 1.0) { - double * context = new double(iterator); + double *context = new double(iterator); values[1] = metacall_value_from_double(values[1], iterator); ret = metacallfmv_await(func, keys, values, hello_boy_await_ok, hello_boy_await_fail, (void *)context); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(ret), (enum metacall_value_id) METACALL_FUTURE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); metacall_value_destroy(ret); } @@ -200,29 +239,37 @@ TEST_F(metacall_map_await_test, DefaultConstructor) metacall_value_destroy(keys[1]); metacall_value_destroy(values[0]); - metacall_value_destroy(values[1]); + metacall_value_destroy(values[1]); /* Await function that throws */ ret = metacall_await("throw_await", metacall_null_args, hello_world_await_fail, hello_world_await_ok, NULL); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(ret), (enum metacall_value_id) METACALL_FUTURE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); metacall_value_destroy(ret); /* Await function that returns */ ret = metacall_await("return_await", metacall_null_args, hello_world_await_ok, hello_world_await_fail, NULL); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(ret), (enum metacall_value_id) METACALL_FUTURE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ metacall_allocator_destroy(allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* Total amount of successful callbacks must be 26 */ + EXPECT_EQ((int)success_callbacks, (int)26); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ } diff --git a/source/tests/metacall_map_test/CMakeLists.txt b/source/tests/metacall_map_test/CMakeLists.txt index e84b3855c6..b9d40bf14a 100644 --- a/source/tests/metacall_map_test/CMakeLists.txt +++ b/source/tests/metacall_map_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,6 +132,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_map_test/source/main.cpp b/source/tests/metacall_map_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_map_test/source/main.cpp +++ b/source/tests/metacall_map_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_map_test/source/metacall_map_test.cpp b/source/tests/metacall_map_test/source/metacall_map_test.cpp index a3c3b4abef..84d641f4d0 100644 --- a/source/tests/metacall_map_test/source/metacall_map_test.cpp +++ b/source/tests/metacall_map_test/source/metacall_map_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_map_test : public testing::Test { @@ -33,17 +33,16 @@ TEST_F(metacall_map_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; @@ -51,21 +50,19 @@ TEST_F(metacall_map_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); static const char left[] = "left"; static const char right[] = "right"; - void * keys[] = - { + void *keys[] = { metacall_value_create_string(left, sizeof(left) - 1), metacall_value_create_string(right, sizeof(right) - 1) }; - void * values[] = - { + void *values[] = { metacall_value_create_long(7), metacall_value_create_long(0) }; @@ -73,9 +70,9 @@ TEST_F(metacall_map_test, DefaultConstructor) static const char args_map[] = "{\"left\":10,\"right\":2}"; static const char args_array[] = "[10, 2]"; - void * func = metacall_function("multiply"); + void *func = metacall_function("multiply"); - ASSERT_NE((void *) NULL, (void *) func); + ASSERT_NE((void *)NULL, (void *)func); /* Call by map using arrays */ for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) @@ -84,9 +81,9 @@ TEST_F(metacall_map_test, DefaultConstructor) ret = metacallfmv(func, keys, values); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } @@ -100,34 +97,31 @@ TEST_F(metacall_map_test, DefaultConstructor) /* Call by map using serial */ ret = metacallfms(func, args_map, sizeof(args_map), allocator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 20); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)20); metacall_value_destroy(ret); /* Call by array using serial */ ret = metacallfs(func, args_array, sizeof(args_array), allocator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 20); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)20); metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - const enum metacall_value_id double_ids[] = - { + const enum metacall_value_id double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; @@ -136,51 +130,85 @@ TEST_F(metacall_map_test, DefaultConstructor) static const char args_array[] = "[10, 2]"; static const char args_bad_array[] = "[10 2"; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); - void * func = metacall_function("call_test"); + void *func = metacall_function("call_test"); ASSERT_NE((void *)NULL, (void *)func); - void * ret = metacallt("call_test", double_ids, 10.0, 2.0); + void *ret = metacallt("call_test", double_ids, 10.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 20.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); metacall_value_destroy(ret); /* Call by map using serial */ ret = metacallfms(func, args_map, sizeof(args_map), allocator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 20.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); metacall_value_destroy(ret); /* Bad call by map using serial */ ret = metacallfms(func, args_bad_map, sizeof(args_bad_map), allocator); - EXPECT_EQ((void *) NULL, (void *) ret); + EXPECT_EQ((void *)NULL, (void *)ret); /* Call by array using serial */ ret = metacallfs(func, args_array, sizeof(args_array), allocator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 20.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); metacall_value_destroy(ret); /* Bad call by array using serial */ ret = metacallfs(func, args_bad_array, sizeof(args_bad_array), allocator); - EXPECT_EQ((void *) NULL, (void *) ret); + EXPECT_EQ((void *)NULL, (void *)ret); + + /* TODO: Implement spread operator */ + /* + static const char buffer[] = + "function spread_parameter(...args) {\n" + " console.log(args);\n" + " return 'ACK: OK!';\n" + "}\n" + "module.exports = {\n" + " spread_parameter,\n" + "};\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + static const char args_map_spread[] = + "{\n" + " \"id\": \"5555555-0000027\",\n" + " \"loanType\": \"Retail\",\n" + " \"lendingCategory\": \"Yeet\",\n" + " \"comments\": {\n" + " \"comment\": \"Rich-Test via Concert/Postman (Auto Approve - Attempt)\"\n" + " }\n" + "}\n"; + + func = metacall_function("spread_parameter"); + + ret = metacallfms(func, args_map_spread, sizeof(args_map_spread), allocator); + + ASSERT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "ACK: OK!"); + + metacall_value_destroy(ret); + */ } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ metacall_allocator_destroy(allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_async_multiple_test/CMakeLists.txt b/source/tests/metacall_node_async_multiple_test/CMakeLists.txt new file mode 100644 index 0000000000..77c1e4b31e --- /dev/null +++ b/source/tests/metacall_node_async_multiple_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-async-multiple-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_async_multiple_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_async_multiple_test/source/main.cpp b/source/tests/metacall_node_async_multiple_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_async_multiple_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_async_multiple_test/source/metacall_node_async_multiple_test.cpp b/source/tests/metacall_node_async_multiple_test/source/metacall_node_async_multiple_test.cpp new file mode 100644 index 0000000000..157a31a02c --- /dev/null +++ b/source/tests/metacall_node_async_multiple_test/source/metacall_node_async_multiple_test.cpp @@ -0,0 +1,210 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +std::atomic success_callbacks{}; + +class metacall_node_async_multiple_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_async_multiple_test, DefaultConstructor) +{ + const size_t iteration_size = 1000; + + struct async_context + { + int value; + } ctx = { + 234 + }; + + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "const util = require('util');\n" + "const delay = ms => new Promise(resolve => setTimeout(resolve, ms))\n" + "async function f(x) {\n" + "\tawait delay(2000);\n" + "\treturn new Promise(r => console.log(`Promise f executed: ${util.inspect(r)} -> ${x}`) || r(x));\n" + "}\n" + "async function g(x) {\n" + "\tawait delay(2000);\n" + "\treturn new Promise((_, r) => console.log(`Promise g executed: ${util.inspect(r)} -> ${x}`) || r(x));\n" + "}\n" + "async function h() {\n" + "\tawait delay(2000);\n" + "\treturn new Promise((resolve) => resolve(34));\n" + "}\n" + "module.exports = { f, g, h };\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + for (size_t i = 0; i < iteration_size; ++i) + { + void *args[] = { + metacall_value_create_double(10.0) + }; + + /* Test resolve */ + void *future = metacall_await( + "f", args, [](void *result, void *data) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 10.0, (double) metacall_value_to_double(result)); + + EXPECT_NE((void *) NULL, (void *) data); + + struct async_context * ctx = static_cast(data); + + EXPECT_EQ((int) 234, (int) ctx->value); + + printf("Resolve C Callback\n"); + + return metacall_value_create_double(15.0); }, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + printf("Reject C Callback\n"); + + return NULL; }, static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + + /* Test reject */ + future = metacall_await( + "g", args, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + printf("Resolve C Callback\n"); + + return NULL; }, [](void *result, void *data) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 10.0, (double) metacall_value_to_double(result)); + + EXPECT_NE((void *) NULL, (void *) data); + + struct async_context * ctx = static_cast(data); + + EXPECT_EQ((int) 234, (int) ctx->value); + + printf("Reject C Callback\n"); + + ++success_callbacks; + + return metacall_value_create_double(15.0); }, static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + + metacall_value_destroy(args[0]); + + /* Test future */ + future = metacall("h"); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + void *ret = metacall_await_future( + metacall_value_to_future(future), [](void *result, void *) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 34.0, (double) metacall_value_to_double(result)); + + ++success_callbacks; + + return metacall_value_create_double(155.0); }, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + return NULL; }, NULL); + + metacall_value_destroy(future); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + void *last = metacall_await_future( + metacall_value_to_future(ret), [](void *result, void *) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 155.0, (double) metacall_value_to_double(result)); + + ++success_callbacks; + + return NULL; }, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + return NULL; }, NULL); + + metacall_value_destroy(last); + + metacall_value_destroy(ret); + } + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* Total amount of successful callbacks must be 3 */ + EXPECT_EQ((int)success_callbacks, (int)(3 * iteration_size)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ +} diff --git a/source/tests/metacall_node_async_resources_test/CMakeLists.txt b/source/tests/metacall_node_async_resources_test/CMakeLists.txt new file mode 100644 index 0000000000..17e130e414 --- /dev/null +++ b/source/tests/metacall_node_async_resources_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-async-resources-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_async_resources_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_async_resources_test/source/main.cpp b/source/tests/metacall_node_async_resources_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_async_resources_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_async_resources_test/source/metacall_node_async_resources_test.cpp b/source/tests/metacall_node_async_resources_test/source/metacall_node_async_resources_test.cpp new file mode 100644 index 0000000000..82d2178970 --- /dev/null +++ b/source/tests/metacall_node_async_resources_test/source/metacall_node_async_resources_test.cpp @@ -0,0 +1,50 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_event_loop_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_event_loop_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "gram/index.js" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_async_test/CMakeLists.txt b/source/tests/metacall_node_async_test/CMakeLists.txt index 6184f16e11..d58db66234 100644 --- a/source/tests/metacall_node_async_test/CMakeLists.txt +++ b/source/tests/metacall_node_async_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -120,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_async_test/source/main.cpp b/source/tests/metacall_node_async_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_node_async_test/source/main.cpp +++ b/source/tests/metacall_node_async_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_async_test/source/metacall_node_async_test.cpp b/source/tests/metacall_node_async_test/source/metacall_node_async_test.cpp index f255a3aef4..e7a8395355 100644 --- a/source/tests/metacall_node_async_test/source/metacall_node_async_test.cpp +++ b/source/tests/metacall_node_async_test/source/metacall_node_async_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,15 @@ * */ -#include +#include #include -#include #include +#include + +#include + +std::atomic success_callbacks{}; class metacall_node_async_test : public testing::Test { @@ -33,10 +37,10 @@ TEST_F(metacall_node_async_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { const char buffer[] = "const util = require('util');\n" @@ -46,12 +50,17 @@ TEST_F(metacall_node_async_test, DefaultConstructor) "function g(x) {\n" "\treturn new Promise((_, r) => console.log(`Promise g executed: ${util.inspect(r)} -> ${x}`) || r(x));\n" "}\n" - "module.exports = { f, g };\n"; + "function h() {\n" + "\treturn new Promise((resolve) => resolve(34));\n" + "}\n" + "async function i() {\n" + "\tthrow Error('Hi there!');\n" + "}\n" + "module.exports = { f, g, h, i };\n"; - EXPECT_EQ((int) 0, (int) metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); - void * args[] = - { + void *args[] = { metacall_value_create_double(10.0) }; @@ -63,74 +72,193 @@ TEST_F(metacall_node_async_test, DefaultConstructor) }; /* Test resolve */ - void * future = metacall_await("f", args, [](void * result, void * data) -> void * { - EXPECT_NE((void *) NULL, (void *) result); + void *future = metacall_await( + "f", + args, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); - EXPECT_EQ((double) 10.0, (double) metacall_value_to_double(result)); + EXPECT_EQ((double)10.0, (double)metacall_value_to_double(result)); - EXPECT_NE((void *) NULL, (void *) data); + EXPECT_NE((void *)NULL, (void *)data); - struct async_context * ctx = static_cast(data); + struct async_context *ctx = static_cast(data); - EXPECT_EQ((int) 234, (int) ctx->value); + EXPECT_EQ((int)234, (int)ctx->value); - printf("Resolve C Callback\n"); + printf("Resolve C Callback\n"); - return metacall_value_create_double(15.0); - }, [](void *, void *) -> void * { - int this_should_never_be_executed = 0; + return metacall_value_create_double(15.0); + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; - EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + EXPECT_EQ((int)1, (int)this_should_never_be_executed); - printf("Reject C Callback\n"); + printf("Reject C Callback\n"); - return NULL; - }, static_cast(&ctx)); + return NULL; + }, + static_cast(&ctx)); - EXPECT_NE((void *) NULL, (void *) future); + EXPECT_NE((void *)NULL, (void *)future); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(future), (enum metacall_value_id) METACALL_FUTURE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); metacall_value_destroy(future); /* Test reject */ - future = metacall_await("g", args, [](void *, void *) -> void * { - int this_should_never_be_executed = 0; + future = metacall_await( + "g", + args, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; - EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + EXPECT_EQ((int)1, (int)this_should_never_be_executed); - printf("Resolve C Callback\n"); + printf("Resolve C Callback\n"); - return NULL; - }, [](void * result, void * data) -> void * { - EXPECT_NE((void *) NULL, (void *) result); + return NULL; + }, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); - EXPECT_EQ((double) 10.0, (double) metacall_value_to_double(result)); + EXPECT_EQ((double)10.0, (double)metacall_value_to_double(result)); - EXPECT_NE((void *) NULL, (void *) data); + EXPECT_NE((void *)NULL, (void *)data); - struct async_context * ctx = static_cast(data); + struct async_context *ctx = static_cast(data); - EXPECT_EQ((int) 234, (int) ctx->value); + EXPECT_EQ((int)234, (int)ctx->value); - printf("Reject C Callback\n"); + printf("Reject C Callback\n"); - return metacall_value_create_double(15.0); - }, static_cast(&ctx)); + ++success_callbacks; - EXPECT_NE((void *) NULL, (void *) future); + return metacall_value_create_double(15.0); + }, + static_cast(&ctx)); - EXPECT_EQ((enum metacall_value_id) metacall_value_id(future), (enum metacall_value_id) METACALL_FUTURE); + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); metacall_value_destroy(future); metacall_value_destroy(args[0]); + + /* Test future */ + future = metacall("h"); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + void *ret = metacall_await_future( + metacall_value_to_future(future), + [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)34.0, (double)metacall_value_to_double(result)); + + ++success_callbacks; + + return metacall_value_create_double(155.0); + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + return NULL; + }, + NULL); + + metacall_value_destroy(future); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + void *last = metacall_await_future( + metacall_value_to_future(ret), + [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)155.0, (double)metacall_value_to_double(result)); + + ++success_callbacks; + + return NULL; + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + return NULL; + }, + NULL); + + metacall_value_destroy(last); + + metacall_value_destroy(ret); + + /* Test throw exception inside async function */ + future = metacall_await( + "i", + metacall_null_args, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + printf("Resolve C Callback\n"); + + return NULL; + }, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_EXCEPTION); + + EXPECT_NE((void *)NULL, (void *)data); + + struct async_context *ctx = static_cast(data); + + EXPECT_EQ((int)234, (int)ctx->value); + + printf("Reject C Callback\n"); + + ++success_callbacks; + + return metacall_value_create_double(15.0); + }, + static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* Total amount of successful callbacks must be 4 */ + EXPECT_EQ((int)success_callbacks, (int)4); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ } diff --git a/source/tests/metacall_node_await_chain_test/CMakeLists.txt b/source/tests/metacall_node_await_chain_test/CMakeLists.txt new file mode 100644 index 0000000000..52e4ab39e3 --- /dev/null +++ b/source/tests/metacall_node_await_chain_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-await-chain-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_await_chain_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_await_chain_test/source/main.cpp b/source/tests/metacall_node_await_chain_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_await_chain_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_await_chain_test/source/metacall_node_await_chain_test.cpp b/source/tests/metacall_node_await_chain_test/source/metacall_node_await_chain_test.cpp new file mode 100644 index 0000000000..1efdd46827 --- /dev/null +++ b/source/tests/metacall_node_await_chain_test/source/metacall_node_await_chain_test.cpp @@ -0,0 +1,101 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +class metacall_node_await_chain_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_await_chain_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + std::atomic callbacks_executed(0); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = + "async function sleep(ms) { return new Promise(resolve => setTimeout(() => resolve(1), ms)) }\n" + "module.exports = { sleep };\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + /* We will try to emulate this: */ + /* sleep(100).then(v => v * 2).then(v => v + 3); */ + + void *args[] = { + metacall_value_create_double(100) + }; + + auto resolve1 = [](void *result, void *data) -> void * { + std::atomic *callbacks_executed = static_cast *>(data); + double v = metacall_value_to_double(result); + EXPECT_EQ((double)1.0, (double)v); + EXPECT_EQ((unsigned int)0, (unsigned int)*callbacks_executed); + ++(*callbacks_executed); + return metacall_value_create_double(v * 2.0); + }; + + auto resolve2 = [](void *result, void *data) -> void * { + std::atomic *callbacks_executed = static_cast *>(data); + double v = metacall_value_to_double(result); + EXPECT_EQ((double)2.0, (double)v); + EXPECT_EQ((unsigned int)1, (unsigned int)*callbacks_executed); + ++(*callbacks_executed); + return metacall_value_create_double(v + 3.0); + }; + + auto resolve3 = [](void *result, void *data) -> void * { + std::atomic *callbacks_executed = static_cast *>(data); + EXPECT_EQ((double)5.0, (double)metacall_value_to_double(result)); + EXPECT_EQ((unsigned int)2, (unsigned int)*callbacks_executed); + ++(*callbacks_executed); + return NULL; + }; + + void *f1 = metacall_await("sleep", args, resolve1, NULL, static_cast(&callbacks_executed)); + + metacall_value_destroy(args[0]); + + void *f2 = metacall_await_future(metacall_value_to_future(f1), resolve2, NULL, static_cast(&callbacks_executed)); + + void *f3 = metacall_await_future(metacall_value_to_future(f2), resolve3, NULL, static_cast(&callbacks_executed)); + + metacall_value_destroy(f1); + metacall_value_destroy(f2); + metacall_value_destroy(f3); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + + EXPECT_EQ((unsigned int)3, (unsigned int)callbacks_executed); +} diff --git a/source/tests/metacall_node_call_test/CMakeLists.txt b/source/tests/metacall_node_call_test/CMakeLists.txt index 541fed96cf..c0a877d4df 100644 --- a/source/tests/metacall_node_call_test/CMakeLists.txt +++ b/source/tests/metacall_node_call_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -120,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_call_test/source/main.cpp b/source/tests/metacall_node_call_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_node_call_test/source/main.cpp +++ b/source/tests/metacall_node_call_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp b/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp index 426f32e361..7117e09419 100644 --- a/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp +++ b/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include #define METACALL_CALL_TEST_SIZE 10000 @@ -37,37 +37,35 @@ TEST_F(metacall_node_call_test, DefaultConstructor) metacall_log_null(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; const size_t call_size = METACALL_CALL_TEST_SIZE; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); for (size_t iterator = 0; iterator < call_size; ++iterator) { - void * ret = metacallt("call_test", hello_boy_double_ids, 3.0, 4.0); + void *ret = metacallt("call_test", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 12.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)12.0); metacall_value_destroy(ret); } } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/py_loader_test/CMakeLists.txt b/source/tests/metacall_node_callback_test/CMakeLists.txt similarity index 74% rename from source/tests/py_loader_test/CMakeLists.txt rename to source/tests/metacall_node_callback_test/CMakeLists.txt index f0f8a0bf71..1ba50b6375 100644 --- a/source/tests/py_loader_test/CMakeLists.txt +++ b/source/tests/metacall_node_callback_test/CMakeLists.txt @@ -1,14 +1,16 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) - return() -endif() - # # Executable name and options # +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE + OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + # Target name -set(target py-loader-test) +set(target metacall-node-callback-test) message(STATUS "Test ${target}") # @@ -32,7 +34,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/py_loader_test.cpp + ${source_path}/metacall_node_callback_test.cpp ) # Group source files @@ -85,20 +87,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader + ${META_PROJECT_NAME}::metacall ) # @@ -119,11 +108,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,6 +134,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + # # Define test properties # @@ -149,4 +156,5 @@ include(TestEnvironmentVariables) test_environment_variables(${target} "" ${TESTS_ENVIRONMENT_VARIABLES} + PY_PORT_LIBRARY_PATH=${CMAKE_SOURCE_DIR}/source/ports/py_port ) diff --git a/source/tests/metacall_node_callback_test/source/main.cpp b/source/tests/metacall_node_callback_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_callback_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_callback_test/source/metacall_node_callback_test.cpp b/source/tests/metacall_node_callback_test/source/metacall_node_callback_test.cpp new file mode 100644 index 0000000000..a49f61313b --- /dev/null +++ b/source/tests/metacall_node_callback_test/source/metacall_node_callback_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_callback_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_callback_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "host.js" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + ret = metacall("a"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + void *handle = metacall_handle("node", "host.js"); + + EXPECT_NE((void *)NULL, (void *)handle); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_clear_mem_test/CMakeLists.txt b/source/tests/metacall_node_clear_mem_test/CMakeLists.txt new file mode 100644 index 0000000000..d7f9e9124c --- /dev/null +++ b/source/tests/metacall_node_clear_mem_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-clear-mem-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_clear_mem_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_clear_mem_test/source/main.cpp b/source/tests/metacall_node_clear_mem_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_clear_mem_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_clear_mem_test/source/metacall_node_clear_mem_test.cpp b/source/tests/metacall_node_clear_mem_test/source/metacall_node_clear_mem_test.cpp new file mode 100644 index 0000000000..ae63885773 --- /dev/null +++ b/source/tests/metacall_node_clear_mem_test/source/metacall_node_clear_mem_test.cpp @@ -0,0 +1,68 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_clear_mem_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_clear_mem_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_null(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char bufferA[] = + "console.log('A');\n"; + + void *handleA = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", bufferA, sizeof(bufferA), &handleA)); + + EXPECT_NE((void *)NULL, (void *)handleA); + + EXPECT_EQ((int)0, (int)metacall_clear(handleA)); + + const char bufferB[] = + "console.log('B');\n"; + + void *handleB = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", bufferB, sizeof(bufferB), &handleB)); + + EXPECT_NE((void *)NULL, (void *)handleB); + + EXPECT_EQ((int)0, (int)metacall_clear(handleB)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_default_export_test/CMakeLists.txt b/source/tests/metacall_node_default_export_test/CMakeLists.txt index 5d2d848e52..c009589339 100644 --- a/source/tests/metacall_node_default_export_test/CMakeLists.txt +++ b/source/tests/metacall_node_default_export_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -120,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_default_export_test/source/main.cpp b/source/tests/metacall_node_default_export_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_node_default_export_test/source/main.cpp +++ b/source/tests/metacall_node_default_export_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_default_export_test/source/metacall_node_default_export_test.cpp b/source/tests/metacall_node_default_export_test/source/metacall_node_default_export_test.cpp index c8adb111b2..890c8b8726 100644 --- a/source/tests/metacall_node_default_export_test/source/metacall_node_default_export_test.cpp +++ b/source/tests/metacall_node_default_export_test/source/metacall_node_default_export_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_node_default_export_test : public testing::Test { @@ -33,38 +33,36 @@ TEST_F(metacall_node_default_export_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js", "export.js" }; - const enum metacall_value_id double_id[] = - { + const enum metacall_value_id double_id[] = { METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); /* When using module exports functions will be exported explicitly */ - EXPECT_EQ((void *) NULL, (void *) metacall_function("this_function_should_not_be_exported")); + EXPECT_EQ((void *)NULL, (void *)metacall_function("this_function_should_not_be_exported")); /* Test default export */ ret = metacallt("export_this_function_even_without_module_exports", double_id, 3.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 3.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ /* Print inspect information */ { @@ -72,18 +70,18 @@ TEST_F(metacall_node_default_export_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); metacall_allocator_free(allocator, inspect_str); metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_event_loop_signal_test/CMakeLists.txt b/source/tests/metacall_node_event_loop_signal_test/CMakeLists.txt new file mode 100644 index 0000000000..cb3e4701ce --- /dev/null +++ b/source/tests/metacall_node_event_loop_signal_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-event-loop-signal-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_event_loop_signal_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_event_loop_signal_test/source/main.cpp b/source/tests/metacall_node_event_loop_signal_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_event_loop_signal_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_event_loop_signal_test/source/metacall_node_event_loop_signal_test.cpp b/source/tests/metacall_node_event_loop_signal_test/source/metacall_node_event_loop_signal_test.cpp new file mode 100644 index 0000000000..54d7cb8fa2 --- /dev/null +++ b/source/tests/metacall_node_event_loop_signal_test/source/metacall_node_event_loop_signal_test.cpp @@ -0,0 +1,58 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_event_loop_signal_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_event_loop_signal_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_null(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + // This test verifies that signals do not prevent event loop from exiting + const char buffer[] = + "process.on('SIGINT', () => { console.log('SIGINT') });\n"; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), &handle)); + + EXPECT_NE((void *)NULL, (void *)handle); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_event_loop_test/CMakeLists.txt b/source/tests/metacall_node_event_loop_test/CMakeLists.txt index 9e4295820b..d1d83d4416 100644 --- a/source/tests/metacall_node_event_loop_test/CMakeLists.txt +++ b/source/tests/metacall_node_event_loop_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -120,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_event_loop_test/source/main.cpp b/source/tests/metacall_node_event_loop_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_node_event_loop_test/source/main.cpp +++ b/source/tests/metacall_node_event_loop_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_event_loop_test/source/metacall_node_event_loop_test.cpp b/source/tests/metacall_node_event_loop_test/source/metacall_node_event_loop_test.cpp index af7b33abdd..306cda37b0 100644 --- a/source/tests/metacall_node_event_loop_test/source/metacall_node_event_loop_test.cpp +++ b/source/tests/metacall_node_event_loop_test/source/metacall_node_event_loop_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_node_event_loop_test : public testing::Test { @@ -33,33 +33,18 @@ TEST_F(metacall_node_event_loop_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* TODO: This works, the only problem is that it seems stdout gets bugged and it is not printing the messages: */ - /* - ################################### - Server up - ################################### - ################################### - Closing server... - ################################### - ################################### - Server closed - ################################### - */ - /* This must be reviewed */ + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "server.js" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_exception_test/CMakeLists.txt b/source/tests/metacall_node_exception_test/CMakeLists.txt new file mode 100644 index 0000000000..1129b31459 --- /dev/null +++ b/source/tests/metacall_node_exception_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-exception-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_exception_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_exception_test/source/main.cpp b/source/tests/metacall_node_exception_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_exception_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_exception_test/source/metacall_node_exception_test.cpp b/source/tests/metacall_node_exception_test/source/metacall_node_exception_test.cpp new file mode 100644 index 0000000000..fd1c3b2e26 --- /dev/null +++ b/source/tests/metacall_node_exception_test/source/metacall_node_exception_test.cpp @@ -0,0 +1,79 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_exception_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_exception_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = + "module.exports = {\n" + " js_return_error: () => new Error('Yeet'),\n" + " js_throw_error: () => { throw new Error('YeetThrown') },\n" + " js_throw_value: () => { throw 56 },\n" + "};\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("js_return_error"); + + struct metacall_exception_type ex; + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("Yeet", ex.message); + + metacall_value_destroy(ret); + + ret = metacall("js_throw_error"); + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("YeetThrown", ex.message); + + metacall_value_destroy(ret); + + ret = metacall("js_throw_value"); + + void *number = metacall_throwable_value(metacall_value_to_throwable(ret)); + + EXPECT_EQ((double)56.0, (double)metacall_value_to_double(number)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_extension_test/CMakeLists.txt b/source/tests/metacall_node_extension_test/CMakeLists.txt new file mode 100644 index 0000000000..f56da937ab --- /dev/null +++ b/source/tests/metacall_node_extension_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-extension-test) +message(STATUS "Test ${target}") + +# Add node extension test +add_subdirectory(node_extension_test) + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_extension_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + METACALL_NODE_EXTENSION_PATH="${PROJECT_OUTPUT_DIR}/node_extension_test.node" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + node_extension_test +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_extension_test/node_extension_test/CMakeLists.txt b/source/tests/metacall_node_extension_test/node_extension_test/CMakeLists.txt new file mode 100644 index 0000000000..1efbefcc03 --- /dev/null +++ b/source/tests/metacall_node_extension_test/node_extension_test/CMakeLists.txt @@ -0,0 +1,186 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# External dependencies +# + +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(SEND_ERROR "NodeJS libraries not found") + return() +endif() + +# +# Plugin name and options +# + +# Target name +set(target node_extension_test) + +# Exit here if required dependencies are not met +message(STATUS "Script ${target}") + +# Set API export file and macro +string(TOUPPER ${target} target_upper) +set(export_file "include/${target}/${target}_api.h") +set(export_macro "${target_upper}_API") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/node_extension_test.c +) + +if(WIN32 AND MSVC_VERSION GREATER_EQUAL 1200) + set(sources + ${sources} + ${source_path}/node_extension_test_win32_delay_load.cpp + ) +endif() + +# Group source files +set(source_group "Source Files") +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create library +# + +# Build library +add_library(${target} MODULE + ${sources} + ${headers} +) + +# Create namespaced alias +add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# Export library for downstream projects +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) + +# Create API export header +generate_export_header(${target} + EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${export_macro} +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" + BUNDLE $<$:$<$>> + + # Set NodeJS extension properies + SUFFIX ".node" + PREFIX "" + DEBUG_POSTFIX "" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + + ${NodeJS_INCLUDE_DIRS} # NodeJS includes + + PUBLIC + ${DEFAULT_INCLUDE_DIRECTORIES} + + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${NodeJS_LIBRARY} # NodeJS library + $<$:delayimp.lib> # Delayed library + + PUBLIC + ${DEFAULT_LIBRARIES} + + INTERFACE +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + $<$:NODEJS_LIBRARY_NAME="${NodeJS_LIBRARY_NAME}"> + $<$>:_LARGEFILE_SOURCE> + $<$>:_FILE_OFFSET_BITS=64> + $<$,$>:_DARWIN_USE_64_BIT_INODE=1> + + PUBLIC + $<$>:${target_upper}_STATIC_DEFINE> + ${DEFAULT_COMPILE_DEFINITIONS} + + INTERFACE +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_COMPILE_OPTIONS} + + INTERFACE +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + $<$:/IGNORE:4199> + $<$:/DELAYLOAD:${NodeJS_LIBRARY_NAME}> + $<$,$>:-undefined dynamic_lookup> + + PUBLIC + ${DEFAULT_LINKER_OPTIONS} + + INTERFACE +) diff --git a/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test.c b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test.c new file mode 100644 index 0000000000..a0df94beac --- /dev/null +++ b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test.c @@ -0,0 +1,53 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +static napi_value Method(napi_env env, napi_callback_info info) +{ + napi_status status; + napi_value world; + (void)info; + status = napi_create_string_utf8(env, "world", 5, &world); + if (status != napi_ok) + { + return NULL; + } + return world; +} + +#define DECLARE_NAPI_METHOD(name, func) \ + { \ + name, 0, func, 0, 0, 0, napi_default, 0 \ + } + +static napi_value Init(napi_env env, napi_value exports) +{ + napi_status status; + napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method); + status = napi_define_properties(env, exports, 1, &desc); + if (status != napi_ok) + { + return NULL; + } + return exports; +} + +NAPI_MODULE(node_extension_test, Init) diff --git a/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test_win32_delay_load.cpp b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test_win32_delay_load.cpp new file mode 100644 index 0000000000..fc968f846a --- /dev/null +++ b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test_win32_delay_load.cpp @@ -0,0 +1,54 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma managed(push, off) + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#include + +#include +#include + +static const char node_library_name[] = NODEJS_LIBRARY_NAME; + +static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo *info) +{ + HMODULE m; + + if (event != dliNotePreLoadLibrary) + { + return NULL; + } + + if (_stricmp(info->szDll, node_library_name) != 0) + { + return NULL; + } + + m = GetModuleHandle(NULL); + return (FARPROC)m; +} + +decltype(__pfnDliNotifyHook2) __pfnDliNotifyHook2 = load_exe_hook; + +#pragma managed(pop) diff --git a/source/tests/metacall_node_extension_test/source/main.cpp b/source/tests/metacall_node_extension_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_extension_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_extension_test/source/metacall_node_extension_test.cpp b/source/tests/metacall_node_extension_test/source/metacall_node_extension_test.cpp new file mode 100644 index 0000000000..ef5fd6caf7 --- /dev/null +++ b/source/tests/metacall_node_extension_test/source/metacall_node_extension_test.cpp @@ -0,0 +1,83 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +class metacall_node_extension_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_extension_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + METACALL_NODE_EXTENSION_PATH + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + ret = metacallv("hello", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ("world", metacall_value_to_string(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_fail_env_var_test/CMakeLists.txt b/source/tests/metacall_node_fail_env_var_test/CMakeLists.txt new file mode 100644 index 0000000000..ecd42051ef --- /dev/null +++ b/source/tests/metacall_node_fail_env_var_test/CMakeLists.txt @@ -0,0 +1,162 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-fail-env-var-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_fail_env_var_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +# Do not define LOADER_LIBRARY_PATH, which is needed by node loader +# in order to test if the node loader fails gracefully +set(NODE_FAIL_ENVIRONMENT_VARIABLES ${TESTS_ENVIRONMENT_VARIABLES}) + +list(REMOVE_ITEM NODE_FAIL_ENVIRONMENT_VARIABLES "LOADER_LIBRARY_PATH=${LOADER_LIBRARY_PATH}") + +test_environment_variables(${target} + "" + ${NODE_FAIL_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_fail_env_var_test/source/main.cpp b/source/tests/metacall_node_fail_env_var_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_fail_env_var_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_fail_env_var_test/source/metacall_node_fail_env_var_test.cpp b/source/tests/metacall_node_fail_env_var_test/source/metacall_node_fail_env_var_test.cpp new file mode 100644 index 0000000000..b6ef3dc8a2 --- /dev/null +++ b/source/tests/metacall_node_fail_env_var_test/source/metacall_node_fail_env_var_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_fail_env_var_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_fail_env_var_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "this_does_not_exists_yeet.js" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_fail_load_leak_test/CMakeLists.txt b/source/tests/metacall_node_fail_load_leak_test/CMakeLists.txt new file mode 100644 index 0000000000..3b1cc5bc5e --- /dev/null +++ b/source/tests/metacall_node_fail_load_leak_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-fail-load-leak-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_fail_load_leak_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_fail_load_leak_test/source/main.cpp b/source/tests/metacall_node_fail_load_leak_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_fail_load_leak_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_fail_load_leak_test/source/metacall_node_fail_load_leak_test.cpp b/source/tests/metacall_node_fail_load_leak_test/source/metacall_node_fail_load_leak_test.cpp new file mode 100644 index 0000000000..a47ec3c226 --- /dev/null +++ b/source/tests/metacall_node_fail_load_leak_test/source/metacall_node_fail_load_leak_test.cpp @@ -0,0 +1,74 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "const { metacall_load_from_memory } = require('metacall');\n" + "metacall_load_from_memory('node', 'throw new Error(\"fail\")');\n"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("hey"); + + EXPECT_EQ((void *)NULL, (void *)ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_fail_test/CMakeLists.txt b/source/tests/metacall_node_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..2f46c9261f --- /dev/null +++ b/source/tests/metacall_node_fail_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_fail_test/source/main.cpp b/source/tests/metacall_node_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_fail_test/source/metacall_node_fail_test.cpp b/source/tests/metacall_node_fail_test/source/metacall_node_fail_test.cpp new file mode 100644 index 0000000000..e052ed25b2 --- /dev/null +++ b/source/tests/metacall_node_fail_test/source/metacall_node_fail_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "this_does_not_exists_yeet.js" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + const char buffer[] = + "function pure ()random 453 code lol()\n"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_inline_test/CMakeLists.txt b/source/tests/metacall_node_inline_test/CMakeLists.txt index 74d5470c1e..8f7c771f26 100644 --- a/source/tests/metacall_node_inline_test/CMakeLists.txt +++ b/source/tests/metacall_node_inline_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -120,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_inline_test/source/main.cpp b/source/tests/metacall_node_inline_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_node_inline_test/source/main.cpp +++ b/source/tests/metacall_node_inline_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp b/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp index 3b44cb9a48..1c578f6eab 100644 --- a/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp +++ b/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_node_inline_test : public testing::Test { @@ -33,34 +33,32 @@ TEST_F(metacall_node_inline_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "inline.js" }; - const enum metacall_value_id inline_double_id[] = - { + const enum metacall_value_id inline_double_id[] = { METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); ret = metacallt("inline", inline_double_id, 3.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 6.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ /* Print inspect information */ { @@ -68,13 +66,13 @@ TEST_F(metacall_node_inline_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -83,5 +81,5 @@ TEST_F(metacall_node_inline_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_multithread_deadlock_test/CMakeLists.txt b/source/tests/metacall_node_multithread_deadlock_test/CMakeLists.txt new file mode 100644 index 0000000000..e09b978263 --- /dev/null +++ b/source/tests/metacall_node_multithread_deadlock_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-multithread-deadlock-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_multithread_deadlock_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_multithread_deadlock_test/source/main.cpp b/source/tests/metacall_node_multithread_deadlock_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_multithread_deadlock_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_multithread_deadlock_test/source/metacall_node_multithread_deadlock_test.cpp b/source/tests/metacall_node_multithread_deadlock_test/source/metacall_node_multithread_deadlock_test.cpp new file mode 100644 index 0000000000..39ff07dabe --- /dev/null +++ b/source/tests/metacall_node_multithread_deadlock_test/source/metacall_node_multithread_deadlock_test.cpp @@ -0,0 +1,136 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include + +std::atomic success_callbacks{}; +static const int call_size = 200000; +static const int thread_size = 8; + +class metacall_node_multithread_deadlock_test : public testing::Test +{ +public: +}; + +void test_await(void) +{ + for (int i = 0; i < call_size; ++i) + { + void *future = metacall_await( + "f", + metacall_null_args, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)34.0, (double)metacall_value_to_double(result)); + + EXPECT_EQ((void *)NULL, (void *)data); + + ++success_callbacks; + + return metacall_value_create_double(15.0); + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + return NULL; + }, + NULL); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + } +} + +void test_call(void) +{ + for (int i = 0; i < call_size; ++i) + { + void *result = metacallv_s("g", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)34.0, (double)metacall_value_to_double(result)); + + metacall_value_destroy(result); + + ++success_callbacks; + } +} + +TEST_F(metacall_node_multithread_deadlock_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "async function f() {\n" + "\treturn 34;\n" + "}\n" + "function g() {\n" + "\treturn 34;\n" + "}\n" + "module.exports = { f, g };\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + std::thread threads[thread_size]; + + for (int i = 0; i < thread_size; ++i) + { + threads[i] = std::thread(test_call); + } + + for (int i = 0; i < thread_size; ++i) + { + threads[i].join(); + } + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + EXPECT_EQ((int)success_callbacks, (int)(call_size * thread_size)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ +} diff --git a/source/tests/metacall_node_native_code_test/CMakeLists.txt b/source/tests/metacall_node_native_code_test/CMakeLists.txt new file mode 100644 index 0000000000..2f485938eb --- /dev/null +++ b/source/tests/metacall_node_native_code_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-native-code-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_native_code_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + node_port +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_native_code_test/source/main.cpp b/source/tests/metacall_node_native_code_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_native_code_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_native_code_test/source/metacall_node_native_code_test.cpp b/source/tests/metacall_node_native_code_test/source/metacall_node_native_code_test.cpp new file mode 100644 index 0000000000..4366808cc2 --- /dev/null +++ b/source/tests/metacall_node_native_code_test/source/metacall_node_native_code_test.cpp @@ -0,0 +1,72 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_unsupported_features_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_unsupported_features_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "const { metacall_inspect } = process._linkedBinding('node_loader_port_module');\n" + "module.exports = { metacall_inspect }\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("metacall_inspect")); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_await_test/CMakeLists.txt b/source/tests/metacall_node_port_await_test/CMakeLists.txt new file mode 100644 index 0000000000..819a878a01 --- /dev/null +++ b/source/tests/metacall_node_port_await_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + node_port +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_port_await_test/source/main.cpp b/source/tests/metacall_node_port_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_await_test/source/metacall_node_port_await_test.cpp b/source/tests/metacall_node_port_await_test/source/metacall_node_port_await_test.cpp new file mode 100644 index 0000000000..43cb8e8db0 --- /dev/null +++ b/source/tests/metacall_node_port_await_test/source/metacall_node_port_await_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_port_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall_await, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('node', `" + /* NodeJS */ + "function sleep(n) { return new Promise(resolve => { setTimeout(() => resolve(n), 200); }); }\n" + "module.exports = { sleep };\n" + "`);\n" + /* NodeJS Check */ + "let buffer = new SharedArrayBuffer(4);\n" + "let int32 = new Int32Array(buffer);\n" + "Atomics.store(int32, 0, 0);\n" + "process.on('exit', () => {\n" + " if (Atomics.load(int32, 0) != 1) {\n" + " process.exit(3);\n" + " }\n" + "});\n" + /* NodeJS Promise */ + "metacall_await('sleep', 32).then(v => {\n" + " console.log('RESULT:', v);\n" + " if (v !== 32) {\n" + " process.exit(1);\n" + " }\n" + " Atomics.add(int32, 0, 1);\n" + "}).catch(v => {\n" + " console.log('ERROR:', v);\n" + " process.exit(2);\n" + "});\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt b/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt new file mode 100644 index 0000000000..ab55d51e7d --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt @@ -0,0 +1,176 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# External dependencies +# + +find_package(LibGit2) + +if(NOT LibGit2_FOUND) + message(WARNING "LibGit2 libraries not found, skipping test metacall-node-port-c-lib-test") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-c-lib-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_c_lib_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # LibGit2 paths + LIBGIT2_LIBRARY_DIR="${LibGit2_LIBRARY_DIR}" + LIBGIT2_INCLUDE_DIR="${LibGit2_INCLUDE_DIR}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_port + node_loader + c_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_port_c_lib_test/source/main.cpp b/source/tests/metacall_node_port_c_lib_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp b/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp new file mode 100644 index 0000000000..7070bbf985 --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp @@ -0,0 +1,57 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_port_c_lib_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_c_lib_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char buffer[] = + /* NodeJS */ + "const assert = require('assert');\n" + "const { metacall_execution_path, metacall_load_from_package_export } = require('" METACALL_NODE_PORT_PATH "');\n" + /* C Lib Paths */ + "metacall_execution_path('c', '" LIBGIT2_INCLUDE_DIR "');\n" + "metacall_execution_path('c', '" LIBGIT2_LIBRARY_DIR "');\n" + /* C Lib Require */ + "const git2 = metacall_load_from_package_export('c', 'git2');\n" + "const { git_libgit2_init, git_libgit2_shutdown } = git2;\n" + "console.log(git2);\n" + /* C Lib Assert */ + "assert(git_libgit2_init() >= 0, 'libgit2 initialization failed');\n" + "git_libgit2_shutdown();\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_rs_test/CMakeLists.txt b/source/tests/metacall_node_port_rs_test/CMakeLists.txt new file mode 100644 index 0000000000..57422785bd --- /dev/null +++ b/source/tests/metacall_node_port_rs_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-rs-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_rs_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_port + node_loader + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_port_rs_test/source/main.cpp b/source/tests/metacall_node_port_rs_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_rs_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_rs_test/source/metacall_node_port_rs_test.cpp b/source/tests/metacall_node_port_rs_test/source/metacall_node_port_rs_test.cpp new file mode 100644 index 0000000000..dc54f4d0c9 --- /dev/null +++ b/source/tests/metacall_node_port_rs_test/source/metacall_node_port_rs_test.cpp @@ -0,0 +1,59 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_port_rs_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_rs_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Rust */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_RS) + { + static const char buffer[] = + /* NodeJS */ + "const assert = require('assert');\n" + "require('" METACALL_NODE_PORT_PATH "');\n" + /* Rust Require */ + "const { new_string, add_vec2, add_float, return_vec } = require('./basic.rs');\n" + /* Rust Assert */ + "assert.strictEqual(new_string(123), 'get number 123');\n" + "assert.strictEqual(add_vec2([1, 2, 3, 4]), 10);\n" + "assert.strictEqual(add_float(12, 23), 35);\n" + "assert.strictEqual(return_vec().reduce((partialSum, a) => partialSum + a, 0), 15);\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_RS */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_test/CMakeLists.txt b/source/tests/metacall_node_port_test/CMakeLists.txt new file mode 100644 index 0000000000..5568059519 --- /dev/null +++ b/source/tests/metacall_node_port_test/CMakeLists.txt @@ -0,0 +1,244 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_MOCK OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_TS OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port Test path + METACALL_NODE_PORT_TEST_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/test.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13603) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #9 node_loader_port_load_from_file_export(napi_env__*, napi_callback_info__*) /usr/local/metacall/source/loaders/node_loader/source/node_loader_port.cpp:395 (libnode_loaderd.so+0x113c5) + # #10 (libnode.so.72+0x7b6344) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +# Enable cobol test if it is built +if(OPTION_BUILD_LOADERS_COB) + set(COBOL_DEPENDENCY cob_loader) + set(TESTS_ENVIRONMENT_VARIABLES_COB "OPTION_BUILD_LOADERS_COB=1") +endif() + +# Enable c test if it is built +if(OPTION_BUILD_LOADERS_C) + set(C_DEPENDENCY c_loader) + set(TESTS_ENVIRONMENT_VARIABLES_C "OPTION_BUILD_LOADERS_C=1") +endif() + +# Enable rust test if it is built +if(OPTION_BUILD_LOADERS_RS) + set(RS_DEPENDENCY rs_loader) + set(TESTS_ENVIRONMENT_VARIABLES_RS "OPTION_BUILD_LOADERS_RS=1") +endif() + +# Add dependencies and optional dependencies +add_dependencies(${target} + node_port + node_loader + mock_loader + py_loader + rb_loader + ts_loader + ${COBOL_DEPENDENCY} + ${C_DEPENDENCY} + ${RS_DEPENDENCY} +) + +# Disable OpenSSL related tests if versions are incompatible +set(NodeJS_EXECUTABLE_ONLY ON) + +find_package(NodeJS) +find_package(Python3 COMPONENTS Interpreter) + +if(NodeJS_FOUND AND Python3_Interpreter_FOUND) + execute_process( + COMMAND ${NodeJS_EXECUTABLE} -e "console.log(process.versions.openssl)" + OUTPUT_VARIABLE NODEJS_OPENSSL_VERSION + ) + execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import ssl; print(ssl.OPENSSL_VERSION.split()[1])" + OUTPUT_VARIABLE PYTHON_OPENSSL_VERSION + ) + + if(NOT "${NODEJS_OPENSSL_VERSION}" STREQUAL "" AND NOT "${PYTHON_OPENSSL_VERSION}" STREQUAL "") + string(REGEX MATCHALL "-.*$|[0-9]+" NODEJS_OPENSSL_PARTIAL_VERSION_LIST "${NODEJS_OPENSSL_VERSION}") + list(GET NODEJS_OPENSSL_PARTIAL_VERSION_LIST 0 NODEJS_OPENSSL_VERSION_MAJOR) + list(GET NODEJS_OPENSSL_PARTIAL_VERSION_LIST 1 NODEJS_OPENSSL_VERSION_MINOR) + list(GET NODEJS_OPENSSL_PARTIAL_VERSION_LIST 2 NODEJS_OPENSSL_VERSION_PATCH) + + string(REGEX MATCHALL "-.*$|[0-9]+" PYTHON_OPENSSL_PARTIAL_VERSION_LIST "${PYTHON_OPENSSL_VERSION}") + list(GET PYTHON_OPENSSL_PARTIAL_VERSION_LIST 0 PYTHON_OPENSSL_VERSION_MAJOR) + list(GET PYTHON_OPENSSL_PARTIAL_VERSION_LIST 1 PYTHON_OPENSSL_VERSION_MINOR) + list(GET PYTHON_OPENSSL_PARTIAL_VERSION_LIST 2 PYTHON_OPENSSL_VERSION_PATCH) + + # If major and minor version match, then enable the OpenSSL related tests (https://github.com/metacall/core/issues/31#issuecomment-1736039845) + if(NODEJS_OPENSSL_VERSION_MAJOR VERSION_EQUAL PYTHON_OPENSSL_VERSION_MAJOR + AND NODEJS_OPENSSL_VERSION_MINOR VERSION_EQUAL PYTHON_OPENSSL_VERSION_MINOR) + set(TESTS_ENVIRONMENT_VARIABLES_OPENSSL "OPTION_NODEJS_PYTHON_OPENSSL_MATCH=1") + endif() + endif() +endif() + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${TESTS_ENVIRONMENT_VARIABLES_COB} + ${TESTS_ENVIRONMENT_VARIABLES_C} + ${TESTS_ENVIRONMENT_VARIABLES_RS} + ${TESTS_ENVIRONMENT_VARIABLES_OPENSSL} + "NODE_PORT_TEST_EXPORTS=1" +) diff --git a/source/tests/metacall_node_port_test/source/main.cpp b/source/tests/metacall_node_port_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_test/source/metacall_node_port_test.cpp b/source/tests/metacall_node_port_test/source/metacall_node_port_test.cpp new file mode 100644 index 0000000000..ab1d6df112 --- /dev/null +++ b/source/tests/metacall_node_port_test/source/metacall_node_port_test.cpp @@ -0,0 +1,89 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include + +class metacall_node_port_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + METACALL_NODE_PORT_TEST_PATH + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + struct await_data_type + { + std::mutex m; + std::condition_variable c; + } await_data; + + std::unique_lock lock(await_data.m); + + auto accept = [](void *v, void *data) -> void * { + struct await_data_type *await_data = static_cast(data); + std::unique_lock lock(await_data->m); + const char *str = metacall_value_to_string(v); + EXPECT_STREQ(str, "Tests passed without errors"); + await_data->c.notify_one(); + return NULL; + }; + + auto reject = [](void *, void *data) -> void * { + static const int promise_rejected = 0; + struct await_data_type *await_data = static_cast(data); + std::unique_lock lock(await_data->m); + EXPECT_EQ((int)1, (int)promise_rejected); // This should never happen + await_data->c.notify_one(); + return NULL; + }; + + void *future = metacall_await("main", metacall_null_args, accept, reject, static_cast(&await_data)); + + ASSERT_NE((void *)NULL, (void *)future); + + ASSERT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + await_data.c.wait(lock); + + metacall_value_destroy(future); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_async_after_destroy_test/CMakeLists.txt b/source/tests/metacall_node_python_async_after_destroy_test/CMakeLists.txt new file mode 100644 index 0000000000..6c88f28d6b --- /dev/null +++ b/source/tests/metacall_node_python_async_after_destroy_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-async-after-destroy-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_async_after_destroy_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_async_after_destroy_test/source/main.cpp b/source/tests/metacall_node_python_async_after_destroy_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_async_after_destroy_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_async_after_destroy_test/source/metacall_node_python_async_after_destroy_test.cpp b/source/tests/metacall_node_python_async_after_destroy_test/source/metacall_node_python_async_after_destroy_test.cpp new file mode 100644 index 0000000000..71ebbcdeab --- /dev/null +++ b/source/tests/metacall_node_python_async_after_destroy_test/source/metacall_node_python_async_after_destroy_test.cpp @@ -0,0 +1,69 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_async_after_destroy_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_async_after_destroy_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python & Mock */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `" + /* Python */ + "def sum(a, b):\n" + " return a + b\n" + "`);\n" + "function log(x) { console.log(x); return x; }\n" + "setTimeout(() => { log(metacall('sum', 3, 4)) === 7 || process.exit(1) }, 2000);\n" + "setTimeout(() => { log(metacall_inspect()) || process.exit(1) }, 4000);\n" + "setTimeout(() => { log(metacall('sum', 3, 4)) === 7 || process.exit(1) }, 6000);\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + /* This should be called before the setTimeout ends, so we can test if the node loader waits + * to be destroyed until the event loop is completely cleaned, so we can call safely to python + * even if the destroy was already emmited, for more info: + * https://github.com/metacall/core/commit/4b61c2f3b22065472828dc7b718defbfc4ac3884 + * https://github.com/metacall/core/commit/e963515cf68e04c91ba0612227d5ef586c08aab6 + * https://github.com/metacall/core/commit/df701d779af0d2a7cb1f27e33aacf5f17ae20f4b + * https://github.com/metacall/core/commit/1fc9c9244d7a5553861a458813d2cf1488ebe08c + * https://github.com/metacall/core/commit/9b64ee533079fa0d543fc346fb7149d1086451f0 + * https://github.com/metacall/core/commit/22bd999c281f23aac04cea7df435a836631706da + */ + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_await_extended_test/CMakeLists.txt b/source/tests/metacall_node_python_await_extended_test/CMakeLists.txt new file mode 100644 index 0000000000..a82165e896 --- /dev/null +++ b/source/tests/metacall_node_python_await_extended_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-await-extended-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_await_extended_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_await_extended_test/source/main.cpp b/source/tests/metacall_node_python_await_extended_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_await_extended_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_await_extended_test/source/metacall_node_python_await_extended_test.cpp b/source/tests/metacall_node_python_await_extended_test/source/metacall_node_python_await_extended_test.cpp new file mode 100644 index 0000000000..d773f0ba76 --- /dev/null +++ b/source/tests/metacall_node_python_await_extended_test/source/metacall_node_python_await_extended_test.cpp @@ -0,0 +1,94 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_await_extended_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_await_extended_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall_await, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "const util = require('util')\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "import threading\n" + "counter = 0\n" + "async def python_simple(n):\n" + "\tglobal counter\n" + "\tprint('inside python_simple', threading.current_thread().ident, counter, ':', n)\n" + "\tcounter = counter + 1\n" + "\tsys.stdout.flush()\n" + "\treturn n\n" + // "import asyncio\n" + // "async def python_simple(n):\n" + // " await asyncio.sleep(1)\n" + // " return n\n" + "`);\n" + /* Debug */ + "console.log('--------------------------------------------------------------------')\n" + "console.log(util.inspect(metacall_inspect(), false, null, true))\n" + "console.log('--------------------------------------------------------------------')\n" + /* NodeJS Check */ + "const size = 10000;\n" + "let buffer = new SharedArrayBuffer(4);\n" + "let int32 = new Int32Array(buffer);\n" + "Atomics.store(int32, 0, 0);\n" + "process.on('exit', () => {\n" + " if (Atomics.load(int32, 0) != size) {\n" + " process.exit(3);\n" + " }\n" + "});\n" + /* NodeJS Promise */ + "for (let i = 0; i < size; i++) {\n" + " metacall_await('python_simple', 32).then(v => {\n" + " console.log('RESULT:', v, Atomics.load(int32, 0));\n" + " if (v !== 32) {\n" + " process.exit(1);\n" + " }\n" + " Atomics.add(int32, 0, 1);\n" + " }).catch(v => {\n" + " console.log('ERROR:', v);\n" + " process.exit(2);\n" + " });\n" + "}\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_await_test/CMakeLists.txt b/source/tests/metacall_node_python_await_test/CMakeLists.txt new file mode 100644 index 0000000000..3553dbc2ca --- /dev/null +++ b/source/tests/metacall_node_python_await_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(PY_DEBUG_ENVIRONMENT_VARIABLES + PYTHONTHREADDEBUG=1 + PYTHONASYNCIODEBUG=1 + ) +else() + set(PY_DEBUG_ENVIRONMENT_VARIABLES) +endif() + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_await_test/source/main.cpp b/source/tests/metacall_node_python_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_await_test/source/metacall_node_python_await_test.cpp b/source/tests/metacall_node_python_await_test/source/metacall_node_python_await_test.cpp new file mode 100644 index 0000000000..9e63b1aa3c --- /dev/null +++ b/source/tests/metacall_node_python_await_test/source/metacall_node_python_await_test.cpp @@ -0,0 +1,88 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall_await, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "const util = require('util')\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "import threading\n" + "async def python_simple(n):\n" + "\tprint('inside python_simple', threading.current_thread().ident, ':', n)\n" + "\tsys.stdout.flush()\n" + "\treturn n\n" + // "import asyncio\n" + // "async def python_simple(n):\n" + // " await asyncio.sleep(1)\n" + // " return n\n" + "`);\n" + /* Debug */ + "console.log('--------------------------------------------------------------------')\n" + "console.log(util.inspect(metacall_inspect(), false, null, true))\n" + "console.log('--------------------------------------------------------------------')\n" + /* NodeJS Check */ + "let buffer = new SharedArrayBuffer(4);\n" + "let int32 = new Int32Array(buffer);\n" + "Atomics.store(int32, 0, 0);\n" + "process.on('exit', () => {\n" + " if (Atomics.load(int32, 0) != 1) {\n" + " process.exit(3);\n" + " }\n" + "});\n" + /* NodeJS Promise */ + "metacall_await('python_simple', 32).then(v => {\n" + " console.log('RESULT:', v);\n" + " if (v !== 32) {\n" + " process.exit(1);\n" + " }\n" + " Atomics.add(int32, 0, 1);\n" + "}).catch(v => {\n" + " console.log('ERROR:', v);\n" + " process.exit(2);\n" + "});\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_deadlock_test/CMakeLists.txt b/source/tests/metacall_node_python_deadlock_test/CMakeLists.txt new file mode 100644 index 0000000000..55bcbfed39 --- /dev/null +++ b/source/tests/metacall_node_python_deadlock_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-deadlock-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_deadlock_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_tests_properties(${target} PROPERTIES + LABELS ${target} + TIMEOUT 120 # Stop test after 120 seconds (to avoid deadlocking forever) +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_deadlock_test/source/main.cpp b/source/tests/metacall_node_python_deadlock_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_deadlock_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_deadlock_test/source/metacall_node_python_deadlock_test.cpp b/source/tests/metacall_node_python_deadlock_test/source/metacall_node_python_deadlock_test.cpp new file mode 100644 index 0000000000..d5cca59f22 --- /dev/null +++ b/source/tests/metacall_node_python_deadlock_test/source/metacall_node_python_deadlock_test.cpp @@ -0,0 +1,58 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_deadlock_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_deadlock_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer_py[] = "print('asd')\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer_py, sizeof(buffer_py), NULL)); + + static const char buffer_node[] = + "const { metacall_load_from_memory, metacall } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `\n" + "def something():\n" + " pass\n" + "`);\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer_node, sizeof(buffer_node), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_exception_test/CMakeLists.txt b/source/tests/metacall_node_python_exception_test/CMakeLists.txt new file mode 100644 index 0000000000..0d4b368692 --- /dev/null +++ b/source/tests/metacall_node_python_exception_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-exception-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_exception_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_exception_test/source/main.cpp b/source/tests/metacall_node_python_exception_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_exception_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_exception_test/source/metacall_node_python_exception_test.cpp b/source/tests/metacall_node_python_exception_test/source/metacall_node_python_exception_test.cpp new file mode 100644 index 0000000000..73f59386c1 --- /dev/null +++ b/source/tests/metacall_node_python_exception_test/source/metacall_node_python_exception_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_exception_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_exception_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "const { metacall_load_from_memory, metacall } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `\n" + "def py_throw_error():\n" + " raise TypeError('yeet')\n" + "`);\n" + "try {\n" + " metacall('py_throw_error');\n" + " process.exit(1);\n" + "} catch (e) {\n" + " console.log('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&');\n" + " console.log(e);\n" + " console.log('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&');\n" + " if (e.message !== 'yeet' || e.code !== 'TypeError') process.exit(1);\n" + "}\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_port_mock_test/CMakeLists.txt b/source/tests/metacall_node_python_port_mock_test/CMakeLists.txt new file mode 100644 index 0000000000..cbe593374d --- /dev/null +++ b/source/tests/metacall_node_python_port_mock_test/CMakeLists.txt @@ -0,0 +1,164 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_MOCK OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-port-mock-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_port_mock_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader + mock_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_port_mock_test/source/main.cpp b/source/tests/metacall_node_python_port_mock_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_port_mock_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_port_mock_test/source/metacall_node_python_port_mock_test.cpp b/source/tests/metacall_node_python_port_mock_test/source/metacall_node_python_port_mock_test.cpp new file mode 100644 index 0000000000..b1c9b76b4c --- /dev/null +++ b/source/tests/metacall_node_python_port_mock_test/source/metacall_node_python_port_mock_test.cpp @@ -0,0 +1,68 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_port_mock_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_port_mock_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python & Mock */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) && defined(OPTION_BUILD_LOADERS_MOCK) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall, metacall_load_from_memory } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + /* Mock */ + "from asd.mock import two_doubles\n" + "print('........................................................')\n" + "print(two_doubles(3.0, 6.0))\n" // This works + "print('........................................................')\n" + "def py_func(cb):\n" + " return cb(two_doubles)\n" + "print(py_func(lambda f: f(3, 4)))\n" // This works too + "`);\n" + // This does not, probably the error is when converting mock_func from metacall value to napi: + "const result = metacall('py_func', (mock_func) => mock_func(3, 4));\n" + "console.log('Result:', result);\n" + "if (result !== 3.1416) process.exit(1);\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY && OPTION_BUILD_LOADERS_MOCK */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_port_ruby_test/CMakeLists.txt b/source/tests/metacall_node_python_port_ruby_test/CMakeLists.txt new file mode 100644 index 0000000000..6dfbd6c337 --- /dev/null +++ b/source/tests/metacall_node_python_port_ruby_test/CMakeLists.txt @@ -0,0 +1,164 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-port-ruby-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_port_ruby_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_port_ruby_test/source/main.cpp b/source/tests/metacall_node_python_port_ruby_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_port_ruby_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_port_ruby_test/source/metacall_node_python_port_ruby_test.cpp b/source/tests/metacall_node_python_port_ruby_test/source/metacall_node_python_port_ruby_test.cpp new file mode 100644 index 0000000000..19acb40e98 --- /dev/null +++ b/source/tests/metacall_node_python_port_ruby_test/source/metacall_node_python_port_ruby_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_port_ruby_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_port_ruby_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python & Ruby */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) && defined(OPTION_BUILD_LOADERS_RB) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall, metacall_load_from_memory } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + /* Ruby: */ + "from hello.rb import say_sum_ducktyped, say_multiply_ducktyped\n" + "def py_func(js_func):\n" + " return js_func(lambda x, y: say_multiply_ducktyped(x, y) - say_sum_ducktyped(x, y))\n" + "`);\n" + "const result = metacall('py_func', (py_lambda) => py_lambda(3, 4));\n" + "console.log('Result:', result);\n" + "if (result !== 5.0) process.exit(1);\n"; // (3 * 4) - (3 + 4) = 5 + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY && OPTION_BUILD_LOADERS_RB */ + + metacall_destroy(); +} diff --git a/source/tests/rb_loader_parser_integration_test/CMakeLists.txt b/source/tests/metacall_node_python_ruby_test/CMakeLists.txt similarity index 73% rename from source/tests/rb_loader_parser_integration_test/CMakeLists.txt rename to source/tests/metacall_node_python_ruby_test/CMakeLists.txt index e6686cf86e..b5e2914333 100644 --- a/source/tests/rb_loader_parser_integration_test/CMakeLists.txt +++ b/source/tests/metacall_node_python_ruby_test/CMakeLists.txt @@ -1,5 +1,17 @@ # Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB) + return() +endif() + +# The test uses node fetch so it requires a version >= 18 +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(SEND_ERROR "NodeJS libraries not found") + return() +endif() + +if(NodeJS_VERSION VERSION_LESS "18.0.0") return() endif() @@ -8,7 +20,7 @@ endif() # # Target name -set(target rb-loader-parser-integration-test) +set(target metacall-node-python-ruby-test) message(STATUS "Test ${target}") # @@ -32,7 +44,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/rb_loader_parser_integration_test.cpp + ${source_path}/metacall_node_python_ruby_test.cpp ) # Group source files @@ -85,19 +97,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +118,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,6 +144,16 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader + rb_loader +) + # # Define test properties # diff --git a/source/tests/metacall_node_python_ruby_test/source/main.cpp b/source/tests/metacall_node_python_ruby_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_ruby_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_ruby_test/source/metacall_node_python_ruby_test.cpp b/source/tests/metacall_node_python_ruby_test/source/metacall_node_python_ruby_test.cpp new file mode 100644 index 0000000000..6253dffc4c --- /dev/null +++ b/source/tests/metacall_node_python_ruby_test/source/metacall_node_python_ruby_test.cpp @@ -0,0 +1,114 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +std::atomic success_callbacks{}; + +class metacall_node_python_ruby_test : public testing::Test +{ +public: +}; + +static void *hello_world_await_ok(void *result, void *data) +{ + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((void *)NULL, (void *)data); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_BOOL); + + EXPECT_EQ((boolean)metacall_value_to_bool(result), (boolean)1L); + + ++success_callbacks; + + return NULL; +} + +static void *hello_world_await_fail(void *, void *data) +{ + int this_should_never_happen = 1; + + EXPECT_EQ((void *)NULL, (void *)data); + + EXPECT_NE((int)0, (int)this_should_never_happen); + + return NULL; +} + +TEST_F(metacall_node_python_ruby_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Python */ + { + static const char buffer[] = + "print('Hello Python')\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } + + /* Ruby */ + { + static const char buffer[] = + "puts 'Hello Ruby'\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer, sizeof(buffer), NULL)); + } + + /* NodeJS */ + { + static const char buffer[] = + "module.exports = {\n" + " test: async function () {\n" + " try {\n" + " const result = fetch('http://github.com/service/https://www.google.com/', { signal: AbortSignal.timeout(60000) });\n" + " console.log(result);\n" + " return true;\n" + " } catch (e) {\n" + " console.log(e);\n" + " return false\n" + " }\n" + " }\n" + "};\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + /* Await function that returns */ + void *ret = metacall_await("test", metacall_null_args, hello_world_await_ok, hello_world_await_fail, NULL); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + } + + EXPECT_EQ((int)success_callbacks, (int)1); + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_reentrant_test/CMakeLists.txt b/source/tests/metacall_node_reentrant_test/CMakeLists.txt index 2b7297e943..6dfff474f4 100644 --- a/source/tests/metacall_node_reentrant_test/CMakeLists.txt +++ b/source/tests/metacall_node_reentrant_test/CMakeLists.txt @@ -85,7 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -109,11 +109,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -126,12 +135,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_reentrant_test/source/main.cpp b/source/tests/metacall_node_reentrant_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_node_reentrant_test/source/main.cpp +++ b/source/tests/metacall_node_reentrant_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_reentrant_test/source/metacall_node_reentrant_test.cpp b/source/tests/metacall_node_reentrant_test/source/metacall_node_reentrant_test.cpp index 93db1839a5..09f0c7af6c 100644 --- a/source/tests/metacall_node_reentrant_test/source/metacall_node_reentrant_test.cpp +++ b/source/tests/metacall_node_reentrant_test/source/metacall_node_reentrant_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include -#include #include +#include #ifndef METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH -# error "The path to the NodeJS port is not defined" + #error "The path to the NodeJS port is not defined" #endif class metacall_node_reentrant_test : public testing::Test @@ -37,10 +37,10 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { /* List of loads (1 level) */ { @@ -55,7 +55,7 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) static const char tag[] = "node"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); } /* Reentrant */ @@ -65,13 +65,13 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) "metacall_load_from_memory('node', 'module.exports = { node_memory_reentrant: () => 4 };');\n" "console.log('Reentrant node_memory_reentrant result (main):', metacall('node_memory_reentrant'));\n" "metacall_load_from_memory('node', '" - "const { metacall } = require(\"" METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH "\");" - "console.log(\"Reentrant node_memory_reentrant result (reentrant):\", metacall(\"node_memory_reentrant\"));" + "const { metacall } = require(\"" METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH "\");" + "console.log(\"Reentrant node_memory_reentrant result (reentrant):\", metacall(\"node_memory_reentrant\"));" "');\n"; static const char tag[] = "node"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); } /* Recursion */ @@ -83,21 +83,20 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) static const char tag[] = "node"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); - const enum metacall_value_id node_memory_recursive_ids[] = - { + const enum metacall_value_id node_memory_recursive_ids[] = { METACALL_INT }; - void * ret = metacallt("node_memory_recursive", node_memory_recursive_ids, 1); + void *ret = metacallt("node_memory_recursive", node_memory_recursive_ids, 1); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) 137438953472.0, (double) metacall_value_to_double(ret)); + EXPECT_EQ((double)137438953472.0, (double)metacall_value_to_double(ret)); } } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ /* Print inspect information */ { @@ -105,13 +104,13 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -120,5 +119,5 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_signal_handler_test/CMakeLists.txt b/source/tests/metacall_node_signal_handler_test/CMakeLists.txt new file mode 100644 index 0000000000..473ad45e51 --- /dev/null +++ b/source/tests/metacall_node_signal_handler_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +include(Portability) + +if(NOT PROJECT_OS_FAMILY STREQUAL unix) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-signal-handler-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_signal_handler_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_signal_handler_test/source/main.cpp b/source/tests/metacall_node_signal_handler_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_signal_handler_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_signal_handler_test/source/metacall_node_signal_handler_test.cpp b/source/tests/metacall_node_signal_handler_test/source/metacall_node_signal_handler_test.cpp new file mode 100644 index 0000000000..61e1ef0754 --- /dev/null +++ b/source/tests/metacall_node_signal_handler_test/source/metacall_node_signal_handler_test.cpp @@ -0,0 +1,134 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +std::atomic_bool callback_result(false); +std::atomic_bool signal_result(false); + +class metacall_node_signal_handler_test : public testing::Test +{ +public: +}; + +void *c_callback(size_t, void *[], void *) +{ + callback_result.store(true); + return metacall_value_create_long(raise(SIGUSR2)); /* Propagate signal */ +} + +static void handle_signals(int s) +{ + switch (s) + { + case SIGUSR1: + fprintf(stdout, "c++: received signal SIGUSR1\n"); + break; + case SIGUSR2: + fprintf(stdout, "c++: received signal SIGUSR1\n"); + signal_result.store(true); + break; + case SIGINT: + fprintf(stdout, "c++: received signal SIGINT\n"); + break; + case SIGCONT: + fprintf(stdout, "c++: received signal SIGCONT\n"); + break; + case SIGCHLD: + fprintf(stdout, "c++: received signal SIGCHLD\n"); + break; + case SIGTSTP: + fprintf(stdout, "c++: received signal SIGTSTP\n"); + break; + case SIGSTOP: + fprintf(stdout, "c++: received signal SIGSTOP\n"); + break; + default: + fprintf(stdout, "c++: received signal number %d\n", s); + break; + } +} + +TEST_F(metacall_node_signal_handler_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Register signal */ + signal(SIGUSR2, handle_signals); + + ASSERT_EQ((int)0, (int)metacall_register("c_callback", c_callback, NULL, METACALL_LONG, 0)); + + const char buffer[] = + "const { metacall } = require('" METACALL_NODE_PORT_PATH "');\n" + "let notified = false;\n" + "const notify = () => {\n" + " if (notified === true) return;\n" + " notified = true;\n" + " metacall('c_callback');\n" + "};\n" + "const cp = require('child_process');\n" + "let sp = cp.spawn('ps');\n" + "sp.stdout.on('data', data => {\n" + " console.log('node: stdout: ' + data.toString());\n" + "});\n" + "process.on('SIGCHLD', () => {\n" + " console.log(`node: Received SIGCHLD signal in process`);\n" + " notify()\n" + "});\n" + "sp.on('exit', code => {\n" + " console.log(`node: child process exited with code ${code}`);\n" + " notify();\n" /* Sometimes exit event gets received before SIGCHLD, so we notify anyway to c_callback */ + "});\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); + + EXPECT_EQ((bool)callback_result.load(), (bool)true); + EXPECT_EQ((bool)signal_result.load(), (bool)true); +} diff --git a/source/tests/metacall_node_test/CMakeLists.txt b/source/tests/metacall_node_test/CMakeLists.txt index 406a920b58..7c08d7c101 100644 --- a/source/tests/metacall_node_test/CMakeLists.txt +++ b/source/tests/metacall_node_test/CMakeLists.txt @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -109,6 +95,8 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + # For execution path test + METACALL_NODE_TEST_EXECUTION_PATH="${CMAKE_SOURCE_DIR}/source/scripts/node/inline/source" ) # @@ -120,11 +108,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,12 +134,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_test/source/main.cpp b/source/tests/metacall_node_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_node_test/source/main.cpp +++ b/source/tests/metacall_node_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_test/source/metacall_node_test.cpp b/source/tests/metacall_node_test/source/metacall_node_test.cpp index d4d9785e82..f38954e090 100644 --- a/source/tests/metacall_node_test/source/metacall_node_test.cpp +++ b/source/tests/metacall_node_test/source/metacall_node_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_node_test : public testing::Test { @@ -33,34 +33,94 @@ TEST_F(metacall_node_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); + + // Test execution path + EXPECT_EQ((int)0, (int)metacall_execution_path("node", METACALL_NODE_TEST_EXECUTION_PATH)); + + const char *node_execution_path_scripts[] = { + "inline.js" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_execution_path_scripts, sizeof(node_execution_path_scripts) / sizeof(node_execution_path_scripts[0]), NULL)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("inline")); + + // Test load from memory + map type + const char buffer[] = { + "module.exports = { test_map: (m) => m.a + m.b };" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("test_map")); + + void *args[] = { + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla0 = metacall_value_to_array(map_value[0]); + + static const char key0[] = "a"; + + tupla0[0] = metacall_value_create_string(key0, sizeof(key0) - 1); + tupla0[1] = metacall_value_create_double(5.0); + + map_value[1] = metacall_value_create_array(NULL, 2); + + void **tupla1 = metacall_value_to_array(map_value[1]); + + static const char key1[] = "b"; + + tupla1[0] = metacall_value_create_string(key1, sizeof(key1) - 1); + tupla1[1] = metacall_value_create_double(7.0); + + ret = metacallv_s("test_map", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((double)12.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + + const char *node_module_scripts[] = { + "path" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_module_scripts, sizeof(node_module_scripts) / sizeof(node_module_scripts[0]), &handle)); + + EXPECT_NE((void *)NULL, (void *)handle); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ /* Print inspect information */ { @@ -68,13 +128,13 @@ TEST_F(metacall_node_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -83,5 +143,5 @@ TEST_F(metacall_node_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_typescript_test/CMakeLists.txt b/source/tests/metacall_node_typescript_test/CMakeLists.txt new file mode 100644 index 0000000000..499baca66e --- /dev/null +++ b/source/tests/metacall_node_typescript_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-typescript-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_typescript_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_typescript_test/source/main.cpp b/source/tests/metacall_node_typescript_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_typescript_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_typescript_test/source/metacall_node_typescript_test.cpp b/source/tests/metacall_node_typescript_test/source/metacall_node_typescript_test.cpp new file mode 100644 index 0000000000..8bf39ee566 --- /dev/null +++ b/source/tests/metacall_node_typescript_test/source/metacall_node_typescript_test.cpp @@ -0,0 +1,142 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_typescript_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_typescript_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" + }; + + void *ret = NULL; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + ret = metacall("typed_sum", 3.0, 4.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); + + metacall_value_destroy(ret); + + /* Test arrays */ + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_double(3.0); + array_value[1] = metacall_value_create_double(5.0); + array_value[2] = metacall_value_create_double(7.0); + + ret = metacallv("typed_array", array_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(array_args[0]); + + /* Test records */ + void *record_args[] = { + metacall_value_create_map(NULL, 1) + }; + + void **map_value = metacall_value_to_map(record_args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla = metacall_value_to_array(map_value[0]); + + static const char key[] = "element"; + + tupla[0] = metacall_value_create_string(key, sizeof(key) - 1); + tupla[1] = metacall_value_create_double(6.0); + + ret = metacallv("object_record", record_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(record_args[0]); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_destroy_order_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_destroy_order_test/CMakeLists.txt new file mode 100644 index 0000000000..0bf7f39890 --- /dev/null +++ b/source/tests/metacall_plugin_extension_destroy_order_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_CLI OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-destroy-order-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_destroy_order_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +set(METACALL_PLUGIN_PATH "${PROJECT_OUTPUT_DIR}/metacall_plugin_extension_destroy_order_test") + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Plugin path + METACALL_PLUGIN_PATH="${METACALL_PLUGIN_PATH}" +) + +# Isolate the plugin into a different directory in order to make it fully reproducible +add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}/cli_core_plugin" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/repl/cli_core_plugin" "${METACALL_PLUGIN_PATH}/cli_core_plugin" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + plugin_extension + cli_core_plugin # Requires cli_core_plugin (from CLI) for reproducing it +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_destroy_order_test/source/main.cpp b/source/tests/metacall_plugin_extension_destroy_order_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_destroy_order_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_destroy_order_test/source/metacall_plugin_extension_destroy_order_test.cpp b/source/tests/metacall_plugin_extension_destroy_order_test/source/metacall_plugin_extension_destroy_order_test.cpp new file mode 100644 index 0000000000..7d094870ca --- /dev/null +++ b/source/tests/metacall_plugin_extension_destroy_order_test/source/metacall_plugin_extension_destroy_order_test.cpp @@ -0,0 +1,81 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_destroy_order_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_destroy_order_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&handle) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_invalid_path_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_invalid_path_test/CMakeLists.txt new file mode 100644 index 0000000000..6e5da526f6 --- /dev/null +++ b/source/tests/metacall_plugin_extension_invalid_path_test/CMakeLists.txt @@ -0,0 +1,163 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_CLI OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-invalid-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_invalid_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +set(METACALL_PLUGIN_PATH "${PROJECT_OUTPUT_DIR}/this/folder/does/not/exist") + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Plugin path + METACALL_PLUGIN_PATH="${METACALL_PLUGIN_PATH}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + plugin_extension + cli_core_plugin # Requires cli_core_plugin (from CLI) for reproducing it +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_invalid_path_test/source/main.cpp b/source/tests/metacall_plugin_extension_invalid_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_invalid_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_invalid_path_test/source/metacall_plugin_extension_invalid_path_test.cpp b/source/tests/metacall_plugin_extension_invalid_path_test/source/metacall_plugin_extension_invalid_path_test.cpp new file mode 100644 index 0000000000..31fec9d9b4 --- /dev/null +++ b/source/tests/metacall_plugin_extension_invalid_path_test/source/metacall_plugin_extension_invalid_path_test.cpp @@ -0,0 +1,81 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_extension_invalid_path_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_extension_invalid_path_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&handle) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_local_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_local_test/CMakeLists.txt new file mode 100644 index 0000000000..174628c0ec --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-local-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_local_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + METACALL_PLUGIN_PATH="${CMAKE_CURRENT_SOURCE_DIR}/plugins" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + py_loader + plugin_extension +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/.gitignore b/source/tests/metacall_plugin_extension_local_test/plugins/.gitignore new file mode 100644 index 0000000000..68bc17f9ff --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/metacall.json b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/metacall.json new file mode 100644 index 0000000000..ec2ac1db88 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginA.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/pluginA.py b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/pluginA.py new file mode 100644 index 0000000000..afb07fc875 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/pluginA.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginA(): + print('Hello World from extensionA!!') + return 6 diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/metacall-extB.json b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/metacall-extB.json new file mode 100644 index 0000000000..8fab42668b --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/metacall-extB.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginB.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/pluginB.py b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/pluginB.py new file mode 100644 index 0000000000..18a4c47424 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/pluginB.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginB(): + print('Hello World from extensionB!!') + return 7 diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/metacall-extC.json b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/metacall-extC.json new file mode 100644 index 0000000000..bd690f9322 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/metacall-extC.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "pluginC.js" + ] +} diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/pluginC.js b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/pluginC.js new file mode 100644 index 0000000000..a069ef53bc --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/pluginC.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node + +function pluginC() { + console.log('Hello World, from extensionC'); + return 8; +} + +module.exports = { + pluginC +}; diff --git a/source/tests/metacall_plugin_extension_local_test/source/main.cpp b/source/tests/metacall_plugin_extension_local_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_local_test/source/metacall_plugin_extension_local_test.cpp b/source/tests/metacall_plugin_extension_local_test/source/metacall_plugin_extension_local_test.cpp new file mode 100644 index 0000000000..060200f35d --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/source/metacall_plugin_extension_local_test.cpp @@ -0,0 +1,123 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_extension_local_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_extension_local_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&handle) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + void *ret = metacallhv_s(handle, "pluginA", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)6, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } + + { + void *ret = metacallhv_s(handle, "pluginB", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)7, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + void *ret = metacallhv_s(handle, "pluginC", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((double)8.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_test/CMakeLists.txt new file mode 100644 index 0000000000..ed506009f8 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + METACALL_PLUGIN_PATH="${CMAKE_CURRENT_SOURCE_DIR}/plugins" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + py_loader + plugin_extension +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_test/plugins/.gitignore b/source/tests/metacall_plugin_extension_test/plugins/.gitignore new file mode 100644 index 0000000000..68bc17f9ff --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/metacall.json b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/metacall.json new file mode 100644 index 0000000000..ec2ac1db88 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginA.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/pluginA.py b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/pluginA.py new file mode 100644 index 0000000000..afb07fc875 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/pluginA.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginA(): + print('Hello World from extensionA!!') + return 6 diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/metacall-extB.json b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/metacall-extB.json new file mode 100644 index 0000000000..8fab42668b --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/metacall-extB.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginB.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/pluginB.py b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/pluginB.py new file mode 100644 index 0000000000..18a4c47424 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/pluginB.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginB(): + print('Hello World from extensionB!!') + return 7 diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/metacall-extC.json b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/metacall-extC.json new file mode 100644 index 0000000000..bd690f9322 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/metacall-extC.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "pluginC.js" + ] +} diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/pluginC.js b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/pluginC.js new file mode 100644 index 0000000000..a069ef53bc --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/pluginC.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node + +function pluginC() { + console.log('Hello World, from extensionC'); + return 8; +} + +module.exports = { + pluginC +}; diff --git a/source/tests/metacall_plugin_extension_test/source/main.cpp b/source/tests/metacall_plugin_extension_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_test/source/metacall_plugin_extension_test.cpp b/source/tests/metacall_plugin_extension_test/source/metacall_plugin_extension_test.cpp new file mode 100644 index 0000000000..4d1d4ac99a --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/source/metacall_plugin_extension_test.cpp @@ -0,0 +1,121 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_extension_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_extension_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(result); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + void *ret = metacall("pluginA"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)6, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } + + { + void *ret = metacall("pluginB"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)7, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + void *ret = metacall("pluginC"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((double)8.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_async_test/CMakeLists.txt b/source/tests/metacall_python_async_test/CMakeLists.txt new file mode 100644 index 0000000000..c329c7a319 --- /dev/null +++ b/source/tests/metacall_python_async_test/CMakeLists.txt @@ -0,0 +1,152 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-async-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_async_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/py_django_integration_test/source/main.cpp b/source/tests/metacall_python_async_test/source/main.cpp similarity index 88% rename from source/tests/py_django_integration_test/source/main.cpp rename to source/tests/metacall_python_async_test/source/main.cpp index 14fb34603e..33a4612744 100644 --- a/source/tests/py_django_integration_test/source/main.cpp +++ b/source/tests/metacall_python_async_test/source/main.cpp @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_async_test/source/metacall_python_async_test.cpp b/source/tests/metacall_python_async_test/source/metacall_python_async_test.cpp new file mode 100644 index 0000000000..736753c3d9 --- /dev/null +++ b/source/tests/metacall_python_async_test/source/metacall_python_async_test.cpp @@ -0,0 +1,164 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +class metacall_python_async_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_async_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + /* TODO: Create another test using Curio library? */ + const char buffer[] = + "import asyncio\n" + "import threading\n" + "import sys\n" + + "print('sync message', threading.current_thread().ident)\n" + "sys.stdout.flush()\n" + + "async def my_async_fn(n):\n" + "\tprint('inside my sleep async', threading.current_thread().ident, ':', n)\n" + "\tsys.stdout.flush()\n" + "\treturn 58\n" + + "async def my_async_fail_fn(n):\n" + "\tprint('inside my sleep fail async', threading.current_thread().ident, ':', n)\n" + "\tsys.stdout.flush()\n" + "\traise Exception(15)\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + /* Test for asyncness Python introspection */ + EXPECT_EQ((int)1, metacall_function_async(metacall_function("my_async_fn"))); + + void *args[] = { + metacall_value_create_long(2L) + }; + + std::thread::id this_id = std::this_thread::get_id(); + std::cout << "thread " << this_id << std::endl; + + /* Test resolve */ + { + auto resolve = [](void *result, void *) -> void * { + std::thread::id this_id = std::this_thread::get_id(); + std::cout << "thread " << this_id << std::endl; + + printf("Got into C callback at least\n"); + fflush(stdout); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_LONG); + + EXPECT_EQ((long)metacall_value_to_long(result), (long)58L); + + printf("Resolve C Callback\n"); + fflush(stdout); + + return NULL; + }; + + auto reject = [](void *, void *) -> void * { + /* If we reach here, there's a serious bug in the C code that is called by python after the task is done */ + int never_executed = 0; + EXPECT_EQ((int)1, (int)never_executed); + + printf("Reject C Callback\n"); + fflush(stdout); + + return NULL; + }; + + void *future = metacall_await("my_async_fn", args, resolve, reject, NULL); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + } + + /* Test reject */ + { + auto resolve = [](void *, void *) -> void * { + /* If we reach here, there's a serious bug in the C code that is called by python after the task is done */ + int never_executed = 0; + EXPECT_EQ((int)1, (int)never_executed); + + printf("Reject C Callback\n"); + fflush(stdout); + + return NULL; + }; + + auto reject = [](void *result, void *) -> void * { + std::thread::id this_id = std::this_thread::get_id(); + std::cout << "thread " << this_id << std::endl; + + printf("Got into C callback at least\n"); + fflush(stdout); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_LONG); + + EXPECT_EQ((long)metacall_value_to_long(result), (long)15L); + + printf("Resolve C Callback\n"); + fflush(stdout); + + return NULL; + }; + + void *future = metacall_await("my_async_fail_fn", args, resolve, reject, NULL); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + } + + metacall_value_destroy(args[0]); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_class_test/CMakeLists.txt b/source/tests/metacall_python_await_test/CMakeLists.txt similarity index 75% rename from source/tests/metacall_python_class_test/CMakeLists.txt rename to source/tests/metacall_python_await_test/CMakeLists.txt index 2ef4e16088..9c34033915 100644 --- a/source/tests/metacall_python_class_test/CMakeLists.txt +++ b/source/tests/metacall_python_await_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) return() endif() @@ -8,7 +8,7 @@ endif() # # Target name -set(target metacall-python-class-test) +set(target metacall-python-await-test) message(STATUS "Test ${target}") # @@ -32,7 +32,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/metacall_python_class_test.cpp + ${source_path}/metacall_python_await_test.cpp ) # Group source files @@ -85,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -109,6 +95,9 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" ) # @@ -120,11 +109,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -137,6 +135,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # @@ -147,7 +153,17 @@ set_property(TEST ${target} include(TestEnvironmentVariables) +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(PY_DEBUG_ENVIRONMENT_VARIABLES + PYTHONTHREADDEBUG=1 + PYTHONASYNCIODEBUG=1 + ) +else() + set(PY_DEBUG_ENVIRONMENT_VARIABLES) +endif() + test_environment_variables(${target} "" ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} ) diff --git a/source/tests/metacall_python_await_test/source/main.cpp b/source/tests/metacall_python_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_await_test/source/metacall_python_await_test.cpp b/source/tests/metacall_python_await_test/source/metacall_python_await_test.cpp new file mode 100644 index 0000000000..71ff8b6f50 --- /dev/null +++ b/source/tests/metacall_python_await_test/source/metacall_python_await_test.cpp @@ -0,0 +1,72 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "import asyncio\n" + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "from metacall import metacall_load_from_memory, metacall_await\n" + "script = \"\"\"\n" + "async def yeet(n):\n" + " return n\n" + "\"\"\"\n" + "result = 0\n" + "lock = threading.Lock()\n" + "metacall_load_from_memory('py', script)\n" + "async def test():\n" + " result = await metacall_await('yeet', 1234)\n" + " if result != 1234:\n" + " sys.exit(1)\n" + " else:\n" + " lock.acquire()\n" + " result = 1\n" + " lock.release()\n" + "asyncio.run(test())\n" + "lock.acquire()\n" + "if result != 1:\n" + " sys.exit(2)\n" + "lock.release()\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_builtins_test/CMakeLists.txt b/source/tests/metacall_python_builtins_test/CMakeLists.txt new file mode 100644 index 0000000000..698df7f4bb --- /dev/null +++ b/source/tests/metacall_python_builtins_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-builtins-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_builtins_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/py_loader_test/source/main.cpp b/source/tests/metacall_python_builtins_test/source/main.cpp similarity index 81% rename from source/tests/py_loader_test/source/main.cpp rename to source/tests/metacall_python_builtins_test/source/main.cpp index 8371fcd178..7832a63230 100644 --- a/source/tests/py_loader_test/source/main.cpp +++ b/source/tests/metacall_python_builtins_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading python code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_builtins_test/source/metacall_python_builtins_test.cpp b/source/tests/metacall_python_builtins_test/source/metacall_python_builtins_test.cpp new file mode 100644 index 0000000000..a806d04427 --- /dev/null +++ b/source/tests/metacall_python_builtins_test/source/metacall_python_builtins_test.cpp @@ -0,0 +1,81 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_builtins_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_python_builtins_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "binascii", + "decimal" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char *py_scripts_ref[] = { + "sys" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts_ref, sizeof(py_scripts_ref) / sizeof(py_scripts_ref[0]), &handle)); + + EXPECT_EQ((int)0, metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_callback_test/CMakeLists.txt b/source/tests/metacall_python_callback_test/CMakeLists.txt new file mode 100644 index 0000000000..e576930fb8 --- /dev/null +++ b/source/tests/metacall_python_callback_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY + OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-callback-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_callback_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_callback_test/source/main.cpp b/source/tests/metacall_python_callback_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_callback_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_callback_test/source/metacall_python_callback_test.cpp b/source/tests/metacall_python_callback_test/source/metacall_python_callback_test.cpp new file mode 100644 index 0000000000..fa15992d94 --- /dev/null +++ b/source/tests/metacall_python_callback_test/source/metacall_python_callback_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_callback_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_callback_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "host.py" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + ret = metacall("a"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + void *handle = metacall_handle("py", "host.py"); + + EXPECT_NE((void *)NULL, (void *)handle); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_dict_test/CMakeLists.txt b/source/tests/metacall_python_dict_test/CMakeLists.txt index 9b30e9ad6c..835a24fed9 100644 --- a/source/tests/metacall_python_dict_test/CMakeLists.txt +++ b/source/tests/metacall_python_dict_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_PY) return() endif() @@ -85,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,6 +132,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_dict_test/source/main.cpp b/source/tests/metacall_python_dict_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_python_dict_test/source/main.cpp +++ b/source/tests/metacall_python_dict_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_dict_test/source/metacall_python_dict_test.cpp b/source/tests/metacall_python_dict_test/source/metacall_python_dict_test.cpp index c11eea4eb3..f5db7671ce 100644 --- a/source/tests/metacall_python_dict_test/source/metacall_python_dict_test.cpp +++ b/source/tests/metacall_python_dict_test/source/metacall_python_dict_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,45 +32,44 @@ TEST_F(metacall_python_dict_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "dicty.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = metacall("nice_dict"); + void *ret = metacall("nice_dict"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - void ** dict = metacall_value_to_map(ret); + void **dict = metacall_value_to_map(ret); size_t size = metacall_value_count(ret); for (size_t iterator = 0; iterator < size; ++iterator) { - void ** array = metacall_value_to_array(dict[iterator]); - char * key = metacall_value_to_string(array[0]); + void **array = metacall_value_to_array(dict[iterator]); + char *key = metacall_value_to_string(array[0]); if (strcmp(key, "asd") == 0) { - EXPECT_EQ((long) 123, (long) metacall_value_to_long(array[1])); + EXPECT_EQ((long)123, (long)metacall_value_to_long(array[1])); } else if (strcmp(key, "hello") == 0) { - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(array[1]), "world")); + EXPECT_STREQ(metacall_value_to_string(array[1]), "world"); } else if (strcmp(key, "efg") == 0) { - EXPECT_EQ((double) 3.4, (double) metacall_value_to_double(array[1])); + EXPECT_EQ((double)3.4, (double)metacall_value_to_double(array[1])); } else { - EXPECT_NE((int) 0, (int) 0); + EXPECT_NE((int)0, (int)0); } } @@ -78,29 +77,76 @@ TEST_F(metacall_python_dict_test, DefaultConstructor) ret = metacall("non_supported_dict"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); dict = metacall_value_to_map(ret); size = metacall_value_count(ret); for (size_t iterator = 0; iterator < size; ++iterator) { - void ** array = metacall_value_to_array(dict[iterator]); - char * key = metacall_value_to_string(array[0]); + void **array = metacall_value_to_array(dict[iterator]); + char *key = metacall_value_to_string(array[0]); if (strcmp(key, "asd") == 0) { - EXPECT_EQ((long) 123, (long) metacall_value_to_long(array[1])); + EXPECT_EQ((long)123, (long)metacall_value_to_long(array[1])); } else { - EXPECT_NE((int) 0, (int) 0); + EXPECT_NE((int)0, (int)0); } } metacall_value_destroy(ret); + + void *args[] = { + // "old" -> 5 + // "whatever" -> 7 + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla0 = metacall_value_to_array(map_value[0]); + + static const char key0[] = "old"; + + tupla0[0] = metacall_value_create_string(key0, sizeof(key0) - 1); + tupla0[1] = metacall_value_create_long(5); + + map_value[1] = metacall_value_create_array(NULL, 2); + + void **tupla1 = metacall_value_to_array(map_value[1]); + + static const char key1[] = "whatever"; + + tupla1[0] = metacall_value_create_string(key1, sizeof(key1) - 1); + tupla1[1] = metacall_value_create_long(7); + + ret = metacallv_s("with_love_for_pragma_devs", args, 1); + + metacall_value_destroy(args[0]); + + void **ret_map = metacall_value_to_map(ret); + void **ret_pair0 = metacall_value_to_array(ret_map[0]); + char *ret_key0 = metacall_value_to_string(ret_pair0[0]); + long ret_value0 = metacall_value_to_long(ret_pair0[1]); + + EXPECT_STREQ(ret_key0, "new"); + EXPECT_EQ((long)5, (long)ret_value0); + + void **ret_pair1 = metacall_value_to_array(ret_map[1]); + char *ret_key1 = metacall_value_to_string(ret_pair1[0]); + long ret_value1 = metacall_value_to_long(ret_pair1[1]); + + EXPECT_STREQ(ret_key1, "whatever"); + EXPECT_EQ((long)7, (long)ret_value1); + + metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -108,13 +154,13 @@ TEST_F(metacall_python_dict_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -123,5 +169,5 @@ TEST_F(metacall_python_dict_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_exception_test/CMakeLists.txt b/source/tests/metacall_python_exception_test/CMakeLists.txt new file mode 100644 index 0000000000..e06790523f --- /dev/null +++ b/source/tests/metacall_python_exception_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-exception-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_exception_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_exception_test/source/main.cpp b/source/tests/metacall_python_exception_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_exception_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_exception_test/source/metacall_python_exception_test.cpp b/source/tests/metacall_python_exception_test/source/metacall_python_exception_test.cpp new file mode 100644 index 0000000000..14ec35d757 --- /dev/null +++ b/source/tests/metacall_python_exception_test/source/metacall_python_exception_test.cpp @@ -0,0 +1,76 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_exception_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_exception_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "def py_throw_error():\n" + " raise TypeError('yeet')\n" + "\n" + "def py_return_error():\n" + " return BaseException('asdf')\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("py_throw_error"); + + struct metacall_exception_type ex; + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("yeet", ex.message); + + EXPECT_STREQ("TypeError", ex.label); + + metacall_value_destroy(ret); + + ret = metacall("py_return_error"); + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("asdf", ex.message); + + EXPECT_STREQ("BaseException", ex.label); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_fail_test/CMakeLists.txt b/source/tests/metacall_python_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..d16a571d28 --- /dev/null +++ b/source/tests/metacall_python_fail_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_fail_test/source/main.cpp b/source/tests/metacall_python_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp b/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp new file mode 100644 index 0000000000..7ef4531f60 --- /dev/null +++ b/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp @@ -0,0 +1,97 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char buffer[] = + "def sumList(list: [int]):\n" + " return sum(list)\n"; + + // Apparently this loads the function but it does not understand the type + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + enum metacall_value_id id; + + EXPECT_EQ((int)0, (int)metacall_function_parameter_type(metacall_function("sumList"), 0, &id)); + + // The type of list must be invalid once it loads + EXPECT_EQ((enum metacall_value_id)METACALL_INVALID, (enum metacall_value_id)id); + + const char *py_scripts[] = { + "this_does_not_exists_yeet.py" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char buffer_fail[] = + "def sdf: as asf return"; + + // This must fail + EXPECT_EQ((int)1, (int)metacall_load_from_memory("py", buffer_fail, sizeof(buffer_fail), NULL)); + + const char *py_scripts_non_installed[] = { + "badimport.py" + }; + + // Print traceback + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_non_installed, sizeof(py_scripts_non_installed) / sizeof(py_scripts_non_installed[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_gc_test/CMakeLists.txt b/source/tests/metacall_python_gc_test/CMakeLists.txt index 89c1554110..b726024342 100644 --- a/source/tests/metacall_python_gc_test/CMakeLists.txt +++ b/source/tests/metacall_python_gc_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if this loader is enabled -if(NOT OPTION_BUILD_DIST_LIBS OR NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) return() endif() @@ -59,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -94,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -117,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -134,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_gc_test/source/main.cpp b/source/tests/metacall_python_gc_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_python_gc_test/source/main.cpp +++ b/source/tests/metacall_python_gc_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp b/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp index f9ed54ea36..8bdea252fb 100644 --- a/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp +++ b/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,37 +32,38 @@ TEST_F(metacall_python_gc_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "garbage.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = metacall("set_debug"); + void *ret = metacall("set_debug"); - EXPECT_NE((void *) NULL, (void *) ret); + ASSERT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret)); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); metacall_value_destroy(ret); ret = metacall("garbage"); - ASSERT_NE((void *) NULL, (void *) ret); + ASSERT_NE((void *)NULL, (void *)ret); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); std::cout << metacall_value_to_string(ret) << std::endl; metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/py_loader_port_test/CMakeLists.txt b/source/tests/metacall_python_loader_port_test/CMakeLists.txt similarity index 88% rename from source/tests/py_loader_port_test/CMakeLists.txt rename to source/tests/metacall_python_loader_port_test/CMakeLists.txt index afaf0cb98b..eed75ba06a 100644 --- a/source/tests/py_loader_port_test/CMakeLists.txt +++ b/source/tests/metacall_python_loader_port_test/CMakeLists.txt @@ -32,7 +32,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/py_loader_port_test.cpp + ${source_path}/metacall_python_loader_port_test.cpp ) # Group source files @@ -85,7 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -106,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -123,6 +132,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + rb_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_loader_port_test/source/main.cpp b/source/tests/metacall_python_loader_port_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_loader_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_loader_port_test/source/metacall_python_loader_port_test.cpp b/source/tests/metacall_python_loader_port_test/source/metacall_python_loader_port_test.cpp new file mode 100644 index 0000000000..ffa9043263 --- /dev/null +++ b/source/tests/metacall_python_loader_port_test/source/metacall_python_loader_port_test.cpp @@ -0,0 +1,135 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_loader_port_test : public testing::Test +{ +public: +}; + +void *callback_host(size_t argc, void *args[], void *data) +{ + const char *str = metacall_value_cast_string(&args[0]); + + (void)argc; + (void)data; + + printf("Host callback: %s\n", str); + + EXPECT_STREQ(str, "some text"); + + return metacall_value_create_int(25); +} + +TEST_F(metacall_python_loader_port_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Native register */ + { + metacall_register("callback_host_impl", callback_host, NULL, METACALL_INT, 1, METACALL_STRING); + + EXPECT_NE((void *)NULL, (void *)metacall_function("callback_host_impl")); + } + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "hello.rb", "second.rb" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + ret = metacall("say_multiply", 5, 7); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)35, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + + ret = metacall("say_null"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); + + ret = metacall("say_hello", "meta-programmer"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_cast_string(&ret), "Hello meta-programmer!"); + + metacall_value_destroy(ret); + + EXPECT_NE((void *)NULL, (void *)metacall_function("get_second")); + + ret = metacall("get_second", 5, 12); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)12, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "callback.py" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + ret = metacall("hello_world", "some text"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)25, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + + ret = metacall("hello_ruby", 12, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)48, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_model_test/CMakeLists.txt b/source/tests/metacall_python_model_test/CMakeLists.txt index 37e56b1f73..01475c8c83 100644 --- a/source/tests/metacall_python_model_test/CMakeLists.txt +++ b/source/tests/metacall_python_model_test/CMakeLists.txt @@ -97,19 +97,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -131,11 +118,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -148,6 +144,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_model_test/source/main.cpp b/source/tests/metacall_python_model_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_python_model_test/source/main.cpp +++ b/source/tests/metacall_python_model_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp b/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp index 73c13e4715..ca200e45b8 100644 --- a/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp +++ b/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,28 +32,26 @@ TEST_F(metacall_python_model_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "model.py" }; - const enum metacall_value_id ids[] = - { + const enum metacall_value_id ids[] = { METACALL_DOUBLE }; - ASSERT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = metacallt("predict_salary", ids, 5.0); + void *ret = metacallt("predict_salary", ids, 5.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - void ** salary_array = metacall_value_to_array(ret); + void **salary_array = metacall_value_to_array(ret); double salary = metacall_value_to_double(salary_array[0]); @@ -61,7 +59,7 @@ TEST_F(metacall_python_model_test, DefaultConstructor) metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -69,13 +67,13 @@ TEST_F(metacall_python_model_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -84,5 +82,5 @@ TEST_F(metacall_python_model_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_node_await_test/CMakeLists.txt b/source/tests/metacall_python_node_await_test/CMakeLists.txt new file mode 100644 index 0000000000..e2d89c8ffc --- /dev/null +++ b/source/tests/metacall_python_node_await_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-node-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_node_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(PY_DEBUG_ENVIRONMENT_VARIABLES + PYTHONTHREADDEBUG=1 + PYTHONASYNCIODEBUG=1 + ) +else() + set(PY_DEBUG_ENVIRONMENT_VARIABLES) +endif() + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_node_await_test/source/main.cpp b/source/tests/metacall_python_node_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_node_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_node_await_test/source/metacall_python_node_await_test.cpp b/source/tests/metacall_python_node_await_test/source/metacall_python_node_await_test.cpp new file mode 100644 index 0000000000..168f3517c4 --- /dev/null +++ b/source/tests/metacall_python_node_await_test/source/metacall_python_node_await_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_node_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_node_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import threading\n" + "import asyncio\n" + "from metacall import metacall_load_from_memory, metacall_await\n" + "script = \"\"\"\n" + "async function yeet(n) {\n" + " return n;\n" + "}\n" + "module.exports = { yeet }\n" + "\"\"\"\n" + "result = 0\n" + "lock = threading.Lock()\n" + "metacall_load_from_memory('node', script)\n" + "async def test():\n" + " result = await metacall_await('yeet', 1234)\n" + " if result != 1234:\n" + " sys.exit(1)\n" + " else:\n" + " lock.acquire()\n" + " result = 1\n" + " lock.release()\n" + "asyncio.run(test())\n" + "lock.acquire()\n" + "if result != 1:\n" + " sys.exit(2)\n" + "lock.release()\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_object_class_test/CMakeLists.txt b/source/tests/metacall_python_object_class_test/CMakeLists.txt new file mode 100644 index 0000000000..f81c535ee8 --- /dev/null +++ b/source/tests/metacall_python_object_class_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-object-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_object_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_object_class_test/source/main.cpp b/source/tests/metacall_python_object_class_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_object_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_object_class_test/source/metacall_python_object_class_test.cpp b/source/tests/metacall_python_object_class_test/source/metacall_python_object_class_test.cpp new file mode 100644 index 0000000000..092c58927c --- /dev/null +++ b/source/tests/metacall_python_object_class_test/source/metacall_python_object_class_test.cpp @@ -0,0 +1,225 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_class_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_class_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "classname.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *return_itself_args[] = { + myclass_value + }; + void *return_itself = metacallv("return_itself", return_itself_args); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(return_itself)); + metacall_value_destroy(return_itself); + metacall_value_destroy(myclass_value); + } + + { + void *myobject_value = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(myobject_value)); + void *return_itself_args[] = { + myobject_value + }; + void *return_itself = metacallv("return_itself", return_itself_args); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(return_itself)); + metacall_value_destroy(return_itself); + metacall_value_destroy(myobject_value); + } + + { + void *ret = metacall("function_returns_object_new_local_variable"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + } + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *myclass = metacall_value_to_class(myclass_value); + + static const char john[] = "John Doe"; + + void *constructor_params[] = { + metacall_value_create_string(john, sizeof(john) - 1), // param1 + metacall_value_create_int(999999) // param2 + }; + void *new_object_v = metacall_class_new(myclass, "objectname", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + metacall_value_destroy(constructor_params[1]); + void *new_object = metacall_value_to_object(new_object_v); + + void *param2 = metacall_object_get(new_object, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)999999, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + void *long_value = metacall_value_create_long(124124L); + int retcode = metacall_object_set(new_object, "b", long_value); + metacall_value_destroy(long_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_object_get(new_object, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)124124L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + metacall_value_destroy(new_object_v); + metacall_value_destroy(myclass_value); + } + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *myclass = metacall_value_to_class(myclass_value); + + void *param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)20L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + void *long_value = metacall_value_create_long(44444L); + int retcode = metacall_class_static_set(myclass, "b", long_value); + metacall_value_destroy(long_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)44444L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + metacall_value_destroy(myclass_value); + } + + { + void *myclass = metacall_class("MyClass"); + ASSERT_NE((void *)NULL, (void *)myclass); + + static const char works[] = "It works!"; + + void *static_method_args[] = { + metacall_value_create_string(works, sizeof(works) - 1) + }; + void *ret_value = metacallv_class(myclass, "static", static_method_args, sizeof(static_method_args) / sizeof(static_method_args[0])); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret_value)); + metacall_value_destroy(static_method_args[0]); + metacall_value_destroy(ret_value); + + // Get and Set + void *param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)44444L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + void *long_value = metacall_value_create_long(5555L); + int retcode = metacall_class_static_set(myclass, "b", long_value); + metacall_value_destroy(long_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)5555L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + } + + { + void *obj_value = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(obj_value)); + void *obj = metacall_value_to_object(obj_value); + + static const char john[] = "John Doe"; + + void *return_bye_args[] = { + metacall_value_create_string(john, sizeof(john) - 1) + }; + void *ret = metacallv_object(obj, "return_bye", return_bye_args, sizeof(return_bye_args) / sizeof(return_bye_args[0])); + metacall_value_destroy(return_bye_args[0]); + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + void *return_check_args[] = { + metacall_value_create_long(4L), + metacall_value_create_long(7L) + }; + ret = metacallv_object(obj, "check_args", return_check_args, sizeof(return_check_args) / sizeof(return_check_args[0])); + metacall_value_destroy(return_check_args[0]); + metacall_value_destroy(return_check_args[1]); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((long)15L, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + + metacall_value_destroy(obj_value); + } + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_open_test/CMakeLists.txt b/source/tests/metacall_python_open_test/CMakeLists.txt index 86af075e36..32959b3635 100644 --- a/source/tests/metacall_python_open_test/CMakeLists.txt +++ b/source/tests/metacall_python_open_test/CMakeLists.txt @@ -60,16 +60,17 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # if(NOT OPTION_BUILD_GUIX) - if(MSVC) - set(DEPENDS_OUTPUT_PATH "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - else() - set(DEPENDS_OUTPUT_PATH "${PROJECT_BINARY_DIR}") + find_package(NPM) + + if(NOT NPM_FOUND) + message(SEND_ERROR "NPM not found") + return() endif() add_custom_target(${target}-depends WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND npm --prefix ${CMAKE_CURRENT_BINARY_DIR} install jsonwebtoken - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/node_modules ${DEPENDS_OUTPUT_PATH}/node_modules + COMMAND ${NPM_EXECUTABLE} --prefix ${CMAKE_CURRENT_BINARY_DIR} install jsonwebtoken + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/node_modules ${PROJECT_OUTPUT_DIR}/node_modules ) add_dependencies(${target} ${target}-depends) @@ -105,19 +106,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -139,11 +127,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -152,10 +149,60 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13743) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 py_loader_port_load_from_file_impl /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:190 (libpy_loaderd.so+0x10a02) + # #18 py_loader_port_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:238 (libpy_loaderd.so+0x10cde) + # #19 (libpython3.9.so.1.0+0x10cc73) + # #20 py_loader_impl_load_from_file_relative /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3102 (libpy_loaderd.so+0xb5ae) + # #21 py_loader_impl_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3193 (libpy_loaderd.so+0xb7db) + # #22 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:838 (libmetacalld.so+0x30944) + # #23 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #24 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #25 metacall_python_open_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp:44 (metacall-python-open-testd+0x20d18) + # #26 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-python-open-testd+0x57646) + # #27 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader + cs_loader +) + # # Define test properties # @@ -164,6 +211,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x1500000aa8 pc=0x7f49da2cc0f0 sp=0x7f49d4ad2d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_python_open_test/source/main.cpp b/source/tests/metacall_python_open_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_python_open_test/source/main.cpp +++ b/source/tests/metacall_python_open_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp b/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp index 7173792757..0174eb61f5 100644 --- a/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp +++ b/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,48 +32,46 @@ TEST_F(metacall_python_open_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "landing.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = metacall("index"); + void *ret = metacall("index"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - const char * result = metacall_value_to_string(ret); + const char *result = metacall_value_to_string(ret); - EXPECT_NE((int) 0, (int) strcmp(result, "Error")); + EXPECT_STRNE(result, "Error"); metacall_value_destroy(ret); static const char str[] = "Hello World"; - void * args[] = - { + void *args[] = { metacall_value_create_string(str, sizeof(str) - 1) }; ret = metacallv("login", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - const char * token = metacall_value_to_string(ret); + const char *token = metacall_value_to_string(ret); - EXPECT_EQ((int) 0, (int) strcmp(token, "eyJhbGciOiJIUzI1NiJ9.SGVsbG8gV29ybGQ.Iyc6PWVbK538giVdaInTeIO3jvvC1Vuy_czZUzoRRec")); + EXPECT_STREQ(token, "eyJhbGciOiJIUzI1NiJ9.SGVsbG8gV29ybGQ.Iyc6PWVbK538giVdaInTeIO3jvvC1Vuy_czZUzoRRec"); metacall_value_destroy(args[0]); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -81,13 +79,13 @@ TEST_F(metacall_python_open_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -96,5 +94,5 @@ TEST_F(metacall_python_open_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_pointer_test/CMakeLists.txt b/source/tests/metacall_python_pointer_test/CMakeLists.txt index 32e576f2c2..fe6f4ee616 100644 --- a/source/tests/metacall_python_pointer_test/CMakeLists.txt +++ b/source/tests/metacall_python_pointer_test/CMakeLists.txt @@ -85,7 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -106,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -123,6 +132,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_pointer_test/source/main.cpp b/source/tests/metacall_python_pointer_test/source/main.cpp index 8371fcd178..7832a63230 100644 --- a/source/tests/metacall_python_pointer_test/source/main.cpp +++ b/source/tests/metacall_python_pointer_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading python code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp b/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp index 94deffedff..cd7edd2e2d 100644 --- a/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp +++ b/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading python code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -34,9 +34,9 @@ struct test_type unsigned char r, g, b; }; -void * native_set_value(size_t argc, void * args[], void * data) +void *native_set_value(size_t argc, void *args[], void *data) { - struct test_type * t = (struct test_type *)metacall_value_to_ptr(args[0]); + struct test_type *t = (struct test_type *)metacall_value_to_ptr(args[0]); long value = metacall_value_to_long(args[1]); (void)argc; @@ -54,12 +54,11 @@ void * native_set_value(size_t argc, void * args[], void * data) return metacall_value_create_ptr((void *)t); } -void * native_get_value(size_t argc, void * args[], void * data) +void *native_get_value(size_t argc, void *args[], void *data) { - struct test_type * t = (struct test_type *)metacall_value_to_ptr(args[0]); + struct test_type *t = (struct test_type *)metacall_value_to_ptr(args[0]); - const void * array[] = - { + const void *array[] = { metacall_value_create_char(t->r), metacall_value_create_char(t->g), metacall_value_create_char(t->b) @@ -75,60 +74,91 @@ void * native_get_value(size_t argc, void * args[], void * data) return metacall_value_create_array(array, size); } +void *native_ret_null_ptr(size_t argc, void *args[], void *data) +{ + void *ptr = metacall_value_to_ptr(args[0]); + + EXPECT_EQ((void *)ptr, (void *)NULL); + + (void)argc; + (void)data; + + return metacall_value_create_ptr(NULL); +} + TEST_F(metacall_python_pointer_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); /* Native register */ { metacall_register("native_set_value", native_set_value, NULL, METACALL_PTR, 2, METACALL_PTR, METACALL_LONG); - EXPECT_NE((void *) NULL, (void *) metacall_function("native_set_value")); + EXPECT_NE((void *)NULL, (void *)metacall_function("native_set_value")); metacall_register("native_get_value", native_get_value, NULL, METACALL_ARRAY, 1, METACALL_PTR); - EXPECT_NE((void *) NULL, (void *) metacall_function("native_get_value")); + EXPECT_NE((void *)NULL, (void *)metacall_function("native_get_value")); + + metacall_register("native_ret_null_ptr", native_ret_null_ptr, NULL, METACALL_PTR, 0); + + EXPECT_NE((void *)NULL, (void *)metacall_function("native_ret_null_ptr")); } - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "pointer.py" }; - void * ret = NULL; + void *ret = NULL; struct test_type t = { 0L, 0U, 0U, 0U }; - void * t_ptr = (void *)&t; + void *t_ptr = (void *)&t; long value = 3000L; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - const enum metacall_value_id ids[] = - { + const enum metacall_value_id ids[] = { METACALL_PTR, METACALL_LONG }; ret = metacallt("python_set_value", ids, t_ptr, value); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_PTR, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)t_ptr, (void *)metacall_value_to_ptr(ret)); + + EXPECT_EQ((long)value, (long)t.value); + EXPECT_EQ((unsigned char)10U, (unsigned char)t.r); + EXPECT_EQ((unsigned char)50U, (unsigned char)t.g); + EXPECT_EQ((unsigned char)70U, (unsigned char)t.b); + + metacall_value_destroy(ret); + + void *args[] = { + metacall_value_create_ptr(NULL) + }; + + ret = metacallv("python_ret_null", args); + + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((void *) t_ptr, (void *) metacall_value_cast_ptr(&ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_PTR, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((long) value, (long) t.value); - EXPECT_EQ((unsigned char) 10U, (unsigned char) t.r); - EXPECT_EQ((unsigned char) 50U, (unsigned char) t.g); - EXPECT_EQ((unsigned char) 70U, (unsigned char) t.b); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_ptr(ret)); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_port_callback_test/CMakeLists.txt b/source/tests/metacall_python_port_callback_test/CMakeLists.txt new file mode 100644 index 0000000000..bbf27070a8 --- /dev/null +++ b/source/tests/metacall_python_port_callback_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-callback-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_callback_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + c_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_port_callback_test/source/main.cpp b/source/tests/metacall_python_port_callback_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_callback_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_callback_test/source/metacall_python_port_callback_test.cpp b/source/tests/metacall_python_port_callback_test/source/metacall_python_port_callback_test.cpp new file mode 100644 index 0000000000..c0fd5e5b21 --- /dev/null +++ b/source/tests/metacall_python_port_callback_test/source/metacall_python_port_callback_test.cpp @@ -0,0 +1,71 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_callback_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_callback_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "from metacall import metacall_load_from_file, metacall\n" + "def sum_cb(a: int, b: int) -> int:\n" + " return a + b\n" + "def v_cb():\n" + " print('hello world')\n" + "def str_cb() -> str:\n" + " return 'hello world'\n" + "def test() -> int:\n" + " metacall_load_from_file('c', ['cbks.c'])\n" + " metacall('c_void_callback', v_cb)\n" + " return metacall('c_long_callback', sum_cb)\n"; + + // TODO: + // " s = metacall('c_str_callback', str_cb)\n" + // " print('----------------------')\n" + // " print(s)\n" + // " print('----------------------')\n" + // " return metacall('c_long_callback', sum_cb) + len(s)\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("test"); + + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)7L, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_port_https_test/CMakeLists.txt b/source/tests/metacall_python_port_https_test/CMakeLists.txt new file mode 100644 index 0000000000..1f1335666e --- /dev/null +++ b/source/tests/metacall_python_port_https_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-https-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_https_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + rb_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_port_https_test/source/main.cpp b/source/tests/metacall_python_port_https_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_https_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_https_test/source/metacall_python_port_https_test.cpp b/source/tests/metacall_python_port_https_test/source/metacall_python_port_https_test.cpp new file mode 100644 index 0000000000..e6082d2fe9 --- /dev/null +++ b/source/tests/metacall_python_port_https_test/source/metacall_python_port_https_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_https_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_https_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + // Test import bug (__metacall_import__() missing 1 required positional argument: 'name') + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from http import client\n" + "def fetch_http_py(url: str):\n" + " try:\n" + " conn = client.HTTPSConnection(url, 443)\n" + " conn.request('GET', '/')\n" + " response = conn.getresponse()\n" + " data = response.read()\n" + " conn.close()\n" + " return data\n" + " except Exception as e:\n" + " print(e)\n" + " sys.stdout.flush()\n" + " return b''\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("fetch_http_py", "www.google.com"); + + static const char prefix[] = ""; + + ASSERT_EQ((enum metacall_value_id)METACALL_BUFFER, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((int)0, (int)strncmp((const char *)metacall_value_to_buffer(ret), prefix, sizeof(prefix) - 1)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_port_import_test/CMakeLists.txt b/source/tests/metacall_python_port_import_test/CMakeLists.txt new file mode 100644 index 0000000000..051f059533 --- /dev/null +++ b/source/tests/metacall_python_port_import_test/CMakeLists.txt @@ -0,0 +1,198 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-import-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_import_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port Test path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13823) + # #0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:706 (libtsan.so.2+0x47e82) + # #1 (libdw.so.1+0x56c62) + # #2 backward::TraceResolverImpl::~TraceResolverImpl() /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:3479 (libbacktrace_plugind.so+0xf95c) + # #3 backward::TraceResolver::~TraceResolver() /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:3694 (libbacktrace_plugind.so+0xf95c) + # #4 backward::Printer::~Printer() /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:3986 (libbacktrace_plugind.so+0xf95c) + # #5 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4265 (libbacktrace_plugind.so+0xf95c) + # #6 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #7 (libcoreclr.so+0x4afbdc) + # #8 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4279 (libbacktrace_plugind.so+0xffff) + # #9 (libcoreclr.so+0x4afbdc) + # #10 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #11 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #12 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #13 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #14 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #15 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #16 py_loader_port_load_from_file_impl /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:190 (libpy_loaderd.so+0x10a02) + # #17 py_loader_port_load_from_file_export /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:244 (libpy_loaderd.so+0x10d34) + # #18 (libpython3.9.so.1.0+0x10cc73) + # #19 loader_impl_load_from_memory /usr/local/metacall/source/loader/source/loader_impl.c:968 (libmetacalld.so+0x30eba) + # #20 loader_load_from_memory /usr/local/metacall/source/loader/source/loader.c:327 (libmetacalld.so+0x2e201) + # #21 metacall_load_from_memory /usr/local/metacall/source/metacall/source/metacall.c:357 (libmetacalld.so+0x32c30) + # #22 metacall_python_port_import_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp:48 (metacall-python-port-import-testd+0x20aa5) + # #23 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-python-port-import-testd+0x4e186) + # #24 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/usr/lib/x86_64-linux-gnu/libdw.so.1+0x56c62) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/ramda +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + "LOADER_SCRIPT_PATH=${LOADER_SCRIPT_PATH}/ramda" +) diff --git a/source/tests/metacall_python_port_import_test/source/main.cpp b/source/tests/metacall_python_port_import_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_import_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp b/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp new file mode 100644 index 0000000000..23b93ca1a4 --- /dev/null +++ b/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp @@ -0,0 +1,122 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_import_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_import_test, metacall_node_ramda_case_1) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Case 0 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from asd.mock import two_doubles\n" + "if two_doubles(3.0, 6.0) != 3.1416:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 1 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "import metacall.node.ramda\n" + "if metacall.node.ramda.all(metacall.node.ramda.equals(3))([3, 3, 3, 3]) != True:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 2 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from metacall.node.ramda import equals, all\n" + "if all(equals(3))([3, 3, 3, 3]) != True:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 2 star */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from metacall.node.ramda import *\n" + "if all(equals(3))([3, 3, 3, 3]) != True:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 3 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from metacall.node import ramda, path\n" + "if ramda.all(ramda.equals(3))([3, 3, 3, 3]) != True or path.extname('index.html') != '.html':\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_port_pointer_test/CMakeLists.txt b/source/tests/metacall_python_port_pointer_test/CMakeLists.txt new file mode 100644 index 0000000000..2c4133672e --- /dev/null +++ b/source/tests/metacall_python_port_pointer_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-pointer-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_pointer_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + c_loader + loadtest +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_port_pointer_test/source/main.cpp b/source/tests/metacall_python_port_pointer_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_pointer_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_pointer_test/source/metacall_python_port_pointer_test.cpp b/source/tests/metacall_python_port_pointer_test/source/metacall_python_port_pointer_test.cpp new file mode 100644 index 0000000000..fd6214d1d5 --- /dev/null +++ b/source/tests/metacall_python_port_pointer_test/source/metacall_python_port_pointer_test.cpp @@ -0,0 +1,122 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_pointer_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_pointer_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Test value reference and dereference */ + void *v = metacall_value_create_int(34551); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(v)); + + void *ref = metacall_value_reference(v); + + ASSERT_EQ((enum metacall_value_id)METACALL_PTR, (enum metacall_value_id)metacall_value_id(ref)); + + int *int_ptr = (int *)metacall_value_to_ptr(ref); + + *int_ptr += 10; + + void *result = metacall_value_dereference(ref); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + ASSERT_EQ((int)34561, (int)metacall_value_to_int(result)); + + ASSERT_EQ((void *)v, (void *)result); + + /* Test Python reference and dereference */ + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "from metacall import metacall_load_from_package, metacall, metacall_value_create_ptr, metacall_value_reference, metacall_value_dereference\n" + "metacall_load_from_package('c', 'loadtest')\n" + + "def test_int_ptr() -> int:\n" + " print('Test start')\n" + " sys.stdout.flush()\n" + + " int_val = 324444\n" + " int_val_ref = metacall_value_reference(int_val)\n" + + " print(int_val_ref)\n" + " sys.stdout.flush()\n" + + " metacall('modify_int_ptr', int_val_ref)\n" + " int_val_deref = metacall_value_dereference(int_val_ref)\n" + + " print(int_val, '!=', int_val_deref)\n" + " sys.stdout.flush()\n" + + " return int_val_deref\n" + + "def test_struct_ptr() -> float:\n" + " print('Test start')\n" + " sys.stdout.flush()\n" + + " list_pair = metacall_value_create_ptr(None)\n" + " list_pair_ref = metacall_value_reference(list_pair)\n" + " result = metacall('pair_list_init', list_pair_ref)\n" + " print(result)\n" + " sys.stdout.flush()\n" + + " list_pair = metacall_value_dereference(list_pair_ref)\n" + " result = metacall('pair_list_value', list_pair, 2)\n" + " print(result)\n" + " sys.stdout.flush()\n" + + " metacall('pair_list_destroy', list_pair)\n" + + " return result\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("test_int_ptr"); + + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)111L, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + ret = metacall("test_struct_ptr"); + + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((double)2.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + + metacall_destroy(); +} diff --git a/source/tests/metacall_callback_test/CMakeLists.txt b/source/tests/metacall_python_port_test/CMakeLists.txt similarity index 66% rename from source/tests/metacall_callback_test/CMakeLists.txt rename to source/tests/metacall_python_port_test/CMakeLists.txt index 97567d2d9c..124502f07b 100644 --- a/source/tests/metacall_callback_test/CMakeLists.txt +++ b/source/tests/metacall_python_port_test/CMakeLists.txt @@ -1,16 +1,14 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_RB OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + # # Executable name and options # -# Check if loaders, scripts and ports are enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE - OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_NODE - OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_PORTS_NODE) - return() -endif() - # Target name -set(target metacall-callback-test) +set(target metacall-python-port-test) message(STATUS "Test ${target}") # @@ -34,7 +32,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/metacall_callback_test.cpp + ${source_path}/metacall_python_port_test.cpp ) # Group source files @@ -87,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -111,6 +95,9 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port Test path + METACALL_PYTHON_PORT_TEST_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port/test.py" ) # @@ -122,11 +109,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -139,12 +135,30 @@ add_test(NAME ${target} COMMAND $ ) +# Enable rust test if it is built +if(OPTION_BUILD_LOADERS_RS) + set(RS_DEPENDENCY rs_loader) + set(TESTS_ENVIRONMENT_VARIABLES_RS "OPTION_BUILD_LOADERS_RS=1") +endif() + +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader + py_loader + rb_loader + node_loader + ${RS_DEPENDENCY} +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) @@ -152,4 +166,5 @@ include(TestEnvironmentVariables) test_environment_variables(${target} "" ${TESTS_ENVIRONMENT_VARIABLES} + ${TESTS_ENVIRONMENT_VARIABLES_RS} ) diff --git a/source/tests/metacall_python_port_test/source/main.cpp b/source/tests/metacall_python_port_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_test/source/metacall_python_port_test.cpp b/source/tests/metacall_python_port_test/source/metacall_python_port_test.cpp new file mode 100644 index 0000000000..7ebb7c236e --- /dev/null +++ b/source/tests/metacall_python_port_test/source/metacall_python_port_test.cpp @@ -0,0 +1,56 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char *py_scripts[] = { + METACALL_PYTHON_PORT_TEST_PATH + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + void *ret = metacallv("main", metacall_null_args); + + EXPECT_STREQ(metacall_value_to_string(ret), "Tests passed without errors"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_reentrant_test/CMakeLists.txt b/source/tests/metacall_python_reentrant_test/CMakeLists.txt index 04027c1b04..ab29592ef2 100644 --- a/source/tests/metacall_python_reentrant_test/CMakeLists.txt +++ b/source/tests/metacall_python_reentrant_test/CMakeLists.txt @@ -85,7 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -94,7 +94,7 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} + ${DEFAULT_COMPILE_DEFINITIONS} # Python Port path METACALL_PYTHON_REENTRANT_TEST_PY_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" @@ -110,11 +110,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -127,6 +136,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_reentrant_test/source/main.cpp b/source/tests/metacall_python_reentrant_test/source/main.cpp index 8371fcd178..7832a63230 100644 --- a/source/tests/metacall_python_reentrant_test/source/main.cpp +++ b/source/tests/metacall_python_reentrant_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading python code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_reentrant_test/source/metacall_python_reentrant_test.cpp b/source/tests/metacall_python_reentrant_test/source/metacall_python_reentrant_test.cpp index 6fe99f279b..ae1d107591 100644 --- a/source/tests/metacall_python_reentrant_test/source/metacall_python_reentrant_test.cpp +++ b/source/tests/metacall_python_reentrant_test/source/metacall_python_reentrant_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,29 +18,29 @@ * */ -#include +#include #include -#include #include +#include #ifndef METACALL_PYTHON_REENTRANT_TEST_PY_PORT_PATH -# error "The path to the Python port is not defined" + #error "The path to the Python port is not defined" #endif -class metacall_node_reentrant_test : public testing::Test +class metacall_python_reentrant_test : public testing::Test { public: }; -TEST_F(metacall_node_reentrant_test, DefaultConstructor) +TEST_F(metacall_python_reentrant_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char buffer[] = "import sys\n" @@ -51,9 +51,9 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) static const char tag[] = "py"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -61,13 +61,13 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -76,5 +76,5 @@ TEST_F(metacall_node_reentrant_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_relative_path_test/CMakeLists.txt b/source/tests/metacall_python_relative_path_test/CMakeLists.txt new file mode 100644 index 0000000000..22cae4f281 --- /dev/null +++ b/source/tests/metacall_python_relative_path_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-relative-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_relative_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_relative_path_test/source/main.cpp b/source/tests/metacall_python_relative_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_relative_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_relative_path_test/source/metacall_python_relative_path_test.cpp b/source/tests/metacall_python_relative_path_test/source/metacall_python_relative_path_test.cpp new file mode 100644 index 0000000000..0c44179d68 --- /dev/null +++ b/source/tests/metacall_python_relative_path_test/source/metacall_python_relative_path_test.cpp @@ -0,0 +1,76 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_dict_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_dict_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "./s2.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char *py_scripts_fail[] = { + "./yeet/fail/this-script-is-impossible-that-exists.py" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_test/CMakeLists.txt b/source/tests/metacall_python_test/CMakeLists.txt new file mode 100644 index 0000000000..6f89617e34 --- /dev/null +++ b/source/tests/metacall_python_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + PY_LOADER_TEST_SCRIPT_ABSOLUTE_PATH="${LOADER_SCRIPT_PATH}/dicty.py" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_test/source/main.cpp b/source/tests/metacall_python_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_test/source/metacall_python_test.cpp b/source/tests/metacall_python_test/source/metacall_python_test.cpp new file mode 100644 index 0000000000..690bf226af --- /dev/null +++ b/source/tests/metacall_python_test/source/metacall_python_test.cpp @@ -0,0 +1,81 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_python_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "example.py", // Classic load + "helloworld.py", // Classic load + "json", // Module load + "os.path", // Submodule load + PY_LOADER_TEST_SCRIPT_ABSOLUTE_PATH, // Absolute load + "./s2.py" // Relative load + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char *py_scripts_fail[] = { + "thismoduledoesnotexistaaaa", // Non-existent Module load + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_varargs_test/CMakeLists.txt b/source/tests/metacall_python_varargs_test/CMakeLists.txt new file mode 100644 index 0000000000..f8636d21e2 --- /dev/null +++ b/source/tests/metacall_python_varargs_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) +return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-varargs-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_varargs_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_varargs_test/source/main.cpp b/source/tests/metacall_python_varargs_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_varargs_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_varargs_test/source/metacall_python_varargs_test.cpp b/source/tests/metacall_python_varargs_test/source/metacall_python_varargs_test.cpp new file mode 100644 index 0000000000..e640a1f50f --- /dev/null +++ b/source/tests/metacall_python_varargs_test/source/metacall_python_varargs_test.cpp @@ -0,0 +1,106 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_varargs_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_varargs_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char python_script[] = + "#!/usr/bin/env python3\n" + "values = [10, 20, 30]\n" + "def varargs(*args):\n" + " for (v, a) in zip(values, args):\n" + " print(v, ' == ', a);\n" + " if v != a:\n" + " return 324;\n" + " return 20;\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", python_script, sizeof(python_script), NULL)); + + void *args2[] = { + metacall_value_create_long(10), + metacall_value_create_long(20) + }; + + void *args3[] = { + metacall_value_create_long(10), + metacall_value_create_long(20), + metacall_value_create_long(30) + }; + + void *ret = metacallv_s("varargs", args2, 2); + + ASSERT_EQ((long)20, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + ret = metacallv_s("varargs", args3, 3); + + ASSERT_EQ((long)20, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + metacall_value_destroy(args2[0]); + metacall_value_destroy(args2[1]); + + metacall_value_destroy(args3[0]); + metacall_value_destroy(args3[1]); + metacall_value_destroy(args3[2]); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt b/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt new file mode 100644 index 0000000000..3aca8756e3 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt @@ -0,0 +1,169 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-without-env-vars-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_without_env_vars_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test leaks and I am not sure if it is a false positive or not: + # + # Direct leak of 18682 byte(s) in 12 object(s) allocated from: + # #0 0x7fa978386bd7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fa9734ddbb1 in _PyMem_RawMalloc ../Objects/obmalloc.c:101 + + # Indirect leak of 2775 byte(s) in 2 object(s) allocated from: + # #0 0x7fa978386bd7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fa9734ddbb1 in _PyMem_RawMalloc ../Objects/obmalloc.c:101 + + # SUMMARY: AddressSanitizer: 21457 byte(s) leaked in 14 allocation(s). + # + # Valgrind does not show anything: + # valgrind --tool=memcheck --leak-check=full --show-leak-kinds=possibly --track-origins=yes --num-callers=500 --suppressions=../source/tests/memcheck/valgrind-python.supp ./metacall-python-without-env-vars-testd + # For checking all the leaks (and including false positives), check: + # valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes --num-callers=500 --suppressions=../source/tests/memcheck/valgrind-python.supp ./metacall-python-without-env-vars-testd + return() +endif() + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/metacall_python_without_env_vars_test/source/main.cpp b/source/tests/metacall_python_without_env_vars_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp b/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp new file mode 100644 index 0000000000..05ec480890 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp @@ -0,0 +1,49 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_without_env_vars_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_python_without_env_vars_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts_fail[] = { + "thismoduledoesnotexistaaaa", // Non-existent Module load + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_without_functions_test/CMakeLists.txt b/source/tests/metacall_python_without_functions_test/CMakeLists.txt new file mode 100644 index 0000000000..e2092380eb --- /dev/null +++ b/source/tests/metacall_python_without_functions_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) +return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-without-functions-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_without_functions_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_without_functions_test/source/main.cpp b/source/tests/metacall_python_without_functions_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_without_functions_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_without_functions_test/source/metacall_python_without_functions_test.cpp b/source/tests/metacall_python_without_functions_test/source/metacall_python_without_functions_test.cpp new file mode 100644 index 0000000000..9e84dbe510 --- /dev/null +++ b/source/tests/metacall_python_without_functions_test/source/metacall_python_without_functions_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_without_functions_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_without_functions_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "withoutfunctions.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_reinitialize_test/CMakeLists.txt b/source/tests/metacall_reinitialize_test/CMakeLists.txt index 132303f811..c1adf593e3 100644 --- a/source/tests/metacall_reinitialize_test/CMakeLists.txt +++ b/source/tests/metacall_reinitialize_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_reinitialize_test/source/main.cpp b/source/tests/metacall_reinitialize_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_reinitialize_test/source/main.cpp +++ b/source/tests/metacall_reinitialize_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp b/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp index 57b29d8eda..da47af9905 100644 --- a/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp +++ b/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,15 @@ * */ -#include +#include #include -#include #include +#include -void * c_function(void * args[]) +void *c_function(void *args[]) { - printf("%s\n", (char*)args[0]); + printf("%s\n", (char *)args[0]); return metacall_value_create_int(1); } @@ -48,20 +48,19 @@ TEST_F(metacall_reinitialize_test, DefaultConstructor) for (size_t iterator = 0; iterator < initialize_count; ++iterator) { - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } } diff --git a/source/tests/metacall_reload_functions_test/CMakeLists.txt b/source/tests/metacall_reload_functions_test/CMakeLists.txt new file mode 100644 index 0000000000..8d7682d2ce --- /dev/null +++ b/source/tests/metacall_reload_functions_test/CMakeLists.txt @@ -0,0 +1,158 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-reload-functions-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_reload_functions_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_reload_functions_test/source/main.cpp b/source/tests/metacall_reload_functions_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_reload_functions_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_reload_functions_test/source/metacall_reload_functions_test.cpp b/source/tests/metacall_reload_functions_test/source/metacall_reload_functions_test.cpp new file mode 100644 index 0000000000..be227cf3d4 --- /dev/null +++ b/source/tests/metacall_reload_functions_test/source/metacall_reload_functions_test.cpp @@ -0,0 +1,174 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_reload_functions_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_reload_functions_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char buffer0[] = + "def f():\n" + " return 5\n"; + + void *handle0 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer0, sizeof(buffer0), &handle0)); + + void *ret = metacallhv_s(handle0, "f", metacall_null_args, 0); + + EXPECT_EQ((long)5, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle0)); + + const char buffer1[] = + "def f():\n" + " return 6\n"; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer1, sizeof(buffer1), &handle1)); + + ret = metacallhv_s(handle1, "f", metacall_null_args, 0); + + EXPECT_EQ((long)6, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle1)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char buffer0[] = + "def f()\n" + " return 5\n" + "end\n"; + + void *handle0 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer0, sizeof(buffer0), &handle0)); + + void *ret = metacallhv_s(handle0, "f", metacall_null_args, 0); + + EXPECT_EQ((int)5, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle0)); + + const char buffer1[] = + "def f()\n" + " return 6\n" + "end\n"; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer1, sizeof(buffer1), &handle1)); + + ret = metacallhv_s(handle1, "f", metacall_null_args, 0); + + EXPECT_EQ((int)6, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle1)); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer0[] = + "module.exports = {\n" + " f: () => 5,\n" + "};\n"; + + void *handle0 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer0, sizeof(buffer0), &handle0)); + + void *ret = metacallhv_s(handle0, "f", metacall_null_args, 0); + + EXPECT_EQ((double)5.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle0)); + + const char buffer1[] = + "module.exports = {\n" + " f: () => 6,\n" + "};\n"; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer1, sizeof(buffer1), &handle1)); + + ret = metacallhv_s(handle1, "f", metacall_null_args, 0); + + EXPECT_EQ((double)6.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle1)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_return_monad_test/CMakeLists.txt b/source/tests/metacall_return_monad_test/CMakeLists.txt index 15c8e3f21d..bcf5ad5c57 100644 --- a/source/tests/metacall_return_monad_test/CMakeLists.txt +++ b/source/tests/metacall_return_monad_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_return_monad_test/source/main.cpp b/source/tests/metacall_return_monad_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_return_monad_test/source/main.cpp +++ b/source/tests/metacall_return_monad_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp b/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp index 737a47a254..5194d3ab86 100644 --- a/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp +++ b/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,15 @@ * */ -#include +#include #include -#include #include +#include -void * c_function(void * args[]) +void *c_function(void *args[]) { - printf("%s\n", (char*)args[0]); + printf("%s\n", (char *)args[0]); return metacall_value_create_int(1); } @@ -42,12 +42,12 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) metacall_log_stdio_type log_stdio = { stdout }; - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char buffer[] = "#!/usr/bin/env python3\n" @@ -62,23 +62,23 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - void * ret = NULL; + void *ret = NULL; size_t size = 0; - char * value_str = NULL; + char *value_str = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); ret = metacall("monad", 1); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_STRING, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((int) 0, (int) strcmp("asd", metacall_value_to_string(ret))); + EXPECT_STREQ("asd", metacall_value_to_string(ret)); value_str = metacall_serialize(metacall_serial(), ret, &size, allocator); @@ -90,11 +90,11 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) ret = metacall("monad", 0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_BUFFER, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_BUFFER, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((int) 0, (int) memcmp(metacall_value_to_buffer(ret), "asd", metacall_value_size(ret))); + EXPECT_EQ((int)0, (int)memcmp(metacall_value_to_buffer(ret), "asd", metacall_value_size(ret))); value_str = metacall_serialize(metacall_serial(), ret, &size, allocator); @@ -106,7 +106,7 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -114,13 +114,13 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -129,5 +129,5 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_rpc_test/CMakeLists.txt b/source/tests/metacall_rpc_test/CMakeLists.txt new file mode 100644 index 0000000000..04925f1f1c --- /dev/null +++ b/source/tests/metacall_rpc_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RPC OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RPC) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rpc-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rpc_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define dependencies +# + +add_dependencies(${target} + rpc_loader +) + +# +# Define test +# + +set(NodeJS_EXECUTABLE_ONLY ON) + +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(STATUS "NodeJS executable not found, skipping RPC loader test") + return() +endif() + +add_test(NAME ${target} + COMMAND ${NodeJS_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/source/test.js ${CMAKE_CURRENT_SOURCE_DIR}/source/server.js $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_rpc_test/source/main.cpp b/source/tests/metacall_rpc_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_rpc_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rpc_test/source/metacall_rpc_test.cpp b/source/tests/metacall_rpc_test/source/metacall_rpc_test.cpp new file mode 100644 index 0000000000..670911293b --- /dev/null +++ b/source/tests/metacall_rpc_test/source/metacall_rpc_test.cpp @@ -0,0 +1,91 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_rpc_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_rpc_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* RPC */ +#if defined(OPTION_BUILD_LOADERS_RPC) + { + const char *rpc_scripts[] = { + "remote.url" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rpc", rpc_scripts, sizeof(rpc_scripts) / sizeof(rpc_scripts[0]), &handle)); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + const enum metacall_value_id divide_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + void *ret = metacallht_s(handle, "divide", divide_ids, 2, 50.0f, 10.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)5.0f); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + + static const char buffer[] = "http://localhost:6094/viferga/example/v1"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rpc", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_RPC */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_rpc_test/source/server.js b/source/tests/metacall_rpc_test/source/server.js new file mode 100644 index 0000000000..f3c7144e36 --- /dev/null +++ b/source/tests/metacall_rpc_test/source/server.js @@ -0,0 +1,60 @@ +const http = require('http'); +const port = 6094; + +const server = http.createServer((req, res) => { + req.on('error', err => { + console.error(err); + process.exit(1); + }); + + res.on('error', err => { + console.error(err); + process.exit(1); + }); + + const data = new Promise((resolve) => { + let body = []; + + req.on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + resolve(Buffer.concat(body).toString()); + }); + }); + + if (req.method === 'GET') { + if (req.url === '/ready') { + res.end('OK'); + return; + } else if (req.url === '/viferga/example/v1/inspect') { + const inspect = '{"py":[{"name":"example.py","scope":{"name":"global_namespace","funcs":[{"name":"divide","signature":{"ret":{"type":{"name":"float","id":6}},"args":[{"name":"left","type":{"name":"float","id":6}},{"name":"right","type":{"name":"float","id":6}}]},"async":false},{"name":"hello","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false},{"name":"return_same_array","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"arr","type":{"name":"","id":18}}]},"async":false},{"name":"bytebuff","signature":{"ret":{"type":{"name":"bytes","id":8}},"args":[{"name":"input","type":{"name":"bytes","id":8}}]},"async":false},{"name":"dont_load_this_function","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"","id":18}},{"name":"right","type":{"name":"","id":18}}]},"async":false},{"name":"sum","signature":{"ret":{"type":{"name":"int","id":4}},"args":[{"name":"left","type":{"name":"int","id":4}},{"name":"right","type":{"name":"int","id":4}}]},"async":false},{"name":"strcat","signature":{"ret":{"type":{"name":"str","id":7}},"args":[{"name":"left","type":{"name":"str","id":7}},{"name":"right","type":{"name":"str","id":7}}]},"async":false},{"name":"return_array","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false},{"name":"multiply","signature":{"ret":{"type":{"name":"int","id":4}},"args":[{"name":"left","type":{"name":"int","id":4}},{"name":"right","type":{"name":"int","id":4}}]},"async":false}],"classes":[],"objects":[]}}],"rb":[{"name":"hello.rb","scope":{"name":"global_namespace","funcs":[{"name":"say_multiply","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"Fixnum","id":3}},{"name":"right","type":{"name":"Fixnum","id":3}}]},"async":false},{"name":"get_second","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"first","type":{"name":"Fixnum","id":3}},{"name":"second","type":{"name":"Fixnum","id":3}}]},"async":false},{"name":"say_null","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false},{"name":"say_hello","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"value","type":{"name":"String","id":7}}]},"async":false},{"name":"backwardsPrime","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"start","type":{"name":"","id":18}},{"name":"stop","type":{"name":"","id":18}}]},"async":false},{"name":"get_second_untyped","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"first","type":{"name":"","id":18}},{"name":"second","type":{"name":"","id":18}}]},"async":false},{"name":"say_sum_ducktyped","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"","id":18}},{"name":"right","type":{"name":"","id":18}}]},"async":false},{"name":"say_string_without_spaces","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"value","type":{"name":"String","id":7}}]},"async":false},{"name":"say_multiply_ducktyped","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"","id":18}},{"name":"right","type":{"name":"","id":18}}]},"async":false}],"classes":[],"objects":[]}}],"cs":[{"name":"hello.cs","scope":{"name":"global_namespace","funcs":[{"name":"Sum","signature":{"ret":{"type":{"name":"int","id":3}},"args":[{"name":"a","type":{"name":"int","id":3}},{"name":"b","type":{"name":"int","id":3}}]},"async":false},{"name":"Say","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"text","type":{"name":"string","id":7}}]},"async":false},{"name":"Concat","signature":{"ret":{"type":{"name":"string","id":7}},"args":[{"name":"a","type":{"name":"string","id":7}},{"name":"b","type":{"name":"string","id":7}}]},"async":false},{"name":"SayHello","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false}],"classes":[],"objects":[]}}],"__metacall_host__":[],"mock":[{"name":"empty.mock","scope":{"name":"global_namespace","funcs":[{"name":"three_str","signature":{"ret":{"type":{"name":"String","id":7}},"args":[{"name":"a_str","type":{"name":"String","id":7}},{"name":"b_str","type":{"name":"String","id":7}},{"name":"c_str","type":{"name":"String","id":7}}]},"async":false},{"name":"my_empty_func_str","signature":{"ret":{"type":{"name":"String","id":7}},"args":[]},"async":false},{"name":"my_empty_func_int","signature":{"ret":{"type":{"name":"Integer","id":3}},"args":[]},"async":false},{"name":"new_args","signature":{"ret":{"type":{"name":"String","id":7}},"args":[{"name":"a_str","type":{"name":"String","id":7}}]},"async":false},{"name":"two_str","signature":{"ret":{"type":{"name":"String","id":7}},"args":[{"name":"a_str","type":{"name":"String","id":7}},{"name":"b_str","type":{"name":"String","id":7}}]},"async":false},{"name":"two_doubles","signature":{"ret":{"type":{"name":"Double","id":6}},"args":[{"name":"first_parameter","type":{"name":"Double","id":6}},{"name":"second_parameter","type":{"name":"Double","id":6}}]},"async":false},{"name":"my_empty_func","signature":{"ret":{"type":{"name":"Integer","id":3}},"args":[]},"async":false},{"name":"mixed_args","signature":{"ret":{"type":{"name":"Char","id":1}},"args":[{"name":"a_char","type":{"name":"Char","id":1}},{"name":"b_int","type":{"name":"Integer","id":3}},{"name":"c_long","type":{"name":"Long","id":4}},{"name":"d_double","type":{"name":"Double","id":6}},{"name":"e_ptr","type":{"name":"Ptr","id":11}}]},"async":false}],"classes":[],"objects":[]}}]}'; + res.setHeader('Content-Type', 'application/json'); + res.end(inspect); + return; + } + } else if (req.method === 'POST') { + if (req.url === '/viferga/example/v1/call/divide') { + data.then((body) => { + console.log('¡Call recieved!'); + if (body !== '[50.0,10.0]') { + console.error('Invalid body:', body); + process.exit(1); + } + const result = '5.0'; + res.setHeader('Content-Type', 'application/json'); + res.end(result); + setTimeout(() => { + process.exit(0); + }, 1000); + }); + return; + } + } + + console.error('Invalid request method or url:', req.method, req.url); + process.exit(1); +}); + +server.listen(port, () => { + console.log(`MetaCall server listening at ${port}`); +}); diff --git a/source/tests/metacall_rpc_test/source/test.js b/source/tests/metacall_rpc_test/source/test.js new file mode 100644 index 0000000000..15d5a9dac5 --- /dev/null +++ b/source/tests/metacall_rpc_test/source/test.js @@ -0,0 +1,86 @@ +const { spawn } = require('child_process'); +const http = require('http'); + +// Start mock server +const server = spawn(process.argv[0], [process.argv[2]]); + +server.stdout.pipe(process.stdout); +server.stderr.pipe(process.stderr); + +server.on('exit', (code) => { + if (code !== 0) { + process.exit(code); + } +}); + +// Check if server is ready +function isReady() { + return new Promise((resolve, reject) => { + const options = { + host: 'localhost', + port: 6094, + path: '/ready', + }; + + const callback = (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + resolve(data === 'OK'); + }); + + res.on('error', reject); + }; + try { + const req = http.request(options, callback); + req.on('error', reject); + req.end(); + } catch (e) { + reject(e); + } + }); +} + +// Catch unhandled exceptions +function killTest(error) { + server.kill('SIGINT'); + console.error(error); + process.exit(1); +} + +process.on('uncaughtException', killTest); + +// Wait server to be ready and execute the test +(async function run() { + let ready = false; + + setTimeout(() => { + if (ready === false) { + killTest('Timeout reached, server is not ready'); + } + }, 60000); + + while (ready !== true) { + try { + ready = await isReady(); + } catch (e) { } + } + + console.log('Starting the test'); + + const test = spawn(process.argv[3]); + + test.stdout.pipe(process.stdout); + test.stderr.pipe(process.stderr); + + test.on('exit', (code) => { + if (code !== 0) { + killTest(`Error: Test exited with code ${code}`); + } + process.exit(0); + }); +})(); diff --git a/source/tests/rb_loader_test/CMakeLists.txt b/source/tests/metacall_ruby_fail_empty_test/CMakeLists.txt similarity index 85% rename from source/tests/rb_loader_test/CMakeLists.txt rename to source/tests/metacall_ruby_fail_empty_test/CMakeLists.txt index f3eba4db23..a480292e1c 100644 --- a/source/tests/rb_loader_test/CMakeLists.txt +++ b/source/tests/metacall_ruby_fail_empty_test/CMakeLists.txt @@ -8,7 +8,7 @@ endif() # # Target name -set(target rb-loader-test) +set(target metacall-ruby-fail-empty-test) message(STATUS "Test ${target}") # @@ -32,7 +32,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/rb_loader_test.cpp + ${source_path}/metacall_ruby_fail_empty_test.cpp ) # Group source files @@ -85,13 +85,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::configuration + ${META_PROJECT_NAME}::metacall ) # @@ -112,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -129,6 +132,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + # # Define test properties # diff --git a/source/tests/metacall_ruby_fail_empty_test/source/main.cpp b/source/tests/metacall_ruby_fail_empty_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_fail_empty_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_fail_empty_test/source/metacall_ruby_fail_empty_test.cpp b/source/tests/metacall_ruby_fail_empty_test/source/metacall_ruby_fail_empty_test.cpp new file mode 100644 index 0000000000..5ab21e63e2 --- /dev/null +++ b/source/tests/metacall_ruby_fail_empty_test/source/metacall_ruby_fail_empty_test.cpp @@ -0,0 +1,73 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_ruby_fail_empty_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_fail_empty_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* File */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *scripts[] = { + "failempty.rb" + }; + + const size_t size = sizeof(scripts) / sizeof(scripts[0]); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", scripts, size, NULL)); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_ruby_fail_test/CMakeLists.txt b/source/tests/metacall_ruby_fail_test/CMakeLists.txt index 2a0605a1c1..a34d4318cd 100644 --- a/source/tests/metacall_ruby_fail_test/CMakeLists.txt +++ b/source/tests/metacall_ruby_fail_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + # # Executable name and options # @@ -80,20 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_ruby_fail_test/source/main.cpp b/source/tests/metacall_ruby_fail_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_ruby_fail_test/source/main.cpp +++ b/source/tests/metacall_ruby_fail_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_ruby_fail_test/source/metacall_ruby_fail_test.cpp b/source/tests/metacall_ruby_fail_test/source/metacall_ruby_fail_test.cpp index 7eb6d914b6..24c11d7981 100644 --- a/source/tests/metacall_ruby_fail_test/source/metacall_ruby_fail_test.cpp +++ b/source/tests/metacall_ruby_fail_test/source/metacall_ruby_fail_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,38 +18,37 @@ * */ -#include +#include #include -#include #include +#include -class metacall_test : public testing::Test +class metacall_ruby_fail_test : public testing::Test { public: }; -TEST_F(metacall_test, DefaultConstructor) +TEST_F(metacall_ruby_fail_test, DefaultConstructor) { metacall_print_info(); metacall_log_stdio_type log_stdio = { stdout }; - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "invalid.rb" }; - EXPECT_EQ((int) 1, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)1, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_ruby_object_class_test/CMakeLists.txt b/source/tests/metacall_ruby_object_class_test/CMakeLists.txt new file mode 100644 index 0000000000..c4d2ad9b38 --- /dev/null +++ b/source/tests/metacall_ruby_object_class_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-object-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_object_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_object_class_test/source/main.cpp b/source/tests/metacall_ruby_object_class_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_object_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_object_class_test/source/metacall_ruby_object_class_test.cpp b/source/tests/metacall_ruby_object_class_test/source/metacall_ruby_object_class_test.cpp new file mode 100644 index 0000000000..05d7f272cb --- /dev/null +++ b/source/tests/metacall_ruby_object_class_test/source/metacall_ruby_object_class_test.cpp @@ -0,0 +1,164 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_ruby_object_class_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_object_class_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "klass.rb" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + { + void *ret = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(ret)); + void *cls = metacall_value_to_class(ret); + + void *static_var = metacall_class_static_get(cls, "@@class_hierarchy_var"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(static_var)); + metacall_value_destroy(static_var); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(ret)); + void *obj = metacall_value_to_object(ret); + + void *v_int = metacall_value_create_int(1234); + int retcode = metacall_object_set(obj, "@intAttribute", v_int); + ASSERT_EQ((int)0, (int)retcode); + metacall_value_destroy(v_int); + + void *intAttribute = metacall_object_get(obj, "@intAttribute"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(intAttribute)); + ASSERT_EQ((int)1234, (int)metacall_value_to_int(intAttribute)); + metacall_value_destroy(intAttribute); + + void *param3 = metacall_object_get(obj, "@param3"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param3)); + ASSERT_EQ((int)777, (int)metacall_value_to_int(param3)); + metacall_value_destroy(param3); + + metacall_value_destroy(ret); + } + + { + void *myclass = metacall_class("MyClass"); + ASSERT_NE((void *)NULL, (void *)myclass); + + static const char works[] = "It works!"; + + void *static_method_args[] = { + metacall_value_create_string(works, sizeof(works) - 1) + }; + void *ret_value = metacallv_class(myclass, "static_hello", static_method_args, sizeof(static_method_args) / sizeof(static_method_args[0])); + metacall_value_destroy(static_method_args[0]); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret_value)); + metacall_value_destroy(ret_value); + } + + { + void *obj_value = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(obj_value)); + void *obj = metacall_value_to_object(obj_value); + + static const char world[] = "world"; + + void *return_bye_args[] = { + metacall_value_create_string(world, sizeof(world) - 1) + }; + void *ret = metacallv_object(obj, "return_bye", return_bye_args, sizeof(return_bye_args) / sizeof(return_bye_args[0])); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + metacall_value_destroy(obj_value); + metacall_value_destroy(return_bye_args[0]); + } + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *myclass = metacall_value_to_class(myclass_value); + + static const char john[] = "John Doe"; + + void *constructor_params[] = { + metacall_value_create_string(john, sizeof(john) - 1), // param1 + metacall_value_create_int(999999) // param2 + }; + void *new_object_v = metacall_class_new(myclass, "objectname", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + metacall_value_destroy(constructor_params[1]); + void *new_object = metacall_value_to_object(new_object_v); + + void *param2 = metacall_object_get(new_object, "@param2"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)999999, (int)metacall_value_to_int(param2)); + + metacall_value_destroy(param2); + metacall_value_destroy(myclass_value); + metacall_value_destroy(new_object_v); + } + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_ruby_parser_integration_test/CMakeLists.txt b/source/tests/metacall_ruby_parser_integration_test/CMakeLists.txt new file mode 100644 index 0000000000..ecd892ffb0 --- /dev/null +++ b/source/tests/metacall_ruby_parser_integration_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-parser-integration-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_parser_integration_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_parser_integration_test/source/main.cpp b/source/tests/metacall_ruby_parser_integration_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_parser_integration_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_parser_integration_test/source/metacall_ruby_parser_integration_test.cpp b/source/tests/metacall_ruby_parser_integration_test/source/metacall_ruby_parser_integration_test.cpp new file mode 100644 index 0000000000..0e38e65ac9 --- /dev/null +++ b/source/tests/metacall_ruby_parser_integration_test/source/metacall_ruby_parser_integration_test.cpp @@ -0,0 +1,84 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_ruby_parser_integration_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_parser_integration_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "cache.rb" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + ret = metacall("cache_initialize"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); + + ret = metacall("cache_set", "meta", "call"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); + + ret = metacall("cache_has_key", "meta"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_NE((int)0, (int)metacall_value_to_bool(ret)); + + metacall_value_destroy(ret); + + ret = metacall("cache_get", "meta"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "call"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + metacall_destroy(); +} diff --git a/source/tests/rb_rails_integration_test/CMakeLists.txt b/source/tests/metacall_ruby_rails_integration_test/CMakeLists.txt similarity index 81% rename from source/tests/rb_rails_integration_test/CMakeLists.txt rename to source/tests/metacall_ruby_rails_integration_test/CMakeLists.txt index d69777c116..6559f627a6 100644 --- a/source/tests/rb_rails_integration_test/CMakeLists.txt +++ b/source/tests/metacall_ruby_rails_integration_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if ruby loader and distributable libs are enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB OR NOT OPTION_BUILD_DIST_LIBS) +# Check if ruby loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) return() endif() @@ -12,7 +8,7 @@ endif() # # Target name -set(target rb-rails-integration-test) +set(target metacall-ruby-rails-integration-test) message(STATUS "Test ${target}") # @@ -36,7 +32,7 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp - ${source_path}/rb_rails_integration_test.cpp + ${source_path}/metacall_ruby_rails_integration_test.cpp ) # Group source files @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -139,6 +141,14 @@ add_test(NAME ${target} WORKING_DIRECTORY ${LOADER_SCRIPT_PATH} ) +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + # # Define test properties # diff --git a/source/tests/metacall_ruby_rails_integration_test/source/main.cpp b/source/tests/metacall_ruby_rails_integration_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_rails_integration_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/rb_rails_integration_test/source/rb_rails_integration_test.cpp b/source/tests/metacall_ruby_rails_integration_test/source/metacall_ruby_rails_integration_test.cpp similarity index 56% rename from source/tests/rb_rails_integration_test/source/rb_rails_integration_test.cpp rename to source/tests/metacall_ruby_rails_integration_test/source/metacall_ruby_rails_integration_test.cpp index 2548e5e0f1..5d04ac4351 100644 --- a/source/tests/rb_rails_integration_test/source/rb_rails_integration_test.cpp +++ b/source/tests/metacall_ruby_rails_integration_test/source/metacall_ruby_rails_integration_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,47 +18,46 @@ * */ -#include +#include #include #include -class rb_rails_integration_test : public testing::Test +class metacall_ruby_integration_test : public testing::Test { public: }; -TEST_F(rb_rails_integration_test, DefaultConstructor) +TEST_F(metacall_ruby_integration_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "blog.rb" }; const char func_run[] = "run_and_kill_server"; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); - EXPECT_NE((void *) NULL, (void *) metacall_function(func_run)); + EXPECT_NE((void *)NULL, (void *)metacall_function(func_run)); ret = metacall(func_run); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) metacall_value_to_int(ret)); + EXPECT_EQ((int)0, (int)metacall_value_to_int(ret)); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_ruby_test/CMakeLists.txt b/source/tests/metacall_ruby_test/CMakeLists.txt new file mode 100644 index 0000000000..163c170a64 --- /dev/null +++ b/source/tests/metacall_ruby_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_test/source/main.cpp b/source/tests/metacall_ruby_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_ruby_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_test/source/metacall_ruby_test.cpp b/source/tests/metacall_ruby_test/source/metacall_ruby_test.cpp new file mode 100644 index 0000000000..cf8bcf49ea --- /dev/null +++ b/source/tests/metacall_ruby_test/source/metacall_ruby_test.cpp @@ -0,0 +1,41 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_ruby_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_ruby_test, DefaultConstructor) +{ + const char *rb_scripts[] = { + "hello.rb" + }; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_class_test/CMakeLists.txt b/source/tests/metacall_rust_class_test/CMakeLists.txt new file mode 100644 index 0000000000..28a7f1dac5 --- /dev/null +++ b/source/tests/metacall_rust_class_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_class_test/source/main.cpp b/source/tests/metacall_rust_class_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_class_test/source/metacall_rust_class_test.cpp b/source/tests/metacall_rust_class_test/source/metacall_rust_class_test.cpp new file mode 100644 index 0000000000..235841a990 --- /dev/null +++ b/source/tests/metacall_rust_class_test/source/metacall_rust_class_test.cpp @@ -0,0 +1,107 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_class_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_class_test, DefaultConstructor) +{ + const char *rs_scripts[] = { + "class.rs" + }; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rs", rs_scripts, sizeof(rs_scripts) / sizeof(rs_scripts[0]), NULL)); + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *ret_value = metacallv_class(book_class, "get_number", nullptr, 0); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret_value)); + ASSERT_EQ((int)123, (int)metacall_value_to_int(ret_value)); + metacall_value_destroy(ret_value); + // metacall_value_destroy(book_class); + } + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *constructor_params[] = { + metacall_value_create_int(111) // param1 + }; + void *new_object_v = metacall_class_new(book_class, "book_one", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + void *new_object = metacall_value_to_object(new_object_v); + + void *ret = metacallv_object(new_object, "get_price", nullptr, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(ret)); + + void *param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(param2)); + + metacall_value_destroy(param2); + + void *int_value = metacall_value_create_int(100); + int retcode = metacall_object_set(new_object, "price", int_value); + metacall_value_destroy(int_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)100, (int)metacall_value_to_int(param2)); + metacall_value_destroy(param2); + + metacall_value_destroy(new_object_v); + metacall_value_destroy(ret); + // metacall_value_destroy(myclass_value); + // metacall_value_destroy(book_class); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_mem_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_mem_test/CMakeLists.txt new file mode 100644 index 0000000000..026115ba3b --- /dev/null +++ b/source/tests/metacall_rust_load_from_mem_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-memory-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_mem_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_mem_test/source/main.cpp b/source/tests/metacall_rust_load_from_mem_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_mem_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_mem_test/source/metacall_rust_load_from_mem_test.cpp b/source/tests/metacall_rust_load_from_mem_test/source/metacall_rust_load_from_mem_test.cpp new file mode 100644 index 0000000000..ab09ed72b5 --- /dev/null +++ b/source/tests/metacall_rust_load_from_mem_test/source/metacall_rust_load_from_mem_test.cpp @@ -0,0 +1,73 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_mem_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_mem_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + // Test: Load from memory + static const char buffer[] = + "fn add(num_1: i32, num_2: i32) -> i32 {\n" + "\tnum_1 + num_2\n" + "}" + "fn add2(num_1: f32, num_2: f32) -> f32 {\n" + "\tnum_1 + num_2\n" + "}"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rs", buffer, sizeof(buffer), NULL)); + void *ret = metacall("add", 5, 10); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + ret = metacall("add2", 5.0, 10.0); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(ret); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_package_class_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_package_class_test/CMakeLists.txt new file mode 100644 index 0000000000..0f98b613a9 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_class_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-package-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_package_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_package_class_test/source/main.cpp b/source/tests/metacall_rust_load_from_package_class_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_package_class_test/source/metacall_rust_load_from_package_class_test.cpp b/source/tests/metacall_rust_load_from_package_class_test/source/metacall_rust_load_from_package_class_test.cpp new file mode 100644 index 0000000000..d4f927d5b2 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_class_test/source/metacall_rust_load_from_package_class_test.cpp @@ -0,0 +1,106 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_mem_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_mem_test, DefaultConstructor) +{ + const char *rs_script = "libclass.rlib"; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_package("rs", rs_script, NULL)); + + // Test: Load from package + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *ret_value = metacallv_class(book_class, "get_number", nullptr, 0); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret_value)); + ASSERT_EQ((int)123, (int)metacall_value_to_int(ret_value)); + metacall_value_destroy(ret_value); + } + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *constructor_params[] = { + metacall_value_create_int(111) // param1 + }; + void *new_object_v = metacall_class_new(book_class, "book_one", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + void *new_object = metacall_value_to_object(new_object_v); + + void *ret = metacallv_object(new_object, "get_price", nullptr, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(ret)); + + void *param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(param2)); + + metacall_value_destroy(param2); + + void *int_value = metacall_value_create_int(100); + int retcode = metacall_object_set(new_object, "price", int_value); + metacall_value_destroy(int_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)100, (int)metacall_value_to_int(param2)); + metacall_value_destroy(param2); + + metacall_value_destroy(new_object_v); + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_package_dep_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_package_dep_test/CMakeLists.txt new file mode 100644 index 0000000000..7ac9429cf3 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_dep_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-package-dep-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_package_dep_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_package_dep_test/source/main.cpp b/source/tests/metacall_rust_load_from_package_dep_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_dep_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_package_dep_test/source/metacall_rust_load_from_package_dep_test.cpp b/source/tests/metacall_rust_load_from_package_dep_test/source/metacall_rust_load_from_package_dep_test.cpp new file mode 100644 index 0000000000..565883bc28 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_dep_test/source/metacall_rust_load_from_package_dep_test.cpp @@ -0,0 +1,69 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_package_dep_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_package_dep_test, DefaultConstructor) +{ + const char *rs_script = "debug/libjson_wrapper.rlib"; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_package("rs", rs_script, NULL)); + + /* Test: Load from package */ + { + const char *text = "{\"name\": \"John Doe\"}"; + void *ret = metacall("compile", text); + ASSERT_NE((void *)NULL, (void *)ret); + EXPECT_STREQ(metacall_value_to_string(ret), "\"John Doe\""); + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_package_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_package_test/CMakeLists.txt new file mode 100644 index 0000000000..a5aaabbe66 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-package-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_package_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_package_test/source/main.cpp b/source/tests/metacall_rust_load_from_package_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_package_test/source/metacall_rust_load_from_package_test.cpp b/source/tests/metacall_rust_load_from_package_test/source/metacall_rust_load_from_package_test.cpp new file mode 100644 index 0000000000..cfb3b49957 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_test/source/metacall_rust_load_from_package_test.cpp @@ -0,0 +1,172 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_mem_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_mem_test, DefaultConstructor) +{ + const char *rs_script = "libbasic.rlib"; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_package("rs", rs_script, NULL)); + // Test: Load from package + + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_int(3); + array_value[1] = metacall_value_create_int(5); + array_value[2] = metacall_value_create_int(7); + + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + // void *ret = metacallv_s("add_vec", array_args, 1); + // EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_float(3.0); + array_value[1] = metacall_value_create_float(5.0); + array_value[2] = metacall_value_create_float(7.0); + + void *ret = metacallv_s("add_float_vec", array_args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add", 5, 10); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("run"); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add_float", 5.0, 10.0); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(ret); + } + + // { + // void *ret = metacall("string_len", "Test String"); + // EXPECT_EQ((long)11, (long)metacall_value_to_long(ret)); + // ret = metacall("new_string", 123); + // EXPECT_STREQ(metacall_value_to_string(ret), "get number 123"); + // metacall_value_destroy(ret); + // } + + { + // test if we can return vec + void *ret_vec = metacall("return_vec"); + void *array_args[] = { + ret_vec + }; + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret_vec); + metacall_value_destroy(ret); + } + + { + void *args[] = { + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + void **tuple0 = metacall_value_to_array(map_value[0]); + static const int key0 = 3; + tuple0[0] = metacall_value_create_int(key0); + tuple0[1] = metacall_value_create_float(5.0); + + map_value[1] = metacall_value_create_array(NULL, 2); + void **tuple1 = metacall_value_to_array(map_value[1]); + static const int key1 = 5; + tuple1[0] = metacall_value_create_int(key1); + tuple1[1] = metacall_value_create_float(10.0); + + void *ret = metacallv_s("add_map", args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(args[0]); + metacall_value_destroy(ret); + } + + { + // test if we can return map + void *ret = metacall("return_map"); + void **map_value2 = metacall_value_to_map(ret); + void **tuple0 = metacall_value_to_array(map_value2[0]); + EXPECT_EQ((int)metacall_value_to_int(tuple0[0]), (int)metacall_value_to_float(tuple0[1])); + void **tuple1 = metacall_value_to_array(map_value2[1]); + EXPECT_EQ((int)metacall_value_to_int(tuple1[0]), (int)metacall_value_to_float(tuple1[1])); + void **tuple2 = metacall_value_to_array(map_value2[2]); + EXPECT_EQ((int)metacall_value_to_int(tuple2[0]), (int)metacall_value_to_float(tuple2[1])); + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_test/CMakeLists.txt b/source/tests/metacall_rust_test/CMakeLists.txt new file mode 100644 index 0000000000..31434b1be3 --- /dev/null +++ b/source/tests/metacall_rust_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_test/source/main.cpp b/source/tests/metacall_rust_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_test/source/metacall_rust_test.cpp b/source/tests/metacall_rust_test/source/metacall_rust_test.cpp new file mode 100644 index 0000000000..55736c73e2 --- /dev/null +++ b/source/tests/metacall_rust_test/source/metacall_rust_test.cpp @@ -0,0 +1,191 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_test, DefaultConstructor) +{ + const char *rs_scripts[] = { + "basic.rs" + }; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rs", rs_scripts, sizeof(rs_scripts) / sizeof(rs_scripts[0]), NULL)); + + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_int(3); + array_value[1] = metacall_value_create_int(5); + array_value[2] = metacall_value_create_int(7); + + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + // void *ret = metacallv_s("add_vec", array_args, 1); + // EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_float(3.0); + array_value[1] = metacall_value_create_float(5.0); + array_value[2] = metacall_value_create_float(7.0); + + void *ret = metacallv_s("add_float_vec", array_args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add", 5, 10); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("run"); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add_float", 5.0, 10.0); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(ret); + } + + // { + // void *ret = metacall("string_len", "Test String"); + // EXPECT_EQ((long)11, (long)metacall_value_to_long(ret)); + // ret = metacall("new_string", 123); + // EXPECT_STREQ(metacall_value_to_string(ret), "get number 123"); + // metacall_value_destroy(ret); + // } + + { + void *ret = metacall("str_slice", "hellow"); + EXPECT_STREQ(metacall_value_to_string(ret), "hel"); + metacall_value_destroy(ret); + } + + { + // test if we can return vec + void *ret_vec = metacall("return_vec"); + void *array_args[] = { + ret_vec + }; + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret_vec); + metacall_value_destroy(ret); + } + + { + void *args[] = { + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + void **tuple0 = metacall_value_to_array(map_value[0]); + static const int key0 = 3; + tuple0[0] = metacall_value_create_int(key0); + tuple0[1] = metacall_value_create_float(5.0); + + map_value[1] = metacall_value_create_array(NULL, 2); + void **tuple1 = metacall_value_to_array(map_value[1]); + static const int key1 = 5; + tuple1[0] = metacall_value_create_int(key1); + tuple1[1] = metacall_value_create_float(10.0); + + void *ret = metacallv_s("add_map", args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(args[0]); + metacall_value_destroy(ret); + } + + { + // test if we can return map + void *ret = metacall("return_map"); + void **map_value2 = metacall_value_to_map(ret); + void **tuple0 = metacall_value_to_array(map_value2[0]); + EXPECT_EQ((int)metacall_value_to_int(tuple0[0]), (int)metacall_value_to_float(tuple0[1])); + void **tuple1 = metacall_value_to_array(map_value2[1]); + EXPECT_EQ((int)metacall_value_to_int(tuple1[0]), (int)metacall_value_to_float(tuple1[1])); + void **tuple2 = metacall_value_to_array(map_value2[2]); + EXPECT_EQ((int)metacall_value_to_int(tuple2[0]), (int)metacall_value_to_float(tuple2[1])); + metacall_value_destroy(ret); + } + + /* TODO */ + /* + { + // test if we can return result + void *ret = metacall("age_group", 21); + + // TODO + + metacall_value_destroy(ret); + } + */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_sandbox_plugin_test/CMakeLists.txt b/source/tests/metacall_sandbox_plugin_test/CMakeLists.txt new file mode 100644 index 0000000000..3a98235b10 --- /dev/null +++ b/source/tests/metacall_sandbox_plugin_test/CMakeLists.txt @@ -0,0 +1,175 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_PLUGINS_SANDBOX) + return() +endif() + +include(Portability) + +# Sandbox is only supported in Linux +if(NOT PROJECT_OS_FAMILY STREQUAL unix) + return() +endif() + +# Thread Sanitizer deadlocks with ASSERT_EXIT (TODO: Review this when fork safety is implemented in sandbox_plugin) +if(OPTION_BUILD_THREAD_SANITIZER) + return() +endif() + +# TODO: Test if we can make it run properly with use_sigaltstack=1 (https://groups.google.com/a/chromium.org/g/chromium-reviews/c/xX4xDy21714/m/1yKX1JLE620J) +if(OPTION_BUILD_ADDRESS_SANITIZER) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-sandbox-plugin-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_sandbox_plugin_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + sandbox_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + # Valgrind does not work well with libseccomp + PROPERTY LABELS ${target} MEMCHECK_IGNORE +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_sandbox_plugin_test/source/main.cpp b/source/tests/metacall_sandbox_plugin_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_sandbox_plugin_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_sandbox_plugin_test/source/metacall_sandbox_plugin_test.cpp b/source/tests/metacall_sandbox_plugin_test/source/metacall_sandbox_plugin_test.cpp new file mode 100644 index 0000000000..3cd32dcf0f --- /dev/null +++ b/source/tests/metacall_sandbox_plugin_test/source/metacall_sandbox_plugin_test.cpp @@ -0,0 +1,652 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include + +void invalid_syscall(void) +{ + struct utsname data; + uname(&data); + printf("%s\n", data.sysname); +} + +void invalid_io_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable io syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_io", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + printf("Invalid IO syscall\n"); +} + +#include + +void invalid_sockets_syscall() +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + close(fd); +} + +#include +#include + +void invalid_ipc_syscall() +{ + // Create a shared memory segment + int shm_id = shmget(1234, 1024, IPC_CREAT | 0666); + if (shm_id == -1) + { + perror("shmget"); + exit(EXIT_FAILURE); + } + + // Attach the shared memory segment to the process's address space + void *shm_addr = shmat(shm_id, NULL, 0); + if (shm_addr == (void *)-1) + { + perror("shmat"); + exit(EXIT_FAILURE); + } + + // Write data to shared memory + const char *message = "Hello, Shared Memory!"; + strncpy((char *)shm_addr, message, 1024); + + printf("Data written to shared memory: %s\n", (char *)shm_addr); + + // Detach the shared memory segment + if (shmdt(shm_addr) == -1) + { + perror("shmdt"); + exit(EXIT_FAILURE); + } + + // Remove the shared memory segment + if (shmctl(shm_id, IPC_RMID, NULL) == -1) + { + perror("shmctl"); + exit(EXIT_FAILURE); + } +} + +#include +#include + +void invalid_process_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable process syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_process", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + pid_t pid = fork(); + + if (pid < 0) + { + perror("fork"); + exit(EXIT_FAILURE); + } + + if (pid == 0) + { + // Child process + exit(EXIT_SUCCESS); // Exit immediately + } + else + { + // Parent process + int status; + if (waitpid(pid, &status, 0) == -1) + { + perror("waitpid"); + exit(EXIT_FAILURE); + } + } +} + +#include +#include + +void invalid_filesystems_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable filesystems syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_filesystems", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + int fd = open("/tmp/testfile", O_RDONLY); + if (fd == -1) + { + perror("open"); + exit(EXIT_FAILURE); + } + + close(fd); +} + +#include + +void invalid_time_syscall() +{ + sleep(1); +} + +#include + +void invalid_memory_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable memory syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_memory", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) + { + perror("mmap"); + exit(EXIT_FAILURE); + } + munmap(addr, 4096); +} + +#include + +void invalid_signals_syscall() +{ + if (signal(SIGINT, SIG_IGN) == SIG_ERR) + { + perror("signal"); + exit(EXIT_FAILURE); + } +} +class metacall_sandbox_plugin_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_sandbox_plugin_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable uname syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_uname", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute uname */ + ASSERT_EXIT({ invalid_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + /* Testing invalid number of parameters */ + { + void *ret = metacallhv_s(handle, "sandbox_destroy", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + } + + /* Testing correct number of parameters but invalid type */ + { + void *args[1] = { metacall_value_create_long(2343) }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +/* [Note] This test blocks all the gtest context, so you should comment it to allow testing for other test cases */ +TEST_F(metacall_sandbox_plugin_test, SANDBOX_IO_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute fopen */ + ASSERT_EXIT({ invalid_io_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_SOCKETS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable sockets syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_sockets", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute socket */ + ASSERT_EXIT({ invalid_sockets_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_IPC_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable IPC syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_ipc", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute IPC operation */ + ASSERT_EXIT({ invalid_ipc_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_PROCESS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute process operation */ + ASSERT_EXIT({ invalid_process_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_FILESYSTEMS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute file systems operation */ + ASSERT_EXIT({ invalid_filesystems_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_TIME_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable Time syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_time", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute time operation */ + ASSERT_EXIT({ invalid_time_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_MEMORY_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute memory operation */ + ASSERT_EXIT({ invalid_memory_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_SIGNALS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable IPC syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_signals", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute signals operation */ + ASSERT_EXIT({ invalid_signals_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_test/CMakeLists.txt b/source/tests/metacall_test/CMakeLists.txt index 29390f80e2..8751e1fe0e 100644 --- a/source/tests/metacall_test/CMakeLists.txt +++ b/source/tests/metacall_test/CMakeLists.txt @@ -80,20 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -128,10 +123,47 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: data race (pid=13427) + # Write of size 8 at 0x7b5c00010680 by thread T8: + # #0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:706 (libtsan.so.2+0x47e82) + # #1 (libcoreclr.so+0x36ba88) + # + # Previous write of size 8 at 0x7b5c00010680 by main thread: + # [failed to restore the stack] + # + # Thread T8 '.NET ThreadPool' (tid=13563, running) created by thread T7 at: + # #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1001 (libtsan.so.2+0x5e686) + # #1 (libcoreclr.so+0x4e90ce) + # + # SUMMARY: ThreadSanitizer: data race (/usr/share/dotnet/shared/Microsoft.NETCore.App/5.0.17/libcoreclr.so+0x36ba88) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader + rb_loader + cs_loader + jsm_loader + js_loader + mock_loader + c_loader + file_loader +) + # # Define test properties # @@ -140,6 +172,20 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizer (this happens when C# loader is enabled): + # + # Tracer caught signal 11: addr=0x600000690 pc=0x7f3a7b6710f0 sp=0x7f3a75e32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_test/source/main.cpp b/source/tests/metacall_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_test/source/main.cpp +++ b/source/tests/metacall_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_test/source/metacall_test.cpp b/source/tests/metacall_test/source/metacall_test.cpp index e4e59a78bf..ddbf10c03b 100644 --- a/source/tests/metacall_test/source/metacall_test.cpp +++ b/source/tests/metacall_test/source/metacall_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,38 +18,38 @@ * */ -#include +#include #include -#include #include +#include #include #if defined(WIN32) || defined(_WIN32) -# ifndef NOMINMAX -# define NOMINMAX -# endif + #ifndef NOMINMAX + #define NOMINMAX + #endif -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif -# include + #include #elif defined(unix) || defined(__unix__) || defined(__unix) || \ defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) || \ defined(__CYGWIN__) || defined(__CYGWIN32__) || \ defined(__MINGW32__) || defined(__MINGW64__) || \ (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) -# include + #include #endif -void * c_function(size_t argc, void * args[], void * data) +void *c_function(size_t argc, void *args[], void *data) { (void)argc; (void)data; - printf("%s\n", (char*)args[0]); + printf("%s\n", (char *)args[0]); return metacall_value_create_int(1); } @@ -65,77 +65,76 @@ TEST_F(metacall_test, DefaultConstructor) metacall_log_stdio_type log_stdio = { stdout }; - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - #if defined(WIN32) || defined(_WIN32) + #if defined(WIN32) || defined(_WIN32) - DWORD length = MAX_PATH; - char cwd[MAX_PATH]; + DWORD length = MAX_PATH; + char cwd[MAX_PATH]; - ASSERT_NE((DWORD) 0, (DWORD) GetCurrentDirectory(length, cwd)); + ASSERT_NE((DWORD)0, (DWORD)GetCurrentDirectory(length, cwd)); - #elif defined(unix) || defined(__unix__) || defined(__unix) || \ - defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) || \ - defined(__CYGWIN__) || defined(__CYGWIN32__) || \ - defined(__MINGW32__) || defined(__MINGW64__) || \ - (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #elif defined(unix) || defined(__unix__) || defined(__unix) || \ + defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) || \ + defined(__CYGWIN__) || defined(__CYGWIN32__) || \ + defined(__MINGW32__) || defined(__MINGW64__) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) - char cwd[PATH_MAX]; + char cwd[PATH_MAX]; - ASSERT_NE((char *) NULL, (char *) getcwd(cwd, sizeof(cwd))); + ASSERT_NE((char *)NULL, (char *)getcwd(cwd, sizeof(cwd))); - #endif + #endif /* Lazy evaluation of execution paths (do not initialize Python runtime) */ - ASSERT_EQ((int) 0, (int) metacall_execution_path("py", cwd)); + ASSERT_EQ((int)0, (int)metacall_execution_path("py", cwd)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Native register */ { - void * ret = NULL; + void *ret = NULL; - void * func = NULL; + void *func = NULL; int result = 0; - ASSERT_EQ((int) 0, (int) metacall_register("c_print", c_function, NULL, METACALL_INT, 1, METACALL_STRING)); + ASSERT_EQ((int)0, (int)metacall_register("c_print", c_function, NULL, METACALL_INT, 1, METACALL_STRING)); ret = metacall("c_print", "Hello native function!"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); result = metacall_value_to_int(ret); - EXPECT_EQ((int) result, (int) 1); + EXPECT_EQ((int)result, (int)1); metacall_value_destroy(ret); func = metacall_function("c_print"); - EXPECT_NE((void *) NULL, (void *) func); + EXPECT_NE((void *)NULL, (void *)func); ret = metacallf(func, "Hello native function!"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); result = metacall_value_to_int(ret); - EXPECT_EQ((int) result, (int) 1); + EXPECT_EQ((int)result, (int)1); metacall_value_destroy(ret); } - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py", "web.py" }; @@ -153,15 +152,18 @@ TEST_F(metacall_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + /* Test for asyncness Python introspection */ + EXPECT_EQ((int)0, metacall_function_async(metacall_function("multiply"))); ret = metacall("multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -169,81 +171,81 @@ TEST_F(metacall_test, DefaultConstructor) { ret = metacall("multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); ret = metacall("sum", 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 4500); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)4500); metacall_value_destroy(ret); ret = metacall("sum", 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 7); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); metacall_value_destroy(ret); ret = metacall("hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret)); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); metacall_value_destroy(ret); ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); /* Testing Python Buffer */ char buffer[] = { 0, 1, 2, 3, 4 }; - void * buffer_value = metacall_value_create_buffer((void *)buffer, sizeof(buffer)); + void *buffer_value = metacall_value_create_buffer((void *)buffer, sizeof(buffer)); - void * args[] = { + void *args[] = { buffer_value }; - EXPECT_NE((void *) NULL, (void *) buffer_value); + EXPECT_NE((void *)NULL, (void *)buffer_value); ret = metacallv("bytebuff", args); metacall_value_destroy(buffer_value); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((size_t) sizeof("abcd"), (size_t) metacall_value_size(ret)); + EXPECT_EQ((size_t)sizeof("abcd") - 1, (size_t)metacall_value_size(ret)); - EXPECT_EQ((int) 0, (int) memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); + EXPECT_EQ((int)0, (int)memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); metacall_value_destroy(ret); const size_t dyn_buffer_size = 256; - unsigned char * dyn_buffer = (unsigned char *)malloc(sizeof(unsigned char) * dyn_buffer_size); + unsigned char *dyn_buffer = (unsigned char *)malloc(sizeof(unsigned char) * dyn_buffer_size); for (size_t i = 0; i < dyn_buffer_size; ++i) { @@ -252,7 +254,7 @@ TEST_F(metacall_test, DefaultConstructor) buffer_value = metacall_value_create_buffer((void *)dyn_buffer, dyn_buffer_size); - EXPECT_NE((void *) NULL, (void *) buffer_value); + EXPECT_NE((void *)NULL, (void *)buffer_value); args[0] = buffer_value; @@ -260,11 +262,11 @@ TEST_F(metacall_test, DefaultConstructor) metacall_value_destroy(buffer_value); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((size_t) sizeof("abcd"), (size_t) metacall_value_size(ret)); + EXPECT_EQ((size_t)sizeof("abcd") - 1, (size_t)metacall_value_size(ret)); - EXPECT_EQ((int) 0, (int) memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); + EXPECT_EQ((int)0, (int)memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); metacall_value_destroy(ret); @@ -272,212 +274,243 @@ TEST_F(metacall_test, DefaultConstructor) ret = metacall("index"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), web_content)); + EXPECT_STREQ(metacall_value_to_string(ret), web_content); metacall_value_destroy(ret); + + #if !defined(__MEMORYCHECK__) && !defined(__ADDRESS_SANITIZER__) && !defined(__THREAD_SANITIZER__) && !defined(__MEMORY_SANITIZER__) + /* Testing corrupted value input */ + struct + { + int a; + int corrupted; + int value; + char padding[100]; + } corrupted_value = { 0, 0, 0, { 0 } }; + + void *corrupted_args[] = { + (void *)&corrupted_value, + (void *)&corrupted_value + }; + + EXPECT_EQ((void *)NULL, (void *)metacallfv_s(metacall_function("multiply"), corrupted_args, 2)); + + /* Testing freed value input */ + + /* TODO: The next snippet of code works but address sanitizer warns about access warning, + * because in order to check the magic number we need to access the pointer to the struct. + * A better solution would be, when using a custom memory pool allocator (aka object pool) + * detect if the pointer is allocated or not in the object pool, so we track it without + * need to access the pointer in order to read it. This can be a better improvement but + * for now, this would be sufficient to catch most of the errors. + */ + + /* + void *freed_args[] = { + (void *)metacall_value_create_long(3L), + (void *)metacall_value_create_long(5L) + }; + + metacall_value_destroy(freed_args[0]); + metacall_value_destroy(freed_args[1]); + + EXPECT_EQ((void *)NULL, (void *)metacallfv_s(metacall_function("multiply"), freed_args, 2)); + */ + #endif } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "hello.rb", "second.rb" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); ret = metacall("say_multiply", 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 35); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)35); metacall_value_destroy(ret); ret = metacall("say_null"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); metacall_value_destroy(ret); ret = metacall("say_hello", "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello meta-programmer!"); metacall_value_destroy(ret); ret = metacall("get_second", 5, 12); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 12); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)12); metacall_value_destroy(ret); - const enum metacall_value_id backwards_prime_int_ids[] = - { + const enum metacall_value_id backwards_prime_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("backwardsPrime", backwards_prime_int_ids, 9900, 10000); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - void ** v_array = metacall_value_to_array(ret); + void **v_array = metacall_value_to_array(ret); - EXPECT_EQ((int) metacall_value_to_int(v_array[0]), (int) 9923); - EXPECT_EQ((int) metacall_value_to_int(v_array[1]), (int) 9931); - EXPECT_EQ((int) metacall_value_to_int(v_array[2]), (int) 9941); - EXPECT_EQ((int) metacall_value_to_int(v_array[3]), (int) 9967); + EXPECT_EQ((int)metacall_value_to_int(v_array[0]), (int)9923); + EXPECT_EQ((int)metacall_value_to_int(v_array[1]), (int)9931); + EXPECT_EQ((int)metacall_value_to_int(v_array[2]), (int)9941); + EXPECT_EQ((int)metacall_value_to_int(v_array[3]), (int)9967); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript SpiderMonkey */ - #if defined(OPTION_BUILD_LOADERS_JSM) +/* JavaScript SpiderMonkey */ +#if defined(OPTION_BUILD_LOADERS_JSM) { - const char * jsm_scripts[] = - { + const char *jsm_scripts[] = { "spider.jsm" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("say_spider", 8, 4)); + EXPECT_EQ((void *)NULL, (void *)metacall("say_spider", 8, 4)); } - #endif /* OPTION_BUILD_LOADERS_JSM */ +#endif /* OPTION_BUILD_LOADERS_JSM */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) && !defined(OPTION_BUILD_LOADERS_NODE) /* TODO: NodeJS and V8 runtimes do not work at same time */ +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) && !defined(OPTION_BUILD_LOADERS_NODE) /* TODO: NodeJS and V8 runtimes do not work at same time */ { - const char * js_scripts[] = - { + const char *js_scripts[] = { "divide.js", "third.js" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); ret = metacall("say_divide", 32.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 8.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)8.0); metacall_value_destroy(ret); ret = metacall("some_text", "abc", "def"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "abcdef")); + EXPECT_STREQ(metacall_value_to_string(ret), "abcdef"); metacall_value_destroy(ret); ret = metacall("third_value", "abc", "def", "efg"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "efg")); + EXPECT_STREQ(metacall_value_to_string(ret), "efg"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); ret = metacall("my_empty_func"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 1234); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)1234); metacall_value_destroy(ret); ret = metacall("two_doubles", 3.14, 68.3); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 3.1416); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.1416); metacall_value_destroy(ret); ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((char) metacall_value_to_char(ret), (char) 'A'); + EXPECT_EQ((char)metacall_value_to_char(ret), (char)'A'); metacall_value_destroy(ret); ret = metacall("new_args", "goodbye"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello World")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello World"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - /* C# NetCore */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* C# NetCore */ +#if defined(OPTION_BUILD_LOADERS_CS) { - const char * cs_scripts[] = - { + const char *cs_scripts[] = { "hello.cs" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("Say", "Hello para with params!")); + EXPECT_EQ((void *)NULL, (void *)metacall("Say", "Hello para with params!")); } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ - /* C */ - #if defined(OPTION_BUILD_LOADERS_C) +/* C */ +#if defined(OPTION_BUILD_LOADERS_C) { - const char * c_scripts[] = - { + const char *c_scripts[] = { "compiled.c" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_C */ +#endif /* OPTION_BUILD_LOADERS_C */ - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; @@ -487,57 +520,55 @@ TEST_F(metacall_test, DefaultConstructor) "\t}\n" "module.exports = { nodmem };\n"; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); ret = metacall("lambda"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 15.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); metacall_value_destroy(ret); /* TODO: Implement all remaining calls for nod.js */ - ASSERT_EQ((int) 0, (int) metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); ret = metacall("nodmem"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 43.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)43.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ - +#endif /* OPTION_BUILD_LOADERS_NODE */ - /* File */ - #if defined(OPTION_BUILD_LOADERS_FILE) +/* File */ +#if defined(OPTION_BUILD_LOADERS_FILE) { - const char * file_scripts[] = - { + const char *file_scripts[] = { "static.html", "favicon.ico" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("file", file_scripts, sizeof(file_scripts) / sizeof(file_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("file", file_scripts, sizeof(file_scripts) / sizeof(file_scripts[0]), NULL)); ret = metacall("static.html"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); std::cout << metacall_value_to_string(ret) << std::endl; @@ -545,13 +576,13 @@ TEST_F(metacall_test, DefaultConstructor) ret = metacall("favicon.ico"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); std::cout << metacall_value_to_string(ret) << std::endl; metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_FILE */ +#endif /* OPTION_BUILD_LOADERS_FILE */ /* Print inspect information */ { @@ -559,13 +590,13 @@ TEST_F(metacall_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -574,5 +605,5 @@ TEST_F(metacall_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_test/source/metacall_test_split.cpp b/source/tests/metacall_test/source/metacall_test_split.cpp index 792ab0e71b..0a19bce6a1 100644 --- a/source/tests/metacall_test/source/metacall_test_split.cpp +++ b/source/tests/metacall_test/source/metacall_test_split.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -34,17 +34,17 @@ class metacall_test : public testing::Test TEST_F(metacall_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); metacall_print_info(); - EXPECT_EQ((int) 0, (int) metacall_initialize()); + EXPECT_EQ((int)0, (int)metacall_initialize()); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } class metacall_loader_test : public testing::Test @@ -52,238 +52,238 @@ class metacall_loader_test : public testing::Test public: metacall_loader_test() { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); - EXPECT_EQ((int) 0, (int) metacall_initialize()); + EXPECT_EQ((int)0, (int)metacall_initialize()); } ~metacall_loader_test() { - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } }; /* Python */ #if defined(OPTION_BUILD_LOADERS_PY) - TEST_F(metacall_loader_test, Python) - { - const long seven_multiples_limit = 10; +TEST_F(metacall_loader_test, Python) +{ + const long seven_multiples_limit = 10; - long iterator; + long iterator; - value ret = NULL; + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("example.py")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("example.py")); - ret = metacall("multiply", 5, 15); + ret = metacall("multiply", 5, 15); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) 75); + EXPECT_EQ((long)value_to_long(ret), (long)75); - value_destroy(ret); + value_destroy(ret); - log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); + log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); - for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) - { - ret = metacall("multiply", 7, iterator); + for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) + { + ret = metacall("multiply", 7, iterator); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)value_to_long(ret), (long)(7 * iterator)); - value_destroy(ret); - } + value_destroy(ret); + } - ret = metacall("divide", 64.0, 2.0); + ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)value_to_double(ret), (double)32.0); - value_destroy(ret); + value_destroy(ret); - ret = metacall("sum", 1000, 3500); + ret = metacall("sum", 1000, 3500); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) 4500); + EXPECT_EQ((long)value_to_long(ret), (long)4500); - value_destroy(ret); + value_destroy(ret); - ret = metacall("sum", 3, 4); + ret = metacall("sum", 3, 4); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) 7); + EXPECT_EQ((long)value_to_long(ret), (long)7); - value_destroy(ret); + value_destroy(ret); - EXPECT_EQ((value) NULL, (value) metacall("hello")); + EXPECT_EQ((value)NULL, (value)metacall("hello")); - ret = metacall("strcat", "Hello ", "Universe"); + ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(value_to_string(ret), "Hello Universe"); - value_destroy(ret); - } + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_PY */ /* Ruby */ #if defined(OPTION_BUILD_LOADERS_RB) - TEST_F(metacall_loader_test, Ruby) - { - value ret = NULL; +TEST_F(metacall_loader_test, Ruby) +{ + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("hello.rb")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("hello.rb")); - ret = metacall("say_multiply", 5, 7); + ret = metacall("say_multiply", 5, 7); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) value_to_int(ret), (int) 35); + EXPECT_EQ((int)value_to_int(ret), (int)35); - value_destroy(ret); + value_destroy(ret); - ret = metacall("say_null"); + ret = metacall("say_null"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - metacall_value_destroy(ret); + metacall_value_destroy(ret); - ret = metacall("say_hello", "meta-programmer"); + ret = metacall("say_hello", "meta-programmer"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello meta-programmer!")); + EXPECT_STREQ(value_to_string(ret), "Hello meta-programmer!"); - value_destroy(ret); - } + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_RB */ /* JavaScript SpiderMonkey */ #if defined(OPTION_BUILD_LOADERS_JSM) - TEST_F(metacall_loader_test, JavascriptSpiderMonkey) - { - EXPECT_EQ((int) 0, (int) metacall_load_from_file("spider.jsm")); +TEST_F(metacall_loader_test, JavascriptSpiderMonkey) +{ + EXPECT_EQ((int)0, (int)metacall_load_from_file("spider.jsm")); - EXPECT_EQ((void *) NULL, (void *) metacall("say_spider", 8, 4)); - } + EXPECT_EQ((void *)NULL, (void *)metacall("say_spider", 8, 4)); +} #endif /* OPTION_BUILD_LOADERS_JSM */ /* JavaScript V8 */ #if defined(OPTION_BUILD_LOADERS_JS) - TEST_F(metacall_loader_test, JavascriptV8) - { - value ret = NULL; +TEST_F(metacall_loader_test, JavascriptV8) +{ + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("divide.js")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("divide.js")); - ret = metacall("say_divide", 32.0, 4.0); + ret = metacall("say_divide", 32.0, 4.0); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 8.0); + EXPECT_EQ((double)value_to_double(ret), (double)8.0); - value_destroy(ret); + value_destroy(ret); - ret = metacall("some_text", "abc", "def"); + ret = metacall("some_text", "abc", "def"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "abcdef")); + EXPECT_STREQ(value_to_string(ret), "abcdef"); - value_destroy(ret); - } + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_JS */ /* Mock */ #if defined(OPTION_BUILD_LOADERS_MOCK) - TEST_F(metacall_loader_test, Mock) - { - value ret = NULL; +TEST_F(metacall_loader_test, Mock) +{ + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("empty.mock")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("empty.mock")); - ret = metacall("my_empty_func"); + ret = metacall("my_empty_func"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) value_to_int(ret), (int) 1234); + EXPECT_EQ((int)value_to_int(ret), (int)1234); - value_destroy(ret); + value_destroy(ret); - ret = metacall("two_doubles", 3.14, 68.3); + ret = metacall("two_doubles", 3.14, 68.3); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 3.1416); + EXPECT_EQ((double)value_to_double(ret), (double)3.1416); - value_destroy(ret); + value_destroy(ret); - ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); + ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((char) value_to_char(ret), (char) 'A'); + EXPECT_EQ((char)value_to_char(ret), (char)'A'); - value_destroy(ret); + value_destroy(ret); - ret = metacall("new_args", "goodbye"); + ret = metacall("new_args", "goodbye"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello World")); + EXPECT_STREQ(value_to_string(ret), "Hello World"); - value_destroy(ret); + value_destroy(ret); - ret = metacall("my_empty_func_str"); + ret = metacall("my_empty_func_str"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello World")); + EXPECT_STREQ(value_to_string(ret), "Hello World"); - value_destroy(ret); - } + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_MOCK */ /* C# Netcore */ #if defined(OPTION_BUILD_LOADERS_CS) - TEST_F(metacall_loader_test, CSharpNetCore) - { - EXPECT_EQ((int) 0, (int) metacall_load_from_file("hello.cs")); +TEST_F(metacall_loader_test, CSharpNetCore) +{ + EXPECT_EQ((int)0, (int)metacall_load_from_file("hello.cs")); - EXPECT_EQ((void *) NULL, (void *) metacall("Say", "Hello para with params!")); - } + EXPECT_EQ((void *)NULL, (void *)metacall("Say", "Hello para with params!")); +} #endif /* OPTION_BUILD_LOADERS_CS */ /* C */ #if defined(OPTION_BUILD_LOADERS_C) - TEST_F(metacall_loader_test, C) - { - EXPECT_EQ((int) 0, (int) metacall_load_from_file("compiled.c")); - } +TEST_F(metacall_loader_test, C) +{ + EXPECT_EQ((int)0, (int)metacall_load_from_file("compiled.c")); +} #endif /* OPTION_BUILD_LOADERS_C */ diff --git a/source/tests/metacall_typescript_call_map_test/CMakeLists.txt b/source/tests/metacall_typescript_call_map_test/CMakeLists.txt new file mode 100644 index 0000000000..4005debcf6 --- /dev/null +++ b/source/tests/metacall_typescript_call_map_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-call-map-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_call_map_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_call_map_test/source/main.cpp b/source/tests/metacall_typescript_call_map_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_call_map_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_call_map_test/source/metacall_typescript_call_map_test.cpp b/source/tests/metacall_typescript_call_map_test/source/metacall_typescript_call_map_test.cpp new file mode 100644 index 0000000000..82af7b2944 --- /dev/null +++ b/source/tests/metacall_typescript_call_map_test/source/metacall_typescript_call_map_test.cpp @@ -0,0 +1,83 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_call_map_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_call_map_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + static const char args_map[] = "{\"left\":10,\"right\":2}"; + + void *ret = metacallfms(metacall_function("typed_sum"), args_map, sizeof(args_map), allocator); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)12.0); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + } + + metacall_allocator_destroy(allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_jsx_default_test/CMakeLists.txt b/source/tests/metacall_typescript_jsx_default_test/CMakeLists.txt new file mode 100644 index 0000000000..32674e550f --- /dev/null +++ b/source/tests/metacall_typescript_jsx_default_test/CMakeLists.txt @@ -0,0 +1,158 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-jsx-default-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_jsx_default_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_jsx_default_test/source/main.cpp b/source/tests/metacall_typescript_jsx_default_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_jsx_default_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_jsx_default_test/source/metacall_typescript_jsx_default_test.cpp b/source/tests/metacall_typescript_jsx_default_test/source/metacall_typescript_jsx_default_test.cpp new file mode 100644 index 0000000000..609bb760e3 --- /dev/null +++ b/source/tests/metacall_typescript_jsx_default_test/source/metacall_typescript_jsx_default_test.cpp @@ -0,0 +1,51 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_jsx_default_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_jsx_default_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *jsx_scripts[] = { + "server.jsx" + }; + + /* Load scripts */ + EXPECT_EQ((int)1, (int)metacall_load_from_file("ts", jsx_scripts, sizeof(jsx_scripts) / sizeof(jsx_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_node_test/CMakeLists.txt b/source/tests/metacall_typescript_node_test/CMakeLists.txt new file mode 100644 index 0000000000..9dbd14de9d --- /dev/null +++ b/source/tests/metacall_typescript_node_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-node-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_node_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_node_test/source/main.cpp b/source/tests/metacall_typescript_node_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_node_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_node_test/source/metacall_typescript_node_test.cpp b/source/tests/metacall_typescript_node_test/source/metacall_typescript_node_test.cpp new file mode 100644 index 0000000000..4285ca809f --- /dev/null +++ b/source/tests/metacall_typescript_node_test/source/metacall_typescript_node_test.cpp @@ -0,0 +1,142 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_node_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_node_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" + }; + + void *ret = NULL; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + ret = metacall("typed_sum", 3.0, 4.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); + + metacall_value_destroy(ret); + + /* Test arrays */ + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_double(3.0); + array_value[1] = metacall_value_create_double(5.0); + array_value[2] = metacall_value_create_double(7.0); + + ret = metacallv("typed_array", array_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(array_args[0]); + + /* Test records */ + void *record_args[] = { + metacall_value_create_map(NULL, 1) + }; + + void **map_value = metacall_value_to_map(record_args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla = metacall_value_to_array(map_value[0]); + + static const char key[] = "element"; + + tupla[0] = metacall_value_create_string(key, sizeof(key) - 1); + tupla[1] = metacall_value_create_double(6.0); + + ret = metacallv("object_record", record_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(record_args[0]); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_require_test/CMakeLists.txt b/source/tests/metacall_typescript_require_test/CMakeLists.txt new file mode 100644 index 0000000000..d4d51e4660 --- /dev/null +++ b/source/tests/metacall_typescript_require_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-require-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_require_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_require_test/source/main.cpp b/source/tests/metacall_typescript_require_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_require_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_require_test/source/metacall_typescript_require_test.cpp b/source/tests/metacall_typescript_require_test/source/metacall_typescript_require_test.cpp new file mode 100644 index 0000000000..b94e43fa36 --- /dev/null +++ b/source/tests/metacall_typescript_require_test/source/metacall_typescript_require_test.cpp @@ -0,0 +1,81 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_require_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_require_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "badrequire/index.ts" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + void *ret = metacall("isExported"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((boolean)metacall_value_to_bool(ret), (boolean)1L); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_test/CMakeLists.txt b/source/tests/metacall_typescript_test/CMakeLists.txt index 2c41cc5009..9af3378ebb 100644 --- a/source/tests/metacall_typescript_test/CMakeLists.txt +++ b/source/tests/metacall_typescript_test/CMakeLists.txt @@ -86,20 +86,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -121,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,6 +131,16 @@ target_link_libraries(${target} add_test(NAME ${target} COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader ) # @@ -143,7 +148,7 @@ add_test(NAME ${target} # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_typescript_test/source/main.cpp b/source/tests/metacall_typescript_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_typescript_test/source/main.cpp +++ b/source/tests/metacall_typescript_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_typescript_test/source/metacall_typescript_test.cpp b/source/tests/metacall_typescript_test/source/metacall_typescript_test.cpp index 735fcebb44..9ebd5f9f30 100644 --- a/source/tests/metacall_typescript_test/source/metacall_typescript_test.cpp +++ b/source/tests/metacall_typescript_test/source/metacall_typescript_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_typescript_test : public testing::Test { @@ -33,37 +33,35 @@ TEST_F(metacall_typescript_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* TypeScript */ - #if defined(OPTION_BUILD_LOADERS_TS) +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) { - const char * ts_scripts[] = - { - "typedfunc.ts" + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" }; - void * ret = NULL; + void *ret = NULL; /* Load scripts */ - EXPECT_EQ((int) 0, (int) metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); /* Test typed sum */ ret = metacall("typed_sum", 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); /* Test arrays */ - void * array_args[] = - { + void *array_args[] = { metacall_value_create_array(NULL, 3) }; - void ** array_value = metacall_value_to_array(array_args[0]); + void **array_value = metacall_value_to_array(array_args[0]); array_value[0] = metacall_value_create_double(3.0); array_value[1] = metacall_value_create_double(5.0); @@ -71,25 +69,24 @@ TEST_F(metacall_typescript_test, DefaultConstructor) ret = metacallv("typed_array", array_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 15.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); metacall_value_destroy(ret); metacall_value_destroy(array_args[0]); /* Test records */ - void * record_args[] = - { + void *record_args[] = { metacall_value_create_map(NULL, 1) }; - void ** map_value = metacall_value_to_map(record_args[0]); + void **map_value = metacall_value_to_map(record_args[0]); map_value[0] = metacall_value_create_array(NULL, 2); - void ** tupla = metacall_value_to_array(map_value[0]); + void **tupla = metacall_value_to_array(map_value[0]); static const char key[] = "element"; @@ -98,15 +95,15 @@ TEST_F(metacall_typescript_test, DefaultConstructor) ret = metacallv("object_record", record_args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 6.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); metacall_value_destroy(ret); metacall_value_destroy(record_args[0]); } - #endif /* OPTION_BUILD_LOADERS_TS */ +#endif /* OPTION_BUILD_LOADERS_TS */ /* Print inspect information */ { @@ -114,13 +111,13 @@ TEST_F(metacall_typescript_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -129,5 +126,5 @@ TEST_F(metacall_typescript_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_typescript_tsx_loop_fail_test/CMakeLists.txt b/source/tests/metacall_typescript_tsx_loop_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..ca6465387a --- /dev/null +++ b/source/tests/metacall_typescript_tsx_loop_fail_test/CMakeLists.txt @@ -0,0 +1,182 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-tsx-loop-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_tsx_loop_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13916) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #9 node_loader_port_load_from_file_export(napi_env__*, napi_callback_info__*) /usr/local/metacall/source/loaders/node_loader/source/node_loader_port.cpp:395 (libnode_loaderd.so+0x113c5) + # #10 (libnode.so.72+0x7b6344) + # #11 node_loader_impl_async_func_call_safe /usr/local/metacall/source/loaders/node_loader/source/node_loader_impl.cpp:2040 (libnode_loaderd.so+0xe2e8) + # #12 (libnode.so.72+0x7b6344) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_tsx_loop_fail_test/source/main.cpp b/source/tests/metacall_typescript_tsx_loop_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_tsx_loop_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_tsx_loop_fail_test/source/metacall_typescript_tsx_loop_fail_test.cpp b/source/tests/metacall_typescript_tsx_loop_fail_test/source/metacall_typescript_tsx_loop_fail_test.cpp new file mode 100644 index 0000000000..54a278e394 --- /dev/null +++ b/source/tests/metacall_typescript_tsx_loop_fail_test/source/metacall_typescript_tsx_loop_fail_test.cpp @@ -0,0 +1,72 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_tsx_loop_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_tsx_loop_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *tsx_scripts[] = { + "loopfail/loopfail.tsx" + }; + + /* Load scripts */ + EXPECT_EQ((int)1, (int)metacall_load_from_file("ts", tsx_scripts, sizeof(tsx_scripts) / sizeof(tsx_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_tsx_test/CMakeLists.txt b/source/tests/metacall_typescript_tsx_test/CMakeLists.txt index 24657cfae5..53682fa434 100644 --- a/source/tests/metacall_typescript_tsx_test/CMakeLists.txt +++ b/source/tests/metacall_typescript_tsx_test/CMakeLists.txt @@ -86,7 +86,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -107,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -122,6 +131,17 @@ target_link_libraries(${target} add_test(NAME ${target} COMMAND $ + # The test must run where the tsconfig is + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/templating +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader ) # @@ -129,7 +149,7 @@ add_test(NAME ${target} # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_typescript_tsx_test/source/main.cpp b/source/tests/metacall_typescript_tsx_test/source/main.cpp index 14fb34603e..5820341294 100644 --- a/source/tests/metacall_typescript_tsx_test/source/main.cpp +++ b/source/tests/metacall_typescript_tsx_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_typescript_tsx_test/source/metacall_typescript_tsx_test.cpp b/source/tests/metacall_typescript_tsx_test/source/metacall_typescript_tsx_test.cpp index ae051f1e39..52a406737c 100644 --- a/source/tests/metacall_typescript_tsx_test/source/metacall_typescript_tsx_test.cpp +++ b/source/tests/metacall_typescript_tsx_test/source/metacall_typescript_tsx_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_tsx_test : public testing::Test { @@ -33,31 +33,30 @@ TEST_F(metacall_tsx_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* TypeScript */ - #if defined(OPTION_BUILD_LOADERS_TS) +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) { - const char * tsx_scripts[] = - { + const char *tsx_scripts[] = { "templating.tsx" }; - void * ret = NULL; + void *ret = NULL; /* Load scripts */ - EXPECT_EQ((int) 0, (int) metacall_load_from_file("ts", tsx_scripts, sizeof(tsx_scripts) / sizeof(tsx_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", tsx_scripts, sizeof(tsx_scripts) / sizeof(tsx_scripts[0]), NULL)); /* Test templating function */ ret = metacall("hello", "metaprogrammer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "

Hello $metaprogrammer

")); + EXPECT_STREQ(metacall_value_to_string(ret), "

Hello metaprogrammer

"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_TS */ +#endif /* OPTION_BUILD_LOADERS_TS */ /* Print inspect information */ { @@ -65,13 +64,13 @@ TEST_F(metacall_tsx_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -80,5 +79,5 @@ TEST_F(metacall_tsx_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_version_test/CMakeLists.txt b/source/tests/metacall_version_test/CMakeLists.txt new file mode 100644 index 0000000000..4d93e4fec3 --- /dev/null +++ b/source/tests/metacall_version_test/CMakeLists.txt @@ -0,0 +1,143 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-version-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_version_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_version_test/source/main.cpp b/source/tests/metacall_version_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_version_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_version_test/source/metacall_version_test.cpp b/source/tests/metacall_version_test/source/metacall_version_test.cpp new file mode 100644 index 0000000000..ef703c79a3 --- /dev/null +++ b/source/tests/metacall_version_test/source/metacall_version_test.cpp @@ -0,0 +1,37 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_version_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_version_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_STREQ(METACALL_VERSION, metacall_version_str()); + + /* TODO: Test other version functions */ +} diff --git a/source/tests/metacall_wasm_python_port_test/CMakeLists.txt b/source/tests/metacall_wasm_python_port_test/CMakeLists.txt new file mode 100644 index 0000000000..7233aa5796 --- /dev/null +++ b/source/tests/metacall_wasm_python_port_test/CMakeLists.txt @@ -0,0 +1,219 @@ +# Check if this loader is enabled +if(NOT (OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_WASM AND OPTION_BUILD_LOADERS_PY + AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_WASM AND OPTION_BUILD_SCRIPTS_PY + AND OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_PY)) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-wasm-python-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_wasm_python_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: Address sanitizer seems to break with WASM loading, we should either review this or instrument properly wasmtime library + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + wasm_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizers: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14007) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 alloc::alloc::alloc::h8eea693b11397046 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 (libwasmtime.so+0x823ef8) + # #2 alloc::alloc::Global::alloc_impl::hdd19e4608fbda4e9 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:166 (libwasmtime.so+0x823ef8) + # #3 _$LT$alloc..alloc..Global$u20$as$u20$core..alloc..Allocator$GT$::allocate::hf61e18fb7f49870e /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:226 (libwasmtime.so+0x823ef8) + # #4 alloc::alloc::exchange_malloc::h1859a431711d6c17 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:316 (libwasmtime.so+0x823ef8) + # #5 alloc::boxed::Box$LT$T$GT$::new::h4ef787c2f87c345b /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:185 (libwasmtime.so+0x823ef8) + # #6 _$LT$alloc..boxed..Box$LT$T$GT$$u20$as$u20$core..convert..From$LT$T$GT$$GT$::from::h26993fc6ee782f3a /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:1200 (libwasmtime.so+0x823ef8) + # #7 std::sys_common::mutex::MovableMutex::new::hf02abe706afa4cd7 library/std/src/sys_common/mutex.rs:64 (libwasmtime.so+0x823ef8) + # #8 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #9 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #10 py_loader_impl_function_type_invoke /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:2003 (libpy_loaderd.so+0xe01f) + # #11 (libpython3.9.so.1.0+0x10cc73) + # #12 py_loader_impl_load_from_file_relative /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3102 (libpy_loaderd.so+0xb5ae) + # #13 py_loader_impl_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3193 (libpy_loaderd.so+0xb7db) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:838 (libmetacalld.so+0x30944) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 metacall_wasm_python_port_test_CallWasmFromPython_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp:45 (metacall-wasm-python-port-testd+0x209c4) + # #18 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-python-port-testd+0x4df46) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 in alloc::alloc::alloc::h8eea693b11397046 + # + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14007) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 (libwasmtime.so+0x57da03) + # #2 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #3 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #4 py_loader_impl_function_type_invoke /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:2003 (libpy_loaderd.so+0xe01f) + # #5 (libpython3.9.so.1.0+0x10cc73) + # #6 py_loader_impl_load_from_file_relative /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3102 (libpy_loaderd.so+0xb5ae) + # #7 py_loader_impl_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3193 (libpy_loaderd.so+0xb7db) + # #8 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:838 (libmetacalld.so+0x30944) + # #9 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #10 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #11 metacall_wasm_python_port_test_CallWasmFromPython_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp:45 (metacall-wasm-python-port-testd+0x209c4) + # #12 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-python-port-testd+0x4df46) + # #13 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/usr/local/metacall/build/wasmtime/wasmtime-v0.28.0-x86_64-linux-c-api/lib/libwasmtime.so+0x57da03) in _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 + # + # ... + # + # + # For solving this, we should enable WASM support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/ports/rb_port/source/rb_port.c b/source/tests/metacall_wasm_python_port_test/source/main.cpp similarity index 71% rename from source/ports/rb_port/source/rb_port.c rename to source/tests/metacall_wasm_python_port_test/source/main.cpp index 31c57d7c24..ae1380d351 100644 --- a/source/ports/rb_port/source/rb_port.c +++ b/source/tests/metacall_wasm_python_port_test/source/main.cpp @@ -1,8 +1,7 @@ /* - * MetaCall SWIG Wrapper by Parra Studios - * A complete infrastructure for supporting multiple language bindings in MetaCall. + * WebAssembly Loader Tests by Parra Studios * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +17,11 @@ * */ -#include +#include -/* ... */ +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp b/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp new file mode 100644 index 0000000000..f233b6a52c --- /dev/null +++ b/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp @@ -0,0 +1,46 @@ +/* + * WebAssembly Loader Tests by Parra Studios + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_wasm_python_port_test : public testing::Test +{ +protected: + void SetUp() override + { + metacall_initialize(); + } + + void TearDown() override + { + metacall_destroy(); + } +}; + +TEST_F(metacall_wasm_python_port_test, CallWasmFromPython) +{ + const char *scripts[] = { + "wasm.py" + }; + + ASSERT_EQ(0, metacall_load_from_file("py", scripts, 1, NULL)); +} diff --git a/source/tests/metacall_wasm_test/CMakeLists.txt b/source/tests/metacall_wasm_test/CMakeLists.txt new file mode 100644 index 0000000000..6ceca66927 --- /dev/null +++ b/source/tests/metacall_wasm_test/CMakeLists.txt @@ -0,0 +1,205 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_WASM OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_WASM) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-wasm-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_wasm_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define dependencies +# + +add_dependencies(${target} + wasm_loader +) + +# +# Define test +# + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: Address sanitizer seems to break with WASM loading, + # we should either review this or instrument properly wasmtime library + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizer: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14006) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 alloc::alloc::alloc::h8eea693b11397046 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 (libwasmtime.so+0x823ef8) + # #2 alloc::alloc::Global::alloc_impl::hdd19e4608fbda4e9 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:166 (libwasmtime.so+0x823ef8) + # #3 _$LT$alloc..alloc..Global$u20$as$u20$core..alloc..Allocator$GT$::allocate::hf61e18fb7f49870e /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:226 (libwasmtime.so+0x823ef8) + # #4 alloc::alloc::exchange_malloc::h1859a431711d6c17 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:316 (libwasmtime.so+0x823ef8) + # #5 alloc::boxed::Box$LT$T$GT$::new::h4ef787c2f87c345b /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:185 (libwasmtime.so+0x823ef8) + # #6 _$LT$alloc..boxed..Box$LT$T$GT$$u20$as$u20$core..convert..From$LT$T$GT$$GT$::from::h26993fc6ee782f3a /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:1200 (libwasmtime.so+0x823ef8) + # #7 std::sys_common::mutex::MovableMutex::new::hf02abe706afa4cd7 library/std/src/sys_common/mutex.rs:64 (libwasmtime.so+0x823ef8) + # #8 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #9 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #10 metacall /usr/local/metacall/source/metacall/source/metacall.c:513 (libmetacalld.so+0x33284) + # #11 metacall_wasm_test_CallFunctions_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp:180 (metacall-wasm-testd+0x271ce) + # #12 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-testd+0x5b606) + # #13 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 in alloc::alloc::alloc::h8eea693b11397046 + # + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14006) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 (libwasmtime.so+0x57da03) + # #2 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #3 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #4 metacall /usr/local/metacall/source/metacall/source/metacall.c:513 (libmetacalld.so+0x33284) + # #5 metacall_wasm_test_CallFunctions_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp:180 (metacall-wasm-testd+0x271ce) + # #6 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-testd+0x5b606) + # #7 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/usr/local/metacall/build/wasmtime/wasmtime-v0.28.0-x86_64-linux-c-api/lib/libwasmtime.so+0x57da03) in _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 + # + # ... + # + # + # For solving this, we should enable WASM support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/ports/java_port/source/java_port.c b/source/tests/metacall_wasm_test/source/main.cpp similarity index 71% rename from source/ports/java_port/source/java_port.c rename to source/tests/metacall_wasm_test/source/main.cpp index 6e22b68d02..ae1380d351 100644 --- a/source/ports/java_port/source/java_port.c +++ b/source/tests/metacall_wasm_test/source/main.cpp @@ -1,8 +1,7 @@ /* - * MetaCall SWIG Wrapper by Parra Studios - * A complete infrastructure for supporting multiple language bindings in MetaCall. + * WebAssembly Loader Tests by Parra Studios * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +17,11 @@ * */ -#include +#include -/* ... */ +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp b/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp new file mode 100644 index 0000000000..0042e45b7c --- /dev/null +++ b/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp @@ -0,0 +1,196 @@ +/* + * WebAssembly Loader Tests by Parra Studios + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_wasm_test : public testing::Test +{ +public: +}; + +void TestFunction(void *handle, const char *name, const std::vector &expected_param_types, enum metacall_value_id expected_return_type) +{ + void *func = metacall_handle_function(handle, name); + + ASSERT_NE((void *)NULL, (void *)func); + ASSERT_EQ((size_t)expected_param_types.size(), (size_t)metacall_function_size(func)); + + for (size_t i = 0; i < expected_param_types.size(); ++i) + { + enum metacall_value_id param_type; + ASSERT_EQ((int)0, (int)metacall_function_parameter_type(func, i, ¶m_type)); + ASSERT_EQ((enum metacall_value_id)expected_param_types[i], (enum metacall_value_id)param_type); + } + + enum metacall_value_id return_type; + ASSERT_EQ((int)0, (int)metacall_function_return_type(func, &return_type)); + ASSERT_EQ((enum metacall_value_id)expected_return_type, (enum metacall_value_id)return_type); +} + +TEST_F(metacall_wasm_test, Default) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* LoadBinaryFromMemory */ + { + /* See https://webassembly.github.io/spec/core/binary/modules.html#binary-module */ + const char empty_module[] = { + 0x00, 0x61, 0x73, 0x6d, // Magic bytes + 0x01, 0x00, 0x00, 0x00 // Version + }; + ASSERT_EQ((int)0, (int)metacall_load_from_memory("wasm", empty_module, sizeof(empty_module), NULL)); + + const char invalid_module[] = { 0 }; + ASSERT_NE((int)0, (int)metacall_load_from_memory("wasm", invalid_module, sizeof(invalid_module), NULL)); + } + + /* LoadTextFromMemory */ + { + const char *empty_module = "(module)"; + ASSERT_EQ((int)0, (int)metacall_load_from_memory("wasm", empty_module, strlen(empty_module), NULL)); + + const char *invalid_module = "(invalid)"; + ASSERT_NE((int)0, (int)metacall_load_from_memory("wasm", invalid_module, strlen(invalid_module), NULL)); + } + + /* LoadFromFile */ + { + const char *empty_module_filename = "empty_module.wat"; + const char *invalid_module_filename = "invalid_module.wat"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("wasm", &empty_module_filename, 1, NULL)); + ASSERT_NE((int)0, (int)metacall_load_from_file("wasm", &invalid_module_filename, 1, NULL)); + } + + /* LoadFromPackage */ + { + ASSERT_EQ((int)0, (int)metacall_load_from_package("wasm", "empty_module.wasm", NULL)); + ASSERT_NE((int)0, (int)metacall_load_from_package("wasm", "invalid_module.wasm", NULL)); + } + + /* DiscoverFunctions & CallFunctions */ + { + const char *functions_module_filename = "functions.wat"; + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("wasm", &functions_module_filename, 1, &handle)); + + ASSERT_EQ((void *)NULL, (void *)metacall_handle_function(handle, "does_not_exist")); + + TestFunction(handle, "none_ret_none", {}, METACALL_INVALID); + TestFunction(handle, "i64_ret_none", { METACALL_LONG }, METACALL_INVALID); + TestFunction(handle, "i32_f32_i64_f64_ret_none", { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }, METACALL_INVALID); + TestFunction(handle, "none_ret_i32", {}, METACALL_INT); + TestFunction(handle, "none_ret_i32_f32_i64_f64", {}, METACALL_ARRAY); + TestFunction(handle, "i32_f32_i64_f64_ret_i32_f32_i64_f64", { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }, METACALL_ARRAY); + + void *ret = metacallht_s(handle, "none_ret_none", {}, 0); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + const enum metacall_value_id i64_ret_none_ids[] = { METACALL_LONG }; + ret = metacallht_s(handle, "i64_ret_none", i64_ret_none_ids, 1, 0L); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + const enum metacall_value_id i32_f32_i64_f64_ret_none_ids[] = { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }; + ret = metacallht_s(handle, "i32_f32_i64_f64_ret_none", i32_f32_i64_f64_ret_none_ids, 4, 0, 0.0f, 0L, 0.0); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + ret = metacallht_s(handle, "none_ret_i32", {}, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)1, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + + ret = metacallht_s(handle, "none_ret_i32_f32_i64_f64", {}, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_ARRAY, (enum metacall_value_id)metacall_value_id(ret)); + + void **values = metacall_value_to_array(ret); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(values[0])); + ASSERT_EQ((enum metacall_value_id)METACALL_FLOAT, (enum metacall_value_id)metacall_value_id(values[1])); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(values[2])); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(values[3])); + ASSERT_EQ((int)1, (int)metacall_value_to_int(values[0])); + ASSERT_EQ((float)2.0f, (float)metacall_value_to_float(values[1])); + ASSERT_EQ((long)3, (long)metacall_value_to_long(values[2])); + ASSERT_EQ((double)4.0, (double)metacall_value_to_double(values[3])); + metacall_value_destroy(ret); + + const enum metacall_value_id i32_f32_i64_f64_ret_i32_f32_i64_f64_ids[] = { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }; + ret = metacallht_s(handle, "i32_f32_i64_f64_ret_i32_f32_i64_f64", i32_f32_i64_f64_ret_i32_f32_i64_f64_ids, 4, 0, 0, 0, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_ARRAY, (enum metacall_value_id)metacall_value_id(ret)); + + values = metacall_value_to_array(ret); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(values[0])); + ASSERT_EQ((enum metacall_value_id)METACALL_FLOAT, (enum metacall_value_id)metacall_value_id(values[1])); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(values[2])); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(values[3])); + ASSERT_EQ((int)1, (int)metacall_value_to_int(values[0])); + ASSERT_EQ((float)2.0f, (float)metacall_value_to_float(values[1])); + ASSERT_EQ((long)3, (long)metacall_value_to_long(values[2])); + ASSERT_EQ((double)4.0, (double)metacall_value_to_double(values[3])); + metacall_value_destroy(ret); + +// It should exit with illegal instruction +#if defined(unix) || defined(__unix__) || defined(__unix) || \ + defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) + ASSERT_EXIT((metacallht_s(handle, "trap", {}, 0), exit(0)), ::testing::ExitedWithCode(0), ".*"); +#endif + } + + /* LinkModules */ + { + const char *modules[] = { + "exports1.wat", + "exports2.wat", + "imports.wat" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("wasm", modules, sizeof(modules) / sizeof(modules[0]), NULL)); + + void *ret = metacall("duplicate_func_i32"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)1, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + + ret = metacall("duplicate_func_i64"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((long)2, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + } + + /* InvalidLinkModules */ + { + const char *modules[] = { + "exports1.wat", + "imports.wat" + }; + + ASSERT_EQ((int)1, (int)metacall_load_from_file("wasm", modules, sizeof(modules) / sizeof(modules[0]), NULL)); + } + + metacall_destroy(); +} diff --git a/source/tests/node_loader_test/source/node_loader_test.cpp b/source/tests/node_loader_test/source/node_loader_test.cpp deleted file mode 100644 index 8cf3e8186b..0000000000 --- a/source/tests/node_loader_test/source/node_loader_test.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -class node_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(node_loader_test, DefaultConstructor) -{ - const loader_naming_tag tag = "node"; - - const loader_naming_path names[] = - { - "nod.js" - }; - - const loader_naming_path handle_names[] = - { - "nod" - }; - - const size_t size = sizeof(names) / sizeof(names[0]); - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) loader_load_from_file(tag, names, size, NULL)); - - /* TODO: Test load from memory */ - - for (size_t index = 0; index < size; ++index) - { - void * handle = loader_get_handle(tag, handle_names[index]); - - EXPECT_NE((void *) NULL, (void *) handle); - - EXPECT_EQ((int) 0, (int) loader_clear(handle)); - } - - EXPECT_EQ((int) 0, (int) loader_unload()); -} diff --git a/source/tests/portability_path_test/CMakeLists.txt b/source/tests/portability_path_test/CMakeLists.txt new file mode 100644 index 0000000000..b95e327189 --- /dev/null +++ b/source/tests/portability_path_test/CMakeLists.txt @@ -0,0 +1,136 @@ +# +# Executable name and options +# + +# Target name +set(target portability-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/portability_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::portability +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/portability_path_test/source/main.cpp b/source/tests/portability_path_test/source/main.cpp new file mode 100644 index 0000000000..24fae6af69 --- /dev/null +++ b/source/tests/portability_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/portability_path_test/source/portability_path_test.cpp b/source/tests/portability_path_test/source/portability_path_test.cpp new file mode 100644 index 0000000000..f4c638c2f0 --- /dev/null +++ b/source/tests/portability_path_test/source/portability_path_test.cpp @@ -0,0 +1,770 @@ +/* +* Loader Library by Parra Studios +* Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +* +* A library for loading executable code at run-time into a process. +* +*/ + +#include + +#include + +#include + +#define NAME_SIZE ((size_t)PORTABILITY_PATH_SIZE / 2) +#define PATH_SIZE ((size_t)PORTABILITY_PATH_SIZE) + +typedef char string_name[NAME_SIZE]; +typedef char string_path[PATH_SIZE]; + +class portability_path_test : public testing::Test +{ +public: +}; + +TEST_F(portability_path_test, portability_path_test_path_get_module_name) +{ + static const char base[] = "/a/b/c/asd.txt"; + static const char result[] = "asd"; + static const char extension[] = "txt"; + + string_name name; + + size_t size = portability_path_get_module_name(base, sizeof(base), extension, sizeof(extension), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_module_name_without_extension) +{ + static const char base[] = "/a/b/c/asd"; + static const char result[] = "asd"; + static const char extension[] = "txt"; + + string_name name; + + size_t size = portability_path_get_module_name(base, sizeof(base), extension, sizeof(extension), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_module_name_with_random_extension) +{ + static const char base[] = "/a/b/c/asd.etc.asd"; + static const char result[] = "asd.etc.asd"; + static const char extension[] = "txt"; + + string_name name; + + size_t size = portability_path_get_module_name(base, sizeof(base), extension, sizeof(extension), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_null) +{ + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(NULL, 0, name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_empty) +{ + static const char base[] = ""; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name) +{ + static const char base[] = "/a/b/c/asd.txt"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_end_dot) +{ + static const char base[] = "/a/b/c/asd."; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_without_dot) +{ + static const char base[] = "/a/b/c/asd"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_dot_in_path) +{ + static const char base[] = "/a/b.c/d/asd"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_dot_in_path_and_name) +{ + static const char base[] = "/a/b.c/d/asd.txt"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_only_separator_dot) +{ + static const char base[] = "/."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_only_dot) +{ + static const char base[] = "/a/b/c/."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_two_dots) +{ + static const char base[] = "/a/b/c/.."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_three_dots) +{ + static const char base[] = "/a/b/c/..."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_only_extension) +{ + static const char base[] = "/a/b/c/.asd"; + static const char result[] = ".asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_double_extension) +{ + static const char base[] = "/a/b/c/.asd.yes"; + static const char result[] = ".asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_triple_extension) +{ + static const char base[] = "/a/b/c/.asd.yes.no"; + static const char result[] = ".asd.yes"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_nullchar) +{ + static const char base[] = "/home/yeet/.nvm/versions/node/v18.20.3/bin/node"; + static const char result[] = "node"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_path_of_path) +{ + static const char base[] = "/a/b/c/"; + static const char result[] = "/a/b/c/"; + + string_path path; + + size_t size = portability_path_get_directory(base, sizeof(base), path, PATH_SIZE); + + EXPECT_STREQ(path, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_path_of_filepath) +{ + static const char base[] = "/a/b/c/asd"; + static const char result[] = "/a/b/c/"; + + string_path path; + + size_t size = portability_path_get_directory(base, sizeof(base), path, PATH_SIZE); + + EXPECT_STREQ(path, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_relative) +{ + static const char base[] = "/a/b/c/"; + static const char path[] = "/a/b/c/abc"; + static const char result[] = "abc"; + + string_path relative; + + size_t size = portability_path_get_relative(base, sizeof(base), path, sizeof(path), relative, PATH_SIZE); + + EXPECT_STREQ(relative, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_relative_fail) +{ + static const char base[] = "/this/is/not/shared/with/path"; + static const char path[] = "/a/b/c/abc"; + static const char result[] = "a/b/c/abc"; + + string_path relative; + + size_t size = portability_path_get_relative(base, sizeof(base), path, sizeof(path), relative, PATH_SIZE); + + EXPECT_STREQ(relative, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_none_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_left_slash) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = "e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_right_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "/e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_both_slash) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = "/e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_left_empty) +{ + static const char left[] = ""; + static const char right[] = "/e/f/g/"; + static const char result[] = "/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_right_empty) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = ""; + static const char result[] = "/a/b/c/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_right_empty_non_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = ""; + static const char result[] = "/a/b/c/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_both_empty) +{ + static const char left[] = ""; + static const char right[] = ""; + static const char result[] = ""; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_dot) +{ + static const char path[] = "./a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_double_dot) +{ + static const char path[] = "../a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_many_dot) +{ + static const char path[] = "./././././a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_many_double_dot) +{ + static const char path[] = "../../../../../a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_dot_non_slash) +{ + static const char path[] = ".a/b/c"; + static const char result[] = ".a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_many_dot_non_slash) +{ + static const char path[] = "..a/b/c"; + static const char result[] = "..a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_invalid) +{ + static const char path[] = "..././.../...../a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_double_dot) +{ + static const char path[] = "../a/b/../c"; + static const char result[] = "a/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_double_dot_all) +{ + static const char path[] = "../a/b/../../c"; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_double_dot_break) +{ + static const char path[] = "../a/b/../../../c"; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_dot) +{ + static const char path[] = "../a/./././b/./././c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_mixed_dot) +{ + static const char path[] = "../a/./././../b/././.././../c"; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_end_dot) +{ + static const char path[] = "../a/./././../b/././.././../c/."; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_end_double_dot) +{ + static const char path[] = "../a/./././b/././../c/.."; + static const char result[] = "a"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_end_mixed_dot) +{ + static const char path[] = "../a/b/c/.././../."; + static const char result[] = "a"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_absolute_end_mixed_dot) +{ + static const char path[] = "/a/b/c/../../../."; + static const char result[] = "/"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_absolute_end_dot) +{ + static const char path[] = "/."; + static const char result[] = "/"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_relative_begin_end_dot) +{ + static const char path[] = "./."; + static const char result[] = "."; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_absolute_end_many_dot) +{ + static const char path[] = "/./././"; + static const char result[] = "/"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_compare_root_equal) +{ + static const char left[] = "/"; + static const char right[] = "/"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_equal) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "/a/b/c"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_equal_trailing_slash_inverted) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = "/a/b/c"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_equal_trailing_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "/a/b/c/"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_root_empty) +{ + static const char left[] = "/"; + static const char right[] = ""; + + EXPECT_EQ((int)1, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_root_file) +{ + static const char left[] = "/a"; + static const char right[] = "/"; + + EXPECT_EQ((int)1, (int)portability_path_compare(left, right)); +} diff --git a/source/tests/preprocessor_test/CMakeLists.txt b/source/tests/preprocessor_test/CMakeLists.txt index f6533c008a..b96a639036 100644 --- a/source/tests/preprocessor_test/CMakeLists.txt +++ b/source/tests/preprocessor_test/CMakeLists.txt @@ -102,11 +102,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/preprocessor_test/source/main.cpp b/source/tests/preprocessor_test/source/main.cpp index 41de036517..ccd0ee35a6 100644 --- a/source/tests/preprocessor_test/source/main.cpp +++ b/source/tests/preprocessor_test/source/main.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/preprocessor_test/source/preprocessor_test.cpp b/source/tests/preprocessor_test/source/preprocessor_test.cpp index 16a82b5f79..4e406c3849 100644 --- a/source/tests/preprocessor_test/source/preprocessor_test.cpp +++ b/source/tests/preprocessor_test/source/preprocessor_test.cpp @@ -2,7 +2,7 @@ * Preprocssor Library by Parra Studios * A generic header-only preprocessor metaprogramming library. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,26 +18,24 @@ * */ -#include +#include #include -#include - class preprocessor_test : public testing::Test { - public: +public: }; TEST_F(preprocessor_test, arguments_comma) { - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_COMMA(1, 2, 3, 4, 5)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_COMMA(1, 2, 3, 4, 5)); - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_COMMA(1, 2)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_COMMA(1, 2)); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COMMA(1)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COMMA(1)); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COMMA()); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COMMA()); } TEST_F(preprocessor_test, arguments) @@ -46,33 +44,33 @@ TEST_F(preprocessor_test, arguments) size_t second_remove[] = { PREPROCESSOR_ARGS_SECOND_REMOVE(1, 2, 3, 4) }; - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_FIRST(1, 2, 3, 4, 5, 6)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_FIRST(1, 2, 3, 4, 5, 6)); - EXPECT_EQ((size_t) first_remove[0], (size_t) 2); + EXPECT_EQ((size_t)first_remove[0], (size_t)2); - EXPECT_EQ((size_t) first_remove[1], (size_t) 3); + EXPECT_EQ((size_t)first_remove[1], (size_t)3); - EXPECT_EQ((size_t) first_remove[2], (size_t) 4); + EXPECT_EQ((size_t)first_remove[2], (size_t)4); - EXPECT_EQ((int) 2, (int) PREPROCESSOR_ARGS_SECOND(1, 2, 3, 4, 5, 6)); + EXPECT_EQ((int)2, (int)PREPROCESSOR_ARGS_SECOND(1, 2, 3, 4, 5, 6)); - EXPECT_EQ((size_t) second_remove[0], (size_t) 1); + EXPECT_EQ((size_t)second_remove[0], (size_t)1); - EXPECT_EQ((size_t) second_remove[1], (size_t) 3); + EXPECT_EQ((size_t)second_remove[1], (size_t)3); - EXPECT_EQ((size_t) second_remove[2], (size_t) 4); + EXPECT_EQ((size_t)second_remove[2], (size_t)4); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COUNT()); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COUNT()); - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_COUNT(a)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_COUNT(a)); - EXPECT_EQ((int) 2, (int) PREPROCESSOR_ARGS_COUNT(a, b)); + EXPECT_EQ((int)2, (int)PREPROCESSOR_ARGS_COUNT(a, b)); - EXPECT_EQ((int) 3, (int) PREPROCESSOR_ARGS_COUNT(a, b, c)); + EXPECT_EQ((int)3, (int)PREPROCESSOR_ARGS_COUNT(a, b, c)); - EXPECT_EQ((int) 4, (int) PREPROCESSOR_ARGS_COUNT(a, b, c, d)); + EXPECT_EQ((int)4, (int)PREPROCESSOR_ARGS_COUNT(a, b, c, d)); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COUNT(/* ... */)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COUNT(/* ... */)); } TEST_F(preprocessor_test, bit) @@ -114,75 +112,149 @@ TEST_F(preprocessor_test, empty) { const char empty_str[] = ""; - EXPECT_EQ((int) 0, (int) strcmp(empty_str, PREPROCESSOR_STRINGIFY(PREPROCESSOR_EMPTY_SYMBOL()))); + EXPECT_STREQ("", PREPROCESSOR_STRINGIFY_OR_EMPTY(PREPROCESSOR_EMPTY_SYMBOL())); + EXPECT_STREQ(empty_str, PREPROCESSOR_STRINGIFY_OR_EMPTY(PREPROCESSOR_EMPTY_SYMBOL())); PREPROCESSOR_EMPTY_EXPANSION(this must compile) PREPROCESSOR_EMPTY_EXPANSION_VARIADIC(this, must, compile) } -TEST_F(preprocessor_test, for) +TEST_F(preprocessor_test, foreach) { - #define PREPROCESSOR_TEST_FOR_EACH_STR_SIZE 0x04 +#define PREPROCESSOR_TEST_FOR_EACH_STR_SIZE 0x04 char for_each_str[PREPROCESSOR_TEST_FOR_EACH_STR_SIZE]; - for_each_str[0] = '\0'; + memset(for_each_str, '\0', sizeof(char) * PREPROCESSOR_TEST_FOR_EACH_STR_SIZE); - #define PREPROCESSOR_TEST_FOR_EACH(expr) strncat(for_each_str, expr, PREPROCESSOR_TEST_FOR_EACH_STR_SIZE); +#define PREPROCESSOR_TEST_FOR_EACH(expr) strncat(for_each_str, expr, PREPROCESSOR_TEST_FOR_EACH_STR_SIZE - 1); PREPROCESSOR_FOR_EACH(PREPROCESSOR_TEST_FOR_EACH, "a", "b", "c") - #undef PREPROCESSOR_TEST_FOR_EACH +#undef PREPROCESSOR_TEST_FOR_EACH + +#undef PREPROCESSOR_TEST_FOR_EACH_STR_SIZE + + EXPECT_STREQ(for_each_str, "abc"); +} + +TEST_F(preprocessor_test, foreach_empty) +{ +#define PREPROCESSOR_TEST_FOR_EACH(expr) "33434" + + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_EMPTY(PREPROCESSOR_FOR_EACH(PREPROCESSOR_TEST_FOR_EACH))); + +#undef PREPROCESSOR_TEST_FOR_EACH +} - #undef PREPROCESSOR_TEST_FOR_EACH_STR_SIZE +TEST_F(preprocessor_test, for_empty) +{ +#define PREPROCESSOR_TEST_FOR(context, iterator, element) "33434" - EXPECT_EQ((int) 0, (int) strcmp(for_each_str, "abc")); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_EMPTY(PREPROCESSOR_FOR(PREPROCESSOR_TEST_FOR, "yeet"))); + +#undef PREPROCESSOR_TEST_FOR +} + +TEST_F(preprocessor_test, for) +{ +#define PREPROCESSOR_TEST_FOR(context, iterator, element) \ + context \ + PREPROCESSOR_STRINGIFY(iterator) \ + element \ + "-" + + const char for_str[] = PREPROCESSOR_FOR(PREPROCESSOR_TEST_FOR, "yeet", "a", "b", "c", "d", "e", "f"); + +#undef PREPROCESSOR_TEST_FOR + + EXPECT_STREQ(for_str, "yeet0a-yeet1b-yeet2c-yeet3d-yeet4e-yeet5f-"); } TEST_F(preprocessor_test, if) { - EXPECT_EQ((int) 0, PREPROCESSOR_IF(1, (int) 0, (int) 1)); + EXPECT_EQ((int)0, PREPROCESSOR_IF(1, (int)0, (int)1)); + + EXPECT_NE((int)0, PREPROCESSOR_IF(0, (int)0, (int)1)); +} - EXPECT_NE((int) 0, PREPROCESSOR_IF(0, (int) 0, (int) 1)); +TEST_F(preprocessor_test, if_va_args) +{ +#define PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, ...) \ + PREPROCESSOR_IF(PREPROCESSOR_ARGS_EMPTY(__VA_ARGS__), 1, 0) + + EXPECT_EQ((int)1, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, D)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, D, E)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, D, E, F)); + +#undef PREPROCESSOR_TEST_IF_VA_ARGS +} + +TEST_F(preprocessor_test, if_va_args_ext) +{ +#define PREPROCESSOR_TEST_IF_VA_ARGS_PRINT(A, B, C) \ + do \ + { \ + printf("%d %d %d\n", A, B, C); \ + } while (0) + +#define PREPROCESSOR_TEST_IF_VA_ARGS_PRINT_VA(A, B, C, ...) \ + do \ + { \ + printf(A, B, C, __VA_ARGS__); \ + } while (0) + +#define PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, ...) \ + PREPROCESSOR_IF(PREPROCESSOR_ARGS_EMPTY(__VA_ARGS__), \ + PREPROCESSOR_TEST_IF_VA_ARGS_PRINT(A, B, C), \ + PREPROCESSOR_TEST_IF_VA_ARGS_PRINT_VA(A, B, C, __VA_ARGS__)) + + PREPROCESSOR_TEST_IF_VA_ARGS(1, 2, 3); + PREPROCESSOR_TEST_IF_VA_ARGS("%s %s %s\n", "B", "C", "D"); + PREPROCESSOR_TEST_IF_VA_ARGS("%s %s %s %s\n", "B", "C", "D", "E"); + PREPROCESSOR_TEST_IF_VA_ARGS("%s %s %s %s %s\n", "B", "C", "D", "E", "F"); + +#undef PREPROCESSOR_TEST_IF_VA_ARGS } TEST_F(preprocessor_test, serial) { - #define PREPROCSSOR_TEST_SERIAL_TAG abc +#define PREPROCSSOR_TEST_SERIAL_TAG abc const char serial_id_a[] = PREPROCESSOR_STRINGIFY(PREPROCESSOR_SERIAL_ID(PREPROCESSOR_TEST_SERIAL_TAG)); const char serial_id_b[] = PREPROCESSOR_STRINGIFY(PREPROCESSOR_SERIAL_ID(PREPROCESSOR_TEST_SERIAL_TAG)); - EXPECT_NE((int) 0, (int) strcmp(serial_id_a, serial_id_b)); + EXPECT_STRNE(serial_id_a, serial_id_b); - #undef PREPROCESSOR_TEST_SERIAL_TAG +#undef PREPROCESSOR_TEST_SERIAL_TAG } TEST_F(preprocessor_test, stringify) { - #define PREPROCESSOR_TEST_STRINGIFY_TAG abc +#define PREPROCESSOR_TEST_STRINGIFY_TAG abc const char stringify_tag[] = "abc"; - EXPECT_EQ((int) 0, (int) strcmp(stringify_tag, PREPROCESSOR_STRINGIFY(PREPROCESSOR_TEST_STRINGIFY_TAG))); + EXPECT_STREQ(stringify_tag, PREPROCESSOR_STRINGIFY(PREPROCESSOR_TEST_STRINGIFY_TAG)); - EXPECT_EQ((int) 0, (int) strcmp(stringify_tag, PREPROCESSOR_STRINGIFY_VARIADIC(a, b, c))); + EXPECT_STREQ(stringify_tag, PREPROCESSOR_STRINGIFY_VARIADIC(a, b, c)); - #undef PREPROCESSOR_TEST_STRINGIFY_TAG +#undef PREPROCESSOR_TEST_STRINGIFY_TAG } TEST_F(preprocessor_test, tuple) { - EXPECT_EQ((int) 0, PREPROCESSOR_TUPLE_EXPAND(PREPROCESSOR_TUPLE_MAKE((int) 0))); + EXPECT_EQ((int)0, PREPROCESSOR_TUPLE_EXPAND(PREPROCESSOR_TUPLE_MAKE((int)0))); - PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_EQ, PREPROCESSOR_TUPLE_PREPEND((int) 1, PREPROCESSOR_TUPLE_MAKE((int) 1))); + PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_EQ, PREPROCESSOR_TUPLE_PREPEND((int)1, PREPROCESSOR_TUPLE_MAKE((int)1))); - PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_NE, PREPROCESSOR_TUPLE_APPEND((int) 1, PREPROCESSOR_TUPLE_MAKE((int) 0))); + PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_NE, PREPROCESSOR_TUPLE_APPEND((int)1, PREPROCESSOR_TUPLE_MAKE((int)0))); } TEST_F(preprocessor_test, if_with_arguments_count) { - EXPECT_EQ((int) 1, (int) PREPROCESSOR_IF(PREPROCESSOR_ARGS_COUNT(a, b, c), 1, 0)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_IF(PREPROCESSOR_ARGS_COUNT(a, b, c), 1, 0)); } diff --git a/source/tests/py_django_integration_test/CMakeLists.txt b/source/tests/py_django_integration_test/CMakeLists.txt deleted file mode 100644 index ccf37dbe42..0000000000 --- a/source/tests/py_django_integration_test/CMakeLists.txt +++ /dev/null @@ -1,247 +0,0 @@ -# Check if python loader and distributable libs are enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_DIST_LIBS) - return() -endif() - -# -# External dependencies -# - -find_package(PythonLibs 3 REQUIRED) - -if(NOT PYTHONLIBS_FOUND) - message(STATUS "Python libraries not found") - return() -endif() - -set(Python_ADDITIONAL_VERSIONS 3.7) - -find_package(PythonInterp REQUIRED) - -if(NOT PYTHONINTERP_FOUND) - message(STATUS "Python interpreter not found") - return() -endif() - -# Detect if Django is available -execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "import django" - RESULT_VARIABLE PYTHON_DJANGO_FOUND - ERROR_QUIET - OUTPUT_QUIET -) - -if("${PYTHON_DJANGO_FOUND}" EQUAL "1") - message(WARNING "Python Django not found, skipping the python djangi integration test project") - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target py-django-integration-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/py_django_integration_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Dependecies -# - -# Add metacall distributable dependency -add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable -) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - get_target_property(DEBUG_POSTFIX ${target} "DEBUG_POSTFIX") - set(TEST_SCRIPT_LAUNCHER_BASE_NAME - ${target}${DEBUG_POSTFIX} - ) -else() - set(TEST_SCRIPT_LAUNCHER_BASE_NAME - ${target} - ) -endif() - -set(TEST_SCRIPT_LAUNCHER_NAME - ${TEST_SCRIPT_LAUNCHER_BASE_NAME}.py -) - -set(TEST_SCRIPT_LAUNCHER_CLIENT_NAME - ${TEST_SCRIPT_LAUNCHER_BASE_NAME}-client.py -) - -set(TEST_SCRIPT_LAUNCHER_FILE_NAME - ${TEST_SCRIPT_LAUNCHER_BASE_NAME}-file.txt -) - -set(TEST_SCRIPT_LAUNCHER_NAME_IN - "data/test.py.in" -) - -set(TEST_SCRIPT_LAUNCHER_CLIENT_NAME_IN - "data/client.py.in" -) - -set(TEST_SCRIPT_LAUNCHER_FILE_NAME_IN - "data/django_file.txt" -) - -# -# Configure test files -# - -if(MSVC) - set(DETOUR_LIBRARY_PATH "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - set(SERIAL_LIBRARY_PATH "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - set(LOADER_LIBRARY_PATH "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - set(TEST_SCRIPT_LAUNCHER_APP "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${TEST_SCRIPT_LAUNCHER_BASE_NAME}.exe") -else() - set(DETOUR_LIBRARY_PATH "${PROJECT_BINARY_DIR}") - set(SERIAL_LIBRARY_PATH "${PROJECT_BINARY_DIR}") - set(LOADER_LIBRARY_PATH "${PROJECT_BINARY_DIR}") - set(TEST_SCRIPT_LAUNCHER_APP "${PROJECT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_BASE_NAME}") -endif() - -configure_file(${TEST_SCRIPT_LAUNCHER_NAME_IN} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_NAME}) - -configure_file(${TEST_SCRIPT_LAUNCHER_CLIENT_NAME_IN} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_CLIENT_NAME}) - -configure_file(${TEST_SCRIPT_LAUNCHER_FILE_NAME_IN} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_FILE_NAME} COPYONLY) - -if(NOT ${PYTHON_VERSION_MAJOR} EQUAL 3) - message(WARNING "Invalid Python executable version: ${PYTHON_VERSION_STRING}") - message(WARNING "Define a valid Python 3 executable in variable PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} or ${target} test won't be compiled") - message(WARNING "Use: \"sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10\" to modify default python executable") - return() -endif() - -add_test(NAME ${target} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_NAME} -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/py_django_integration_test/data/client.py.in b/source/tests/py_django_integration_test/data/client.py.in deleted file mode 100755 index 1cc4173fbe..0000000000 --- a/source/tests/py_django_integration_test/data/client.py.in +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 - -import unittest -import time -import urllib.request -import requests -import signal -import os - -class py_django_integration_client_test(unittest.TestCase): - def test_py_django_client(self): - - print('Client: Waiting for python django server ...'); - - time.sleep(3); # Fake reactive behavior - - print('Client: Sending GET request to index'); - - request = urllib.request.urlopen('http://127.0.0.1:8080/').read(); - - print('Client: Request { ' + str(request) + ' }'); - - self.assertEqual(request, b'hello world'); - - print('Client: Sending POST request to media'); - - file_handle = open('${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_FILE_NAME@'); - - files = { 'file': file_handle }; - - request = requests.post('http://127.0.0.1:8080/media/', files = files); - - file_handle.close(); - - print('Client: Request { ' + str(request) + ' }'); - - self.assertEqual(request.content, b'123456'); - - print('Client: Killing python django server (${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_BASE_NAME@)'); - - request = urllib.request.urlopen('http://127.0.0.1:8080/pid/').read(); - - print('Client: Request { ' + str(request) + ' }'); - - os.kill(int(request), signal.SIGINT); - - print('Server: Python django server killed gracefully'); - -if __name__ == '__main__': - unittest.main() diff --git a/source/tests/py_django_integration_test/data/django_file.txt b/source/tests/py_django_integration_test/data/django_file.txt deleted file mode 100644 index 4632e068d5..0000000000 --- a/source/tests/py_django_integration_test/data/django_file.txt +++ /dev/null @@ -1 +0,0 @@ -123456 \ No newline at end of file diff --git a/source/tests/py_django_integration_test/data/test.py.in b/source/tests/py_django_integration_test/data/test.py.in deleted file mode 100755 index 249c56b9c7..0000000000 --- a/source/tests/py_django_integration_test/data/test.py.in +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 - - -import os - -os.environ['DETOUR_LIBRARY_PATH'] = '@DETOUR_LIBRARY_PATH@' -os.environ['SERIAL_LIBRARY_PATH'] = '@SERIAL_LIBRARY_PATH@' -os.environ['CONFIGURATION_PATH'] = '@CONFIGURATION_PATH@' -os.environ['LOADER_LIBRARY_PATH'] = '@LOADER_LIBRARY_PATH@' -os.environ['LOADER_SCRIPT_PATH'] = '@LOADER_SCRIPT_PATH@' - -import unittest -import signal -import subprocess - -def sigint_handler(signal, frame): - print('Server: SIGINT signal from client to server (Ctrl-C)'); - - -class py_django_integration_test(unittest.TestCase): - - def test_py_django_server(self): - - signal.signal(signal.SIGINT, sigint_handler); - - print('Server: Launching python django client in background (${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_CLIENT_NAME@)'); - - django_client = subprocess.Popen(['${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_CLIENT_NAME@']); - - print('Server: Launching python django server (@TEST_SCRIPT_LAUNCHER_APP@)'); - - django_server = subprocess.Popen(['@TEST_SCRIPT_LAUNCHER_APP@'], env = os.environ.copy(), stderr = subprocess.STDOUT); - - django_client_output = django_client.communicate()[0]; - - print(django_client_output); - - self.assertEqual(django_client.poll(), 0); - - print('Server: Python django client finished gracefully'); - -if __name__ == '__main__': - unittest.main() diff --git a/source/tests/py_loader_port_test/source/py_loader_port_test.cpp b/source/tests/py_loader_port_test/source/py_loader_port_test.cpp deleted file mode 100644 index c14633b968..0000000000 --- a/source/tests/py_loader_port_test/source/py_loader_port_test.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading python code at run-time into a process. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -class py_loader_port_test : public testing::Test -{ -public: -}; - -void * callback_host(size_t argc, void * args[], void * data) -{ - const char * str = metacall_value_cast_string(&args[0]); - - (void)argc; - (void)data; - - printf("Host callback: %s\n", str); - - EXPECT_EQ((int) 0, (int) strcmp(str, "some text")); - - return metacall_value_create_int(25); -} - -TEST_F(py_loader_port_test, DefaultConstructor) -{ - metacall_print_info(); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* Native register */ - { - metacall_register("callback_host_impl", callback_host, NULL, METACALL_INT, 1, METACALL_STRING); - - EXPECT_NE((void *) NULL, (void *) metacall_function("callback_host_impl")); - } - - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) - { - const char * rb_scripts[] = - { - "hello.rb", "second.rb" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); - - ret = metacall("say_multiply", 5, 7); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 35, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - - ret = metacall("say_null"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); - - metacall_value_destroy(ret); - - ret = metacall("say_hello", "meta-programmer"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "Hello meta-programmer!")); - - metacall_value_destroy(ret); - - ret = metacall("get_second", 5, 12); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 12, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_RB */ - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) - { - const char * py_scripts[] = - { - "callback.py" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - - ret = metacall("hello_world", "some text"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 25, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - - ret = metacall("hello_ruby", 12, 4); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 48, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_PY */ - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/py_loader_test/source/py_loader_test.cpp b/source/tests/py_loader_test/source/py_loader_test.cpp deleted file mode 100644 index 0d28989bc3..0000000000 --- a/source/tests/py_loader_test/source/py_loader_test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading python code at run-time into a process. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -class py_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(py_loader_test, DefaultConstructor) -{ - const loader_naming_path names[] = - { - "example.py" - }; - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, loader_load_from_file("py", names, sizeof(names) / sizeof(names[0]), NULL)); - - EXPECT_EQ((int) 0, loader_unload()); -} diff --git a/source/tests/rb_loader_parser_integration_test/source/rb_loader_parser_integration_test.cpp b/source/tests/rb_loader_parser_integration_test/source/rb_loader_parser_integration_test.cpp deleted file mode 100644 index d403f90380..0000000000 --- a/source/tests/rb_loader_parser_integration_test/source/rb_loader_parser_integration_test.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include - -#include - -class rb_loader_parser_integration_test : public testing::Test -{ -public: -}; - -TEST_F(rb_loader_parser_integration_test, DefaultConstructor) -{ - metacall_print_info(); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) - { - const char * rb_scripts[] = - { - "cache.rb" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); - - ret = metacall("cache_initialize"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); - - metacall_value_destroy(ret); - - ret = metacall("cache_set", "meta", "call"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((enum metacall_value_id) METACALL_NULL, (enum metacall_value_id) metacall_value_id(ret)); - - metacall_value_destroy(ret); - - ret = metacall("cache_has_key","meta"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_NE((int) 0, (int) metacall_value_to_bool(ret)); - - metacall_value_destroy(ret); - - ret = metacall("cache_get", "meta"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "call")); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_RB */ - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/rb_loader_parser_test/CMakeLists.txt b/source/tests/rb_loader_parser_test/CMakeLists.txt index a48e43aebd..af3df47fee 100644 --- a/source/tests/rb_loader_parser_test/CMakeLists.txt +++ b/source/tests/rb_loader_parser_test/CMakeLists.txt @@ -33,6 +33,10 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp ${source_path}/rb_loader_parser_test.cpp + + # Add directly the sources to be tested, Ruby Loader is a MODULE and it cannot be linked + ${CMAKE_SOURCE_DIR}/source/loaders/rb_loader/include/rb_loader/rb_loader_impl_parser.h + ${CMAKE_SOURCE_DIR}/source/loaders/rb_loader/source/rb_loader_impl_parser.c ) # Group source files @@ -73,6 +77,10 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + # Ruby Loader headers + ${CMAKE_BINARY_DIR}/source/loaders/rb_loader/include + ${CMAKE_SOURCE_DIR}/source/loaders/rb_loader/include ) # @@ -87,10 +95,13 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::memory + ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::rb_loader ) # @@ -111,11 +122,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -128,6 +148,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + # # Define test properties # diff --git a/source/tests/rb_loader_parser_test/source/main.cpp b/source/tests/rb_loader_parser_test/source/main.cpp index 95d7f4b83f..37d4adc23f 100644 --- a/source/tests/rb_loader_parser_test/source/main.cpp +++ b/source/tests/rb_loader_parser_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading ruby code at run-time into a process. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp b/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp index a19e190788..a8984ff47f 100644 --- a/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp +++ b/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp @@ -1,12 +1,12 @@ /* * Loader Library by Parra Studios - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A plugin for loading ruby code at run-time into a process. * */ -#include +#include #include @@ -14,7 +14,7 @@ class rb_loader_parser_test : public testing::Test { - protected: +protected: }; TEST_F(rb_loader_parser_test, DefaultConstructor) @@ -66,17 +66,17 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) rb_function_parser function_parser; - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); function_map = set_create(&hash_callback_str, &comparable_callback_str); - EXPECT_NE((set) NULL, (set) function_map); + EXPECT_NE((set)NULL, (set)function_map); - EXPECT_EQ((int) 0, (int) rb_loader_impl_key_parse(script, function_map)); + EXPECT_EQ((int)0, (int)rb_loader_impl_key_parse(script, function_map)); rb_loader_impl_key_print(function_map); @@ -86,15 +86,15 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_nothing); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_nothing, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_nothing, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 1, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)1, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_get */ @@ -103,15 +103,15 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_get); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_get, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_get, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 1, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)1, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_set */ @@ -120,19 +120,19 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_set); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_set, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_set, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 2, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)2, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); - EXPECT_EQ((int) 1, (int) function_parser->params[1].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[1].name, "value", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[1].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)1, (int)function_parser->params[1].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[1].name, "value", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[1].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_has_key */ @@ -141,15 +141,15 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_has_key); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_has_key, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_has_key, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 1, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)1, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_initialize */ @@ -158,14 +158,12 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_initialize); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_initialize, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_initialize, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 0, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)0, (size_t)function_parser->params_size); } - EXPECT_EQ((int) 0, (int) rb_loader_impl_key_clear(function_map)); - - set_destroy(function_map); + rb_loader_impl_key_clear(function_map); } diff --git a/source/tests/rb_loader_test/source/rb_loader_test.cpp b/source/tests/rb_loader_test/source/rb_loader_test.cpp deleted file mode 100644 index f211768424..0000000000 --- a/source/tests/rb_loader_test/source/rb_loader_test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -class rb_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(rb_loader_test, DefaultConstructor) -{ - const loader_naming_path names[] = - { - "hello.rb" - }; - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) loader_load_from_file("rb", names, sizeof(names) / sizeof(names[0]), NULL)); - - EXPECT_EQ((int) 0, (int) loader_unload()); -} diff --git a/source/tests/rb_rails_integration_test/source/main.cpp b/source/tests/rb_rails_integration_test/source/main.cpp deleted file mode 100644 index 14fb34603e..0000000000 --- a/source/tests/rb_rails_integration_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/reflect_function_test/CMakeLists.txt b/source/tests/reflect_function_test/CMakeLists.txt index c7f82fd07c..fd1875cd36 100644 --- a/source/tests/reflect_function_test/CMakeLists.txt +++ b/source/tests/reflect_function_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,17 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -111,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_function_test/source/main.cpp b/source/tests/reflect_function_test/source/main.cpp index 5dab5b82e0..cba58797ca 100644 --- a/source/tests/reflect_function_test/source/main.cpp +++ b/source/tests/reflect_function_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_function_test/source/reflect_function_test.cpp b/source/tests/reflect_function_test/source/reflect_function_test.cpp index 99d06981bf..7567f4505c 100644 --- a/source/tests/reflect_function_test/source/reflect_function_test.cpp +++ b/source/tests/reflect_function_test/source/reflect_function_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -41,9 +41,9 @@ typedef struct function_impl_example_type } * function_impl_example; -void function_example(char c, int i, void * p) +void function_example(char c, int i, void *p) { - struct example_arg_type * e = (struct example_arg_type *)p; + struct example_arg_type *e = (struct example_arg_type *)p; log_write("metacall", LOG_LEVEL_DEBUG, "char: %c; int: %d; ptr: %p", c, i, p); @@ -78,7 +78,7 @@ function_return function_example_interface_invoke(function func, function_impl f return NULL; } -function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void * context) +function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void *context) { /* TODO */ @@ -102,8 +102,7 @@ void function_example_interface_destroy(function func, function_impl func_impl) function_interface function_example_singleton() { - static struct function_interface_type example_interface = - { + static struct function_interface_type example_interface = { &function_example_interface_create, &function_example_interface_invoke, &function_example_interface_await, @@ -115,24 +114,24 @@ function_interface function_example_singleton() class reflect_function_test : public testing::Test { - public: +public: }; TEST_F(reflect_function_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); type int_type = type_create(TYPE_INT, "int", NULL, NULL); type ptr_type = type_create(TYPE_PTR, "ptr", NULL, NULL); - EXPECT_NE((type) char_type, (type) NULL); - EXPECT_NE((type) int_type, (type) NULL); - EXPECT_NE((type) ptr_type, (type) NULL); + EXPECT_NE((type)char_type, (type)NULL); + EXPECT_NE((type)int_type, (type)NULL); + EXPECT_NE((type)ptr_type, (type)NULL); if (char_type != NULL && int_type != NULL && ptr_type != NULL) { @@ -146,6 +145,8 @@ TEST_F(reflect_function_test, DefaultConstructor) if (f != NULL) { + EXPECT_EQ((int)function_increment_reference(f), (int)0); + signature_set(function_signature(f), 0, "c", char_type); signature_set(function_signature(f), 1, "i", int_type); signature_set(function_signature(f), 2, "p", ptr_type); diff --git a/source/tests/reflect_metadata_test/CMakeLists.txt b/source/tests/reflect_metadata_test/CMakeLists.txt index 5d5e204ad9..33497b513c 100644 --- a/source/tests/reflect_metadata_test/CMakeLists.txt +++ b/source/tests/reflect_metadata_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,17 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -111,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_metadata_test/source/main.cpp b/source/tests/reflect_metadata_test/source/main.cpp index 5dab5b82e0..cba58797ca 100644 --- a/source/tests/reflect_metadata_test/source/main.cpp +++ b/source/tests/reflect_metadata_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp b/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp index e2995a9ba3..6c1bc10f7a 100644 --- a/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp +++ b/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -42,9 +42,9 @@ typedef struct function_impl_example_type } * function_impl_example; -void function_example(char c, int i, void * p) +void function_example(char c, int i, void *p) { - struct example_arg_type * e = (struct example_arg_type *)p; + struct example_arg_type *e = (struct example_arg_type *)p; log_write("metacall", LOG_LEVEL_DEBUG, "char: %c; int: %d; ptr: %p", c, i, p); @@ -79,7 +79,7 @@ function_return function_example_interface_invoke(function func, function_impl f return NULL; } -function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void * context) +function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void *context) { /* TODO */ @@ -103,8 +103,7 @@ void function_example_interface_destroy(function func, function_impl func_impl) function_interface function_example_singleton() { - static struct function_interface_type example_interface = - { + static struct function_interface_type example_interface = { &function_example_interface_create, &function_example_interface_invoke, &function_example_interface_await, @@ -116,32 +115,32 @@ function_interface function_example_singleton() class reflect_metadata_test : public testing::Test { - public: +public: }; TEST_F(reflect_metadata_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); // Initialize serial - EXPECT_EQ((int) 0, (int) serial_initialize()); + EXPECT_EQ((int)0, (int)serial_initialize()); // Create serial serial s = serial_create("rapid_json"); - ASSERT_NE((serial) NULL, (serial) s); + ASSERT_NE((serial)NULL, (serial)s); type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); type int_type = type_create(TYPE_INT, "int", NULL, NULL); type ptr_type = type_create(TYPE_PTR, "ptr", NULL, NULL); - EXPECT_NE((type) char_type, (type) NULL); - EXPECT_NE((type) int_type, (type) NULL); - EXPECT_NE((type) ptr_type, (type) NULL); + EXPECT_NE((type)char_type, (type)NULL); + EXPECT_NE((type)int_type, (type)NULL); + EXPECT_NE((type)ptr_type, (type)NULL); if (char_type != NULL && int_type != NULL && ptr_type != NULL) { @@ -149,13 +148,15 @@ TEST_F(reflect_metadata_test, DefaultConstructor) function_impl_example example_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); - char * str; + char *str; size_t size; f = function_create("example", 3, example_impl, &function_example_singleton); - EXPECT_NE((function) f, (function) NULL); + EXPECT_NE((function)f, (function)NULL); + + EXPECT_EQ((int)function_increment_reference(f), (int)0); if (f != NULL) { @@ -201,7 +202,7 @@ TEST_F(reflect_metadata_test, DefaultConstructor) } // Clear serial - EXPECT_EQ((int) 0, (int) serial_clear(s)); + EXPECT_EQ((int)0, (int)serial_clear(s)); // Destroy serial serial_destroy(); diff --git a/source/tests/reflect_object_class_test/CMakeLists.txt b/source/tests/reflect_object_class_test/CMakeLists.txt new file mode 100644 index 0000000000..3a74590dc5 --- /dev/null +++ b/source/tests/reflect_object_class_test/CMakeLists.txt @@ -0,0 +1,150 @@ +# +# Executable name and options +# + +# Target name +set(target reflect-object-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/reflect_object_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/reflect_object_class_test/source/main.cpp b/source/tests/reflect_object_class_test/source/main.cpp new file mode 100644 index 0000000000..cba58797ca --- /dev/null +++ b/source/tests/reflect_object_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Reflect Library by Parra Studios + * A library for provide reflection and metadata representation. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/reflect_object_class_test/source/reflect_object_class_test.cpp b/source/tests/reflect_object_class_test/source/reflect_object_class_test.cpp new file mode 100644 index 0000000000..1abd68347d --- /dev/null +++ b/source/tests/reflect_object_class_test/source/reflect_object_class_test.cpp @@ -0,0 +1,576 @@ +/* + * Reflect Library by Parra Studios + * A library for provide reflection and metadata representation. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +typedef struct hello_world_class_type +{ + // These are static attributes that belong to the class + int a; + float b; + char c[10]; + +} * hello_world_class; + +typedef struct hello_world_object_type +{ + // These are attributes that belong to the object + char d; + long e; + +} * hello_world_object; + +int hello_world_object_impl_interface_create(object obj, object_impl impl) +{ + hello_world_object hello_world = (hello_world_object)impl; + + (void)obj; + + EXPECT_NE((void *)NULL, (void *)hello_world); + + // Default values for static attributes (this will be done automatically by the language runtime) + hello_world->d = 'd'; + hello_world->e = 55; + + return 0; +} + +value hello_world_object_impl_interface_get(object obj, object_impl impl, struct accessor_type *accessor) +{ + hello_world_object hello_world = (hello_world_object)impl; + attribute attr = accessor->data.attr; + const char *key = attribute_name(attr); + + (void)obj; + + // Horrible but it is just a ilustrative example + if (strcmp(key, "d") == 0) + { + return value_create_char(hello_world->d); + } + else if (strcmp(key, "e") == 0) + { + return value_create_long(hello_world->e); + } + + return NULL; +} + +int hello_world_object_impl_interface_set(object obj, object_impl impl, struct accessor_type *accessor, value v) +{ + hello_world_object hello_world = (hello_world_object)impl; + attribute attr = accessor->data.attr; + const char *key = attribute_name(attr); + + EXPECT_NE((void *)NULL, (void *)hello_world); + + (void)obj; + + // Horrible but it is just a ilustrative example + if (strcmp(key, "d") == 0) + { + hello_world->d = value_to_char(v); + } + else if (strcmp(key, "e") == 0) + { + hello_world->e = value_to_long(v); + } + + return 0; +} + +value hello_world_object_impl_interface_method_invoke(object obj, object_impl impl, method m, object_args args, size_t size) +{ + // TODO: Maybe we can improve this with other methods and arguments like in reflect_function_test + static const char str[] = "Hello World"; + + (void)obj; + (void)impl; + (void)m; + (void)args; + (void)size; + + return value_create_string(str, sizeof(str) - 1); +} + +value hello_world_object_impl_interface_method_await(object obj, object_impl impl, method m, object_args args, size_t size, object_resolve_callback resolve, object_reject_callback reject, void *ctx) +{ + // TODO + (void)obj; + (void)impl; + (void)m; + (void)args; + (void)size; + (void)resolve; + (void)reject; + (void)ctx; + + return NULL; +} + +int hello_world_object_impl_interface_destructor(object obj, object_impl impl) +{ + (void)obj; + (void)impl; + + return 0; +} + +void hello_world_object_impl_interface_destroy(object obj, object_impl impl) +{ + hello_world_object hello_world_obj = static_cast(impl); + + (void)obj; + + delete hello_world_obj; +} + +object_interface hello_world_object_impl_interface_singleton() +{ + static struct object_interface_type hello_world_interface + { + &hello_world_object_impl_interface_create, + &hello_world_object_impl_interface_get, + &hello_world_object_impl_interface_set, + &hello_world_object_impl_interface_method_invoke, + &hello_world_object_impl_interface_method_await, + &hello_world_object_impl_interface_destructor, + &hello_world_object_impl_interface_destroy + }; + + return &hello_world_interface; +} + +int hello_world_class_impl_interface_create(klass cls, class_impl impl) +{ + hello_world_class hello_world = (hello_world_class)impl; + + (void)cls; + + EXPECT_NE((void *)NULL, (void *)hello_world); + + // Default values for static attributes (this will be done automatically by the language runtime) + hello_world->a = 0; + hello_world->b = 0.0f; + hello_world->c[0] = '\0'; + + return 0; +} + +object hello_world_class_impl_interface_constructor(klass cls, class_impl impl, const char *name, constructor ctor, class_args args, size_t size) +{ + hello_world_object hello_world_obj = new hello_world_object_type(); + + (void)impl; + (void)ctor; + + object obj = object_create(name, ACCESSOR_TYPE_STATIC, hello_world_obj, &hello_world_object_impl_interface_singleton, cls); + + if (object_increment_reference(obj) != 0) + { + /* TODO: Abort? */ + } + + if (args == 0) + { + // Default constructor + hello_world_obj->d = 'A'; + hello_world_obj->e = 0L; + } + else if (size == 2) + { + hello_world_obj->d = value_to_char(args[0]); + hello_world_obj->e = value_to_long(args[1]); + } + + return obj; +} + +value hello_world_class_impl_interface_static_get(klass cls, class_impl impl, struct accessor_type *accessor) +{ + hello_world_class hello_world = (hello_world_class)impl; + attribute attr = accessor->data.attr; + char *key = attribute_name(attr); + + (void)cls; + + if (key == NULL) + { + return NULL; + } + + // Horrible but it is just a ilustrative example + if (strcmp(key, "a") == 0) + { + return value_create_int(hello_world->a); + } + else if (strcmp(key, "b") == 0) + { + return value_create_float(hello_world->b); + } + else if (strcmp(key, "c") == 0) + { + return value_create_string(hello_world->c, 9); + } + + return NULL; +} + +int hello_world_class_impl_interface_static_set(klass cls, class_impl impl, struct accessor_type *accessor, value v) +{ + hello_world_class hello_world = (hello_world_class)impl; + attribute attr = accessor->data.attr; + + EXPECT_NE((void *)NULL, (void *)hello_world); + + (void)cls; + + char *key = attribute_name(attr); + + if (key == NULL) + { + return 1; + } + + // Horrible but it is just a ilustrative example + if (strcmp(key, "a") == 0) + { + hello_world->a = value_to_int(v); + } + else if (strcmp(key, "b") == 0) + { + hello_world->b = value_to_float(v); + } + else if (strcmp(key, "c") == 0) + { + strncpy(hello_world->c, value_to_string(v), 9); + } + + return 0; +} + +value hello_world_class_impl_interface_static_invoke(klass cls, class_impl impl, method m, class_args args, size_t argc) +{ + // TODO + (void)cls; + (void)impl; + (void)m; + (void)args; + (void)argc; + + return NULL; +} + +value hello_world_class_impl_interface_static_await(klass cls, class_impl impl, method m, class_args args, size_t size, class_resolve_callback resolve, class_reject_callback reject, void *ctx) +{ + // TODO + (void)cls; + (void)impl; + (void)m; + (void)args; + (void)size; + (void)resolve; + (void)reject; + (void)ctx; + + return NULL; +} + +void hello_world_class_impl_interface_destroy(klass cls, class_impl impl) +{ + hello_world_class hellow_world_cls = static_cast(impl); + + (void)cls; + + delete hellow_world_cls; +} + +class_interface hello_world_class_impl_interface_singleton() +{ + static struct class_interface_type hello_world_interface + { + &hello_world_class_impl_interface_create, + &hello_world_class_impl_interface_constructor, + &hello_world_class_impl_interface_static_get, + &hello_world_class_impl_interface_static_set, + &hello_world_class_impl_interface_static_invoke, + &hello_world_class_impl_interface_static_await, + &hello_world_class_impl_interface_destroy + }; + + return &hello_world_interface; +} + +class reflect_object_class_test : public testing::Test +{ +public: +}; + +TEST_F(reflect_object_class_test, DefaultConstructor) +{ + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + // Create class + hello_world_class hellow_world_cls = new hello_world_class_type(); + + EXPECT_NE((void *)NULL, (void *)hellow_world_cls); + + klass cls = class_create("HelloWorld", ACCESSOR_TYPE_STATIC, hellow_world_cls, &hello_world_class_impl_interface_singleton); + + EXPECT_EQ((int)class_increment_reference(cls), (int)0); + + // Register constructors + constructor default_ctor = constructor_create(0, VISIBILITY_PUBLIC); + constructor custom_ctor = constructor_create(2, VISIBILITY_PUBLIC); + + ASSERT_NE((constructor)NULL, (constructor)default_ctor); + ASSERT_NE((constructor)NULL, (constructor)custom_ctor); + + type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); + type long_type = type_create(TYPE_LONG, "long", NULL, NULL); + type str_type = type_create(TYPE_STRING, "string", NULL, NULL); + type int_type = type_create(TYPE_INT, "int", NULL, NULL); + type float_type = type_create(TYPE_FLOAT, "float", NULL, NULL); + + ASSERT_NE((type)NULL, (type)char_type); + ASSERT_NE((type)NULL, (type)long_type); + ASSERT_NE((type)NULL, (type)str_type); + ASSERT_NE((type)NULL, (type)int_type); + ASSERT_NE((type)NULL, (type)float_type); + + constructor_set(custom_ctor, 0, "c", char_type); + constructor_set(custom_ctor, 1, "l", long_type); + + EXPECT_EQ((int)0, (int)class_register_constructor(cls, default_ctor)); + EXPECT_EQ((int)0, (int)class_register_constructor(cls, custom_ctor)); + + // Register methods + method test_func_method = method_create(cls, "test_func", 0, NULL, VISIBILITY_PUBLIC, SYNCHRONOUS, NULL); + + ASSERT_NE((method)NULL, (method)test_func_method); + + signature s = method_signature(test_func_method); + + signature_set_return(s, str_type); + + EXPECT_EQ((int)0, (int)class_register_method(cls, test_func_method)); + + // Register static attributes + attribute a_attr = attribute_create(cls, "a", int_type, NULL, VISIBILITY_PUBLIC, NULL); + attribute b_attr = attribute_create(cls, "b", float_type, NULL, VISIBILITY_PUBLIC, NULL); + attribute c_attr = attribute_create(cls, "c", str_type, NULL, VISIBILITY_PUBLIC, NULL); + + ASSERT_NE((attribute)NULL, (attribute)a_attr); + ASSERT_NE((attribute)NULL, (attribute)b_attr); + ASSERT_NE((attribute)NULL, (attribute)c_attr); + + EXPECT_EQ((int)0, (int)class_register_static_attribute(cls, a_attr)); + EXPECT_EQ((int)0, (int)class_register_static_attribute(cls, b_attr)); + EXPECT_EQ((int)0, (int)class_register_static_attribute(cls, c_attr)); + + // Register attributes + attribute d_attr = attribute_create(cls, "d", char_type, NULL, VISIBILITY_PUBLIC, NULL); + attribute e_attr = attribute_create(cls, "e", long_type, NULL, VISIBILITY_PUBLIC, NULL); + + ASSERT_NE((attribute)NULL, (attribute)d_attr); + ASSERT_NE((attribute)NULL, (attribute)e_attr); + + EXPECT_EQ((int)0, (int)class_register_attribute(cls, d_attr)); + EXPECT_EQ((int)0, (int)class_register_attribute(cls, e_attr)); + + // Get and set static attributes from the class + { + value a = class_static_get(cls, "a"); + ASSERT_NE((value)NULL, (value)a); + EXPECT_EQ((int)0, (int)value_to_int(a)); + value_type_destroy(a); + + value b = class_static_get(cls, "b"); + ASSERT_NE((value)NULL, (value)b); + EXPECT_EQ((float)0.0f, (float)value_to_float(b)); + value_type_destroy(b); + + value c = class_static_get(cls, "c"); + ASSERT_NE((value)NULL, (value)c); + EXPECT_EQ((char)'\0', (char)*value_to_string(c)); + value_type_destroy(c); + + value new_a = value_create_int(1234); + ASSERT_EQ((int)0, (int)class_static_set(cls, "a", new_a)); + a = class_static_get(cls, "a"); + EXPECT_NE((value)NULL, (value)a); + EXPECT_EQ((int)1234, (int)value_to_int(a)); + value_type_destroy(a); + value_type_destroy(new_a); + + value new_c = value_create_string("hi", 2); + + ASSERT_EQ((int)0, (int)class_static_set(cls, "c", new_c)); + c = class_static_get(cls, "c"); + EXPECT_NE((value)NULL, (value)c); + EXPECT_EQ((char)'h', (char)value_to_string(c)[0]); + EXPECT_EQ((char)'i', (char)value_to_string(c)[1]); + value_type_destroy(c); + value_type_destroy(new_c); + } + + // Create object (default constructor) + { + value args[] = { + NULL + }; + + type_id ctor_ids[] = { + TYPE_INVALID + }; + + constructor ctor = class_constructor(cls, ctor_ids, 0); + + ASSERT_EQ((constructor)ctor, (constructor)default_ctor); + + object obj = class_new(cls, "helloWorldObj", ctor, args, 0); + ASSERT_NE((object)NULL, (object)obj); + + // Get & set attributes from object + { + value d = object_get(obj, "d"); + ASSERT_NE((value)NULL, (value)d); + EXPECT_EQ((char)'d', (char)value_to_char(d)); + value_type_destroy(d); + + value e = object_get(obj, "e"); + ASSERT_NE((value)NULL, (value)e); + EXPECT_EQ((long)55L, (long)value_to_long(e)); + value_type_destroy(e); + + value new_d = value_create_char('M'); + ASSERT_EQ((char)0, (char)object_set(obj, "d", new_d)); + d = object_get(obj, "d"); + EXPECT_NE((value)NULL, (value)d); + EXPECT_EQ((char)'M', (char)value_to_char(d)); + value_type_destroy(d); + value_type_destroy(new_d); + + value new_e = value_create_long(1234); + ASSERT_EQ((long)0, (long)object_set(obj, "e", new_e)); + e = object_get(obj, "e"); + EXPECT_NE((value)NULL, (value)e); + EXPECT_EQ((long)1234L, (long)value_to_long(e)); + value_type_destroy(e); + value_type_destroy(new_e); + } + + // Test object call + type_id method_ids[] = { + TYPE_INVALID + }; + + method m = class_method(cls, "test_func", TYPE_STRING, method_ids, 0); + + ASSERT_EQ((method)m, (method)test_func_method); + + value ret = object_call(obj, m, args, 0); + + ASSERT_NE((value)NULL, (value)ret); + + ASSERT_STREQ(value_to_string(ret), "Hello World"); + + value_type_destroy(ret); + + // TODO: Test object await + + object_destroy(obj); + } + + // Create object (custom constructor) + { + value args[] = { + value_create_char('F'), + value_create_long(3435L) + }; + + type_id ctor_ids[] = { + TYPE_CHAR, + TYPE_LONG + }; + + constructor ctor = class_constructor(cls, ctor_ids, 2); + + ASSERT_EQ((constructor)ctor, (constructor)custom_ctor); + + object obj = class_new(cls, "helloWorldObj", ctor, args, 2); + ASSERT_NE((object)NULL, (object)obj); + + value_type_destroy(args[0]); + value_type_destroy(args[1]); + + // Get attributes from object + { + value d = object_get(obj, "d"); + ASSERT_NE((value)NULL, (value)d); + EXPECT_EQ((char)'F', (char)value_to_char(d)); + value_type_destroy(d); + + value e = object_get(obj, "e"); + ASSERT_NE((value)NULL, (value)e); + EXPECT_EQ((long)3435L, (long)value_to_long(e)); + value_type_destroy(e); + } + + // Test object call + type_id method_ids[] = { + TYPE_INVALID + }; + + method m = class_method(cls, "test_func", TYPE_STRING, method_ids, 0); + + ASSERT_EQ((method)m, (method)test_func_method); + + value ret = object_call(obj, m, args, 0); + + ASSERT_NE((value)NULL, (value)ret); + + ASSERT_STREQ(value_to_string(ret), "Hello World"); + + value_type_destroy(ret); + + // TODO: Test object await + + object_destroy(obj); + } + + type_destroy(char_type); + type_destroy(long_type); + type_destroy(str_type); + type_destroy(int_type); + type_destroy(float_type); + + class_destroy(cls); +} diff --git a/source/tests/reflect_scope_test/CMakeLists.txt b/source/tests/reflect_scope_test/CMakeLists.txt index c1b0775aa2..83990f2359 100644 --- a/source/tests/reflect_scope_test/CMakeLists.txt +++ b/source/tests/reflect_scope_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,17 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -111,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_scope_test/source/main.cpp b/source/tests/reflect_scope_test/source/main.cpp index 5dab5b82e0..cba58797ca 100644 --- a/source/tests/reflect_scope_test/source/main.cpp +++ b/source/tests/reflect_scope_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_scope_test/source/reflect_scope_test.cpp b/source/tests/reflect_scope_test/source/reflect_scope_test.cpp index 5e7580e695..2e58c50b04 100644 --- a/source/tests/reflect_scope_test/source/reflect_scope_test.cpp +++ b/source/tests/reflect_scope_test/source/reflect_scope_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -45,9 +45,9 @@ typedef struct function_impl_example_type } * function_impl_example; -void function_example(char c, int i, void * p) +void function_example(char c, int i, void *p) { - struct example_arg_type * e = (struct example_arg_type *)p; + struct example_arg_type *e = (struct example_arg_type *)p; log_write("metacall", LOG_LEVEL_DEBUG, "char: %c; int: %d; ptr: %p", c, i, p); @@ -82,7 +82,7 @@ function_return function_example_interface_invoke(function func, function_impl f return NULL; } -function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void * context) +function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void *context) { /* TODO */ @@ -106,8 +106,7 @@ void function_example_interface_destroy(function func, function_impl func_impl) function_interface function_example_singleton() { - static struct function_interface_type example_interface = - { + static struct function_interface_type example_interface = { &function_example_interface_create, &function_example_interface_invoke, &function_example_interface_await, @@ -119,34 +118,34 @@ function_interface function_example_singleton() class reflect_scope_test : public testing::Test { - public: +public: }; TEST_F(reflect_scope_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); // Initialize serial - EXPECT_EQ((int) 0, (int) serial_initialize()); + EXPECT_EQ((int)0, (int)serial_initialize()); // Create serial serial s = serial_create("rapid_json"); scope sp = scope_create("test"); - EXPECT_NE((scope) sp, (scope) NULL); + EXPECT_NE((scope)sp, (scope)NULL); type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); type int_type = type_create(TYPE_INT, "int", NULL, NULL); type ptr_type = type_create(TYPE_PTR, "ptr", NULL, NULL); - EXPECT_NE((type) char_type, (type) NULL); - EXPECT_NE((type) int_type, (type) NULL); - EXPECT_NE((type) ptr_type, (type) NULL); + EXPECT_NE((type)char_type, (type)NULL); + EXPECT_NE((type)int_type, (type)NULL); + EXPECT_NE((type)ptr_type, (type)NULL); if (sp && char_type != NULL && int_type != NULL && ptr_type != NULL) { @@ -156,7 +155,7 @@ TEST_F(reflect_scope_test, DefaultConstructor) f1 = function_create("example", 3, example_impl, &function_example_singleton); - EXPECT_NE((function) f1, (function) NULL); + EXPECT_NE((function)f1, (function)NULL); if (f1 != NULL) { @@ -164,14 +163,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f1), 1, "i", int_type); signature_set(function_signature(f1), 2, "p", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f1), f1), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f1), value_create_function(f1)), (int)0); } function_impl_example example_asd_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f2 = function_create("example_asd", 3, example_asd_impl, &function_example_singleton); - EXPECT_NE((function) f2, (function) NULL); + EXPECT_NE((function)f2, (function)NULL); if (f2 != NULL) { @@ -179,14 +178,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f2), 1, "o", int_type); signature_set(function_signature(f2), 2, "u", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f2), f2), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f2), value_create_function(f2)), (int)0); } function_impl_example example_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f3 = function_create("example_ret", 3, example_ret_impl, &function_example_singleton); - EXPECT_NE((function) f3, (function) NULL); + EXPECT_NE((function)f3, (function)NULL); if (f3 != NULL) { @@ -195,14 +194,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f3), 1, "o", int_type); signature_set(function_signature(f3), 2, "u", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f3), f3), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f3), value_create_function(f3)), (int)0); } function_impl_example example_duck_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f4 = function_create("example_duck", 3, example_duck_impl, &function_example_singleton); - EXPECT_NE((function) f4, (function) NULL); + EXPECT_NE((function)f4, (function)NULL); if (f4 != NULL) { @@ -210,14 +209,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f4), 1, "o", NULL); signature_set(function_signature(f4), 2, "u", NULL); - EXPECT_EQ((int) scope_define(sp, function_name(f4), f4), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f4), value_create_function(f4)), (int)0); } function_impl_example example_duck_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f5 = function_create("example_duck_ret", 3, example_duck_ret_impl, &function_example_singleton); - EXPECT_NE((function) f5, (function) NULL); + EXPECT_NE((function)f5, (function)NULL); if (f5 != NULL) { @@ -226,14 +225,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f5), 1, "do", NULL); signature_set(function_signature(f5), 2, "dafu", NULL); - EXPECT_EQ((int) scope_define(sp, function_name(f5), f5), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f5), value_create_function(f5)), (int)0); } function_impl_example example_duck_mix_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f6 = function_create("example_duck_mix", 4, example_duck_mix_impl, &function_example_singleton); - EXPECT_NE((function) f6, (function) NULL); + EXPECT_NE((function)f6, (function)NULL); if (f6 != NULL) { @@ -242,14 +241,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f6), 2, "dafu", NULL); signature_set(function_signature(f6), 3, "dafu", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f6), f6), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f6), value_create_function(f6)), (int)0); } function_impl_example example_duck_mix_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f7 = function_create("example_duck_mix_ret", 4, example_duck_mix_ret_impl, &function_example_singleton); - EXPECT_NE((function) f7, (function) NULL); + EXPECT_NE((function)f7, (function)NULL); if (f7 != NULL) { @@ -259,31 +258,31 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f7), 2, "dafu", ptr_type); signature_set(function_signature(f7), 3, "dafufs", NULL); - EXPECT_EQ((int) scope_define(sp, function_name(f7), f7), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f7), value_create_function(f7)), (int)0); } function_impl_example example_empty_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f8 = function_create("example_empty_ret", 0, example_empty_ret_impl, &function_example_singleton); - EXPECT_NE((function) f8, (function) NULL); + EXPECT_NE((function)f8, (function)NULL); if (f8 != NULL) { signature_set_return(function_signature(f8), int_type); - EXPECT_EQ((int) scope_define(sp, function_name(f8), f8), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f8), value_create_function(f8)), (int)0); } function_impl_example example_empty_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f9 = function_create("example_empty", 0, example_empty_impl, &function_example_singleton); - EXPECT_NE((function) f9, (function) NULL); + EXPECT_NE((function)f9, (function)NULL); if (f9 != NULL) { - EXPECT_EQ((int) scope_define(sp, function_name(f9), f9), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f9), value_create_function(f9)), (int)0); } { @@ -291,11 +290,11 @@ TEST_F(reflect_scope_test, DefaultConstructor) value v = scope_metadata(sp); - EXPECT_NE((value) NULL, (value) v); + EXPECT_NE((value)NULL, (value)v); memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); - char * str = serial_serialize(s, v, &size, allocator); + char *str = serial_serialize(s, v, &size, allocator); log_write("metacall", LOG_LEVEL_DEBUG, "Scope serialization info: %s", str); @@ -314,7 +313,7 @@ TEST_F(reflect_scope_test, DefaultConstructor) } // Clear serial - EXPECT_EQ((int) 0, (int) serial_clear(s)); + EXPECT_EQ((int)0, (int)serial_clear(s)); // Destroy serial serial_destroy(); diff --git a/source/tests/reflect_value_cast_test/CMakeLists.txt b/source/tests/reflect_value_cast_test/CMakeLists.txt index f8ac6957e5..4b28d3a1a9 100644 --- a/source/tests/reflect_value_cast_test/CMakeLists.txt +++ b/source/tests/reflect_value_cast_test/CMakeLists.txt @@ -74,6 +74,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -86,17 +100,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -117,11 +121,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_value_cast_test/source/main.cpp b/source/tests/reflect_value_cast_test/source/main.cpp index 5dab5b82e0..cba58797ca 100644 --- a/source/tests/reflect_value_cast_test/source/main.cpp +++ b/source/tests/reflect_value_cast_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp index 91fb1bd46b..988e7a607a 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_bool_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_bool_test, bool_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) b, (char) value_to_char(v)); + EXPECT_EQ((char)b, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) b, (short) value_to_short(v)); + EXPECT_EQ((short)b, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) b, (int) value_to_int(v)); + EXPECT_EQ((int)b, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) b, (long) value_to_long(v)); + EXPECT_EQ((long)b, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) b, (float) value_to_float(v)); + EXPECT_EQ((float)b, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) b, (double) value_to_double(v)); + EXPECT_EQ((double)b, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp index 9adad2f6e3..0bbe2bbecf 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_char_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_char_test, char_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_char_test, char_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_char_test, char_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) c, (char) value_to_char(v)); + EXPECT_EQ((char)c, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_char_test, char_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) c, (short) value_to_short(v)); + EXPECT_EQ((short)c, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_char_test, char_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) c, (int) value_to_int(v)); + EXPECT_EQ((int)c, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_char_test, char_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) c, (long) value_to_long(v)); + EXPECT_EQ((long)c, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_char_test, char_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) c, (float) value_to_float(v)); + EXPECT_EQ((float)c, (float)value_to_float(v)); value_destroy(v); } @@ -114,8 +114,7 @@ TEST_F(reflect_value_cast_char_test, char_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) c, (double) value_to_double(v)); + EXPECT_EQ((double)c, (double)value_to_double(v)); value_destroy(v); } - diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp index 18a2bef35d..8991352731 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_double_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_double_test, double_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_double_test, double_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_double_test, double_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) d, (char) value_to_char(v)); + EXPECT_EQ((char)d, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_double_test, double_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) d, (short) value_to_short(v)); + EXPECT_EQ((short)d, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_double_test, double_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) d, (int) value_to_int(v)); + EXPECT_EQ((int)d, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_double_test, double_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) d, (long) value_to_long(v)); + EXPECT_EQ((long)d, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_double_test, double_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) d, (float) value_to_float(v)); + EXPECT_EQ((float)d, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_double_test, double_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) d, (double) value_to_double(v)); + EXPECT_EQ((double)d, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp index b8ce1179cb..f1ffedc062 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_float_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_float_test, float_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_float_test, float_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_float_test, float_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) f, (char) value_to_char(v)); + EXPECT_EQ((char)f, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_float_test, float_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) f, (short) value_to_short(v)); + EXPECT_EQ((short)f, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_float_test, float_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) f, (int) value_to_int(v)); + EXPECT_EQ((int)f, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_float_test, float_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) f, (long) value_to_long(v)); + EXPECT_EQ((long)f, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_float_test, float_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) f, (float) value_to_float(v)); + EXPECT_EQ((float)f, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_float_test, float_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) f, (double) value_to_double(v)); + EXPECT_EQ((double)f, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp index 245aca21c6..7e6e2aa35d 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_int_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_int_test, int_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_int_test, int_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_int_test, int_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) i, (char) value_to_char(v)); + EXPECT_EQ((char)i, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_int_test, int_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) i, (short) value_to_short(v)); + EXPECT_EQ((short)i, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_int_test, int_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) i, (int) value_to_int(v)); + EXPECT_EQ((int)i, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_int_test, int_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) i, (long) value_to_long(v)); + EXPECT_EQ((long)i, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_int_test, int_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) i, (float) value_to_float(v)); + EXPECT_EQ((float)i, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_int_test, int_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) i, (double) value_to_double(v)); + EXPECT_EQ((double)i, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp index ba7260f677..9258a11e32 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_long_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_long_test, long_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_long_test, long_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_long_test, long_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) l, (char) value_to_char(v)); + EXPECT_EQ((char)l, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_long_test, long_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) l, (short) value_to_short(v)); + EXPECT_EQ((short)l, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_long_test, long_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) l, (int) value_to_int(v)); + EXPECT_EQ((int)l, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_long_test, long_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) l, (long) value_to_long(v)); + EXPECT_EQ((long)l, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_long_test, long_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) l, (float) value_to_float(v)); + EXPECT_EQ((float)l, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_long_test, long_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) l, (double) value_to_double(v)); + EXPECT_EQ((double)l, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp index 843ef958ef..e33d576373 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_short_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_short_test, short_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_short_test, short_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_short_test, short_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) s, (char) value_to_char(v)); + EXPECT_EQ((char)s, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_short_test, short_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) s, (short) value_to_short(v)); + EXPECT_EQ((short)s, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_short_test, short_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) s, (int) value_to_int(v)); + EXPECT_EQ((int)s, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_short_test, short_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) s, (long) value_to_long(v)); + EXPECT_EQ((long)s, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_short_test, short_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) s, (float) value_to_float(v)); + EXPECT_EQ((float)s, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_short_test, short_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) s, (double) value_to_double(v)); + EXPECT_EQ((double)s, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/sanitizer/lsan.supp b/source/tests/sanitizer/lsan.supp new file mode 100644 index 0000000000..8550a51160 --- /dev/null +++ b/source/tests/sanitizer/lsan.supp @@ -0,0 +1,42 @@ +# List of supressions for leak sanitizer +# +# IMPORTANT: Do NOT add blank lines between the script, +# it seems that gcc sanitizer does not support that. +# +# At this point there are leaks from the runtimes but +# I am not sure all of them are due to metacall (probably +# some of them are due to GC errors but not all of them). +# We disable them until we debug those cases (if any) in depth. +# +# +# Python +# +leak:_PyObject_Malloc +leak:_PyObject_Realloc +leak:_PyObject_GC_Resize +leak:_PyMem_RawRealloc +leak:_PyMem_RawMalloc +leak:PyThread_allocate_lock +leak:libpython* +# +# NodeJS +# +# Suppress small (intentional) leaks in glibc +leak:libc.so +# Supress node library (for now) +leak:libnode* +# +# Ruby +# +leak:libruby* +# +# DotNet Core +# +leak:libcoreclr* +leak:libicuuc* +# TODO: Implement assembly unloading with loader context +leak:System.Private.CoreLib.dll +# +# Rust +# +leak:libLLVM* \ No newline at end of file diff --git a/source/tests/sanitizer/tsan.supp b/source/tests/sanitizer/tsan.supp new file mode 100644 index 0000000000..a653d097e7 --- /dev/null +++ b/source/tests/sanitizer/tsan.supp @@ -0,0 +1,76 @@ +# List of supressions for thread sanitizer +# +# IMPORTANT: Do NOT add blank lines between the script, +# it seems that gcc sanitizer does not support that. +# +# At this point there are data races from the runtimes but +# I am not sure all of them are due to metacall (probably +# some of them are due to race conditions but not all of them). +# We disable them until we debug those cases (if any) in depth. +# +# +# Python +# +#called_from_lib:libpython* +# +# Suppress race condition from Python 10 async io: https://github.com/python/cpython/issues/116912 +race:socketpair +race:socket_socketpair +race:sock_close +race:sock_send_impl +# +# NodeJS +# +race:v8::platform::tracing::TracingController::GetCategoryGroupEnabled +race:v8::platform::DefaultJobWorker::~DefaultJobWorker +race:v8::platform::DefaultJobState::~DefaultJobState +race:v8::internal::ScavengerCollector::JobTask::~JobTask +race:heap::base::Worklist, (unsigned short)256>::Local::Pop(std::pair*) +# +# After version 108, NodeJS has started to fail without stack trace, for example: +# +# WARNING: ThreadSanitizer: data race (pid=5179) +# Write of size 8 at 0x723000007538 by thread T4: +# #0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.2+0x9b42b) (BuildId: f23ac1bd2939198f3fef776fd2a1312e536dcf1b) +# #1 (libnode.so.109+0xdb8fa9) (BuildId: fa61d14d9def07b0f94f901c16a182f9e3a944ae) +# +# Now if we use addr2line: +# +# addr2line -f -e /lib/x86_64-linux-gnu/libnode.so.109 0xdb8fa9 +# _ZN2v88platform16DefaultJobWorkerD0Ev +# ??:? +# +# The symbols are exactly the same of the well known suppressions already listed before +# I have tried compiling NodeJS with thread sanitizer support and also using llvm-symbolizer +# but I had no luck making those symbols work in the last stack traces, so for now.. I am going +# to suppress all data races from NodeJS in order to avoid false positives. +# +race:libnode* +# +# Ruby +# +#called_from_lib:libruby* +# +# DotNet Core +# +called_from_lib:libcoreclr* +called_from_lib:libclrjit* +# +# Java +# +called_from_lib:libjvm* +# +# WASM (TODO: This must be reviewed, probably it's a false positive) +# +called_from_lib:libwasmtime* +# +# C +# +mutex:clang_install_aborting_llvm_fatal_error_handler +mutex:libclang-* +# +# Rust +# +race:libLLVM* +race:librustc_driver-* +race:libstd-* \ No newline at end of file diff --git a/source/tests/serial_test/CMakeLists.txt b/source/tests/serial_test/CMakeLists.txt index efd1094933..12356bc112 100644 --- a/source/tests/serial_test/CMakeLists.txt +++ b/source/tests/serial_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this serial is enabled +if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_METACALL OR NOT OPTION_BUILD_SERIALS_RAPID_JSON) + return() +endif() + # # Executable name and options # @@ -68,6 +73,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,17 +99,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::portability - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -111,11 +120,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -128,6 +146,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + metacall_serial + rapid_json_serial +) # # Define test labels # diff --git a/source/tests/serial_test/source/main.cpp b/source/tests/serial_test/source/main.cpp index 5dab5b82e0..cba58797ca 100644 --- a/source/tests/serial_test/source/main.cpp +++ b/source/tests/serial_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/serial_test/source/serial_test.cpp b/source/tests/serial_test/source/serial_test.cpp index d474dad407..bf2d158acb 100644 --- a/source/tests/serial_test/source/serial_test.cpp +++ b/source/tests/serial_test/source/serial_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ * */ -#include +#include + +#include #include @@ -26,31 +28,31 @@ class serial_test : public testing::Test { - public: - void create_serial(const char * name, const char * extension) +public: + void create_serial(const char *name, const char *extension) { serial s = serial_create(name); - ASSERT_NE((serial) NULL, (serial) s); + ASSERT_NE((serial)NULL, (serial)s); - EXPECT_EQ((int) 0, (int) strcmp(name, serial_name(s))); + EXPECT_STREQ(name, serial_name(s)); - EXPECT_EQ((int) 0, (int) strcmp(extension, serial_extension(s))); + EXPECT_STREQ(extension, serial_extension(s)); } - const char * rapid_json_name() + const char *rapid_json_name() { return "rapid_json"; } - const char * rapid_json_extension() + const char *rapid_json_extension() { return "json"; } - const char * metacall_name() + const char *metacall_name() { return "metacall"; } - const char * metacall_extension() + const char *metacall_extension() { return "meta"; } @@ -58,19 +60,19 @@ class serial_test : public testing::Test TEST_F(serial_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); // Create allocator memory_allocator allocator = memory_allocator_std(&malloc, &realloc, &free); - EXPECT_NE((memory_allocator) NULL, (memory_allocator) allocator); + EXPECT_NE((memory_allocator)NULL, (memory_allocator)allocator); // Initialize serial - EXPECT_EQ((int) 0, (int) serial_initialize()); + EXPECT_EQ((int)0, (int)serial_initialize()); // Create RapidJSON serial create_serial(rapid_json_name(), rapid_json_extension()); @@ -82,8 +84,7 @@ TEST_F(serial_test, DefaultConstructor) { static const char hello_world[] = "hello world"; - static const value value_list[] = - { + static const value value_list[] = { value_create_int(244), value_create_double(6.8), value_create_string(hello_world, sizeof(hello_world) - 1) @@ -92,20 +93,17 @@ TEST_F(serial_test, DefaultConstructor) static const char map_index_a[] = "aaa"; static const char map_index_b[] = "bbb"; - static const value value_map_a[] = - { + static const value value_map_a[] = { value_create_string(map_index_a, sizeof(map_index_a) - 1), value_create_double(3.333) }; - static const value value_map_b[] = - { + static const value value_map_b[] = { value_create_string(map_index_b, sizeof(map_index_b) - 1), value_create_double(4.5) }; - static const value value_map[] = - { + static const value value_map[] = { value_create_array(value_map_a, sizeof(value_map_a) / sizeof(value_map_a[0])), value_create_array(value_map_b, sizeof(value_map_b) / sizeof(value_map_b[0])) }; @@ -126,6 +124,8 @@ TEST_F(serial_test, DefaultConstructor) static const char json_string[] = "\"Hello World\""; static const char json_string_value[] = "Hello World"; + static const char json_empty_array[] = "[]"; + size_t serialize_size = 0; serial s = serial_create(rapid_json_name()); @@ -133,14 +133,14 @@ TEST_F(serial_test, DefaultConstructor) // Create value array from value list value v = value_create_array(value_list, value_list_size); - EXPECT_NE((value) NULL, (value) v); + EXPECT_NE((value)NULL, (value)v); // Serialize value array into buffer - char * buffer = serial_serialize(s, v, &serialize_size, allocator); + char *buffer = serial_serialize(s, v, &serialize_size, allocator); - EXPECT_EQ((size_t) sizeof(value_list_str), (size_t) serialize_size); - EXPECT_NE((char *) NULL, (char *) buffer); - EXPECT_EQ((int) 0, (int) strcmp(buffer, value_list_str)); + EXPECT_EQ((size_t)sizeof(value_list_str), (size_t)serialize_size); + EXPECT_NE((char *)NULL, (char *)buffer); + EXPECT_STREQ(buffer, value_list_str); value_destroy(v); @@ -154,20 +154,20 @@ TEST_F(serial_test, DefaultConstructor) // Create value map from value list map v = value_create_map(value_map, value_map_size); - EXPECT_NE((value) NULL, (value) v); + EXPECT_NE((value)NULL, (value)v); // Serialize value map into buffer buffer = serial_serialize(s, v, &serialize_size, allocator); - EXPECT_EQ((size_t) sizeof(value_map_str), (size_t) serialize_size); - EXPECT_NE((value) NULL, (value) v); - EXPECT_EQ((int) 0, (int) strcmp(buffer, value_map_str)); + EXPECT_EQ((size_t)sizeof(value_map_str), (size_t)serialize_size); + EXPECT_NE((value)NULL, (value)v); + EXPECT_STREQ(buffer, value_map_str); - value * v_map = value_to_map(v); + value *v_map = value_to_map(v); for (size_t iterator = 0; iterator < value_map_size; ++iterator) { - value * tupla = value_to_array(v_map[iterator]); + value *tupla = value_to_array(v_map[iterator]); value_destroy(tupla[0]); value_destroy(tupla[1]); @@ -178,25 +178,41 @@ TEST_F(serial_test, DefaultConstructor) value_destroy(v); memory_allocator_deallocate(allocator, buffer); - + + // Create an empty array + v = value_create_array(NULL, 0); + + EXPECT_NE((value)NULL, (value)v); + + // Serialize empty array into buffer + buffer = serial_serialize(s, v, &serialize_size, allocator); + + EXPECT_EQ((size_t)sizeof(json_empty_array), (size_t)serialize_size); + EXPECT_NE((value)NULL, (value)v); + EXPECT_STREQ(buffer, json_empty_array); + + value_destroy(v); + + memory_allocator_deallocate(allocator, buffer); + // Deserialize json buffer array into value v = serial_deserialize(s, json_buffer_array, sizeof(json_buffer_array), allocator); - EXPECT_EQ((type_id) TYPE_ARRAY, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) json_buffer_array_size, (size_t) value_type_count(v)); + EXPECT_EQ((type_id)TYPE_ARRAY, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)json_buffer_array_size, (size_t)value_type_count(v)); - value * v_array = value_to_array(v); + value *v_array = value_to_array(v); - EXPECT_NE((value *) NULL, (value *) v_array); + EXPECT_NE((value *)NULL, (value *)v_array); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(v_array[0])); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(v_array[0]), "asdf")); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(v_array[0])); + EXPECT_STREQ(value_to_string(v_array[0]), "asdf"); - EXPECT_EQ((type_id) TYPE_INT, (type_id) value_type_id(v_array[1])); - EXPECT_EQ((int) 443, (int) value_to_int(v_array[1])); + EXPECT_EQ((type_id)TYPE_INT, (type_id)value_type_id(v_array[1])); + EXPECT_EQ((int)443, (int)value_to_int(v_array[1])); - EXPECT_EQ((type_id) TYPE_FLOAT, (type_id) value_type_id(v_array[2])); - EXPECT_EQ((float) 3.2f, (float) value_to_float(v_array[2])); + EXPECT_EQ((type_id)TYPE_FLOAT, (type_id)value_type_id(v_array[2])); + EXPECT_EQ((float)3.2f, (float)value_to_float(v_array[2])); for (size_t iterator = 0; iterator < value_list_size; ++iterator) { @@ -208,23 +224,23 @@ TEST_F(serial_test, DefaultConstructor) // Deserialize json buffer map into value v = serial_deserialize(s, json_buffer_map, sizeof(json_buffer_map), allocator); - EXPECT_EQ((type_id) TYPE_MAP, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) json_buffer_map_size, (size_t) value_type_count(v)); + EXPECT_EQ((type_id)TYPE_MAP, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)json_buffer_map_size, (size_t)value_type_count(v)); v_map = value_to_map(v); - EXPECT_NE((value *) NULL, (value *) v_map); + EXPECT_NE((value *)NULL, (value *)v_map); - EXPECT_EQ((type_id) TYPE_ARRAY, (type_id) value_type_id(v_map[0])); - EXPECT_EQ((type_id) TYPE_ARRAY, (type_id) value_type_id(v_map[1])); + EXPECT_EQ((type_id)TYPE_ARRAY, (type_id)value_type_id(v_map[0])); + EXPECT_EQ((type_id)TYPE_ARRAY, (type_id)value_type_id(v_map[1])); - value * tupla = value_to_array(v_map[0]); + value *tupla = value_to_array(v_map[0]); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(tupla[0])); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(tupla[0]), "abc")); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(tupla[0])); + EXPECT_STREQ(value_to_string(tupla[0]), "abc"); - EXPECT_EQ((type_id) TYPE_FLOAT, (type_id) value_type_id(tupla[1])); - EXPECT_EQ((float) 9.9f, (float) value_to_float(tupla[1])); + EXPECT_EQ((type_id)TYPE_FLOAT, (type_id)value_type_id(tupla[1])); + EXPECT_EQ((float)9.9f, (float)value_to_float(tupla[1])); value_destroy(tupla[0]); value_destroy(tupla[1]); @@ -233,11 +249,11 @@ TEST_F(serial_test, DefaultConstructor) tupla = value_to_array(v_map[1]); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(tupla[0])); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(tupla[0]), "cde")); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(tupla[0])); + EXPECT_STREQ(value_to_string(tupla[0]), "cde"); - EXPECT_EQ((type_id) TYPE_FLOAT, (type_id) value_type_id(tupla[1])); - EXPECT_EQ((float) 1.5f, (float) value_to_float(tupla[1])); + EXPECT_EQ((type_id)TYPE_FLOAT, (type_id)value_type_id(tupla[1])); + EXPECT_EQ((float)1.5f, (float)value_to_float(tupla[1])); value_destroy(tupla[0]); value_destroy(tupla[1]); @@ -249,27 +265,27 @@ TEST_F(serial_test, DefaultConstructor) // Deserialize json boolean primitive type into value v = serial_deserialize(s, json_true, sizeof(json_true), allocator); - EXPECT_EQ((type_id) TYPE_BOOL, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) sizeof(boolean), (size_t)value_type_size(v)); - EXPECT_EQ((boolean) 1L, (boolean) value_to_bool(v)); + EXPECT_EQ((type_id)TYPE_BOOL, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)sizeof(boolean), (size_t)value_type_size(v)); + EXPECT_EQ((boolean)1L, (boolean)value_to_bool(v)); value_destroy(v); // Deserialize json number primitive type into value v = serial_deserialize(s, json_number, sizeof(json_number), allocator); - EXPECT_EQ((type_id) TYPE_INT, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) sizeof(int), (size_t) value_type_size(v)); - EXPECT_EQ((int) 23434, (int) value_to_int(v)); + EXPECT_EQ((type_id)TYPE_INT, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)sizeof(int), (size_t)value_type_size(v)); + EXPECT_EQ((int)23434, (int)value_to_int(v)); value_destroy(v); // Deserialize json string primitive type into value v = serial_deserialize(s, json_string, sizeof(json_string), allocator); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) sizeof(json_string_value), (size_t) value_type_size(v)); - EXPECT_EQ((int) 0, (int) strncmp(value_to_string(v), json_string_value, sizeof(json_string_value) - 1)); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)sizeof(json_string_value), (size_t)value_type_size(v)); + EXPECT_EQ((int)0, (int)strncmp(value_to_string(v), json_string_value, sizeof(json_string_value) - 1)); value_destroy(v); } @@ -277,13 +293,9 @@ TEST_F(serial_test, DefaultConstructor) // MetaCall { static const char hello_world[] = "hello world"; - static const char good_bye[] = "good bye"; - static const size_t hello_world_length = sizeof(hello_world) - 1; - static const size_t good_bye_length = sizeof(good_bye) - 1; - static const char * value_names[] = - { + static const char *value_names[] = { "true", "A", "123", @@ -295,32 +307,34 @@ TEST_F(serial_test, DefaultConstructor) "05060708", "[244,6.800000,hello world]", NULL, /* TODO: Map */ - #if defined(_WIN32) && defined(_MSC_VER) - #if defined(_WIN64) - "0x00000000000A7EF2", - #else - "0x000A7EF2", - #endif - #elif defined(__linux) || defined(__linux__) || defined(__APPLE__) - "0xa7ef2", - #else - "", - #endif +#if defined(_WIN32) && defined(_MSC_VER) + #if defined(_WIN64) + "0x00000000000A7EF2", + #else + "0x000A7EF2", + #endif +#elif defined(__linux) || defined(__linux__) || defined(__APPLE__) + "0xa7ef2", +#else + "", +#endif NULL, /* TODO: Future */ NULL, /* TODO: Function */ - "(null)" + "(null)", + NULL, /* TODO: Class */ + NULL, /* TODO: Object */ + NULL, /* TODO: Exception */ + NULL /* TODO: Throwable */ }; - static_assert((int) sizeof(value_names) / sizeof(value_names[0]) == (int) TYPE_SIZE, + portability_static_assert((int)sizeof(value_names) / sizeof(value_names[0]) == (int)TYPE_SIZE, "Value names size does not match type size."); - static const char char_array[] = - { + static const char char_array[] = { 0x05, 0x06, 0x07, 0x08 }; - static const value value_list[] = - { + static const value value_list[] = { value_create_int(244), value_create_double(6.8), value_create_string(hello_world, hello_world_length) @@ -328,6 +342,11 @@ TEST_F(serial_test, DefaultConstructor) static const size_t value_list_size = sizeof(value_list) / sizeof(value_list[0]); + /* TODO: Implement map properly */ + /* + static const char good_bye[] = "good bye"; + static const size_t good_bye_length = sizeof(good_bye) - 1; + static const value value_map_tupla_a[] = { value_create_string(hello_world, hello_world_length), @@ -347,9 +366,12 @@ TEST_F(serial_test, DefaultConstructor) }; static const size_t value_map_size = sizeof(value_map) / sizeof(value_map[0]); + */ - value value_array[] = - { + /* TODO: Implement class properly */ + /* klass cls = class_create(NULL, ACCESSOR_TYPE_STATIC, NULL, NULL); */ + + value value_array[] = { value_create_bool(1), value_create_char('A'), value_create_short(123), @@ -360,14 +382,33 @@ TEST_F(serial_test, DefaultConstructor) value_create_string(hello_world, hello_world_length), value_create_buffer(char_array, sizeof(char_array)), value_create_array(value_list, value_list_size), + /* TODO: Implement map properly */ + NULL, + /* value_create_map(value_map, value_map_size), + */ value_create_ptr((void *)0x000A7EF2), - value_create_future(NULL), /* TODO: Implement future properly */ - value_create_function(NULL), /* TODO: Implement function properly */ - value_create_null() + /* TODO: Implement class properly */ + NULL, + NULL, + /* + value_create_future(future_create(NULL, NULL)), + value_create_function(function_create(NULL, 0, NULL, NULL)), + */ + value_create_null(), + /* TODO: Implement class properly */ + NULL, + NULL, + /* + value_create_class(cls), + value_create_object(class_new(cls, NULL, NULL, 0)), + */ + /* TODO: Implement exception properly */ + NULL, + NULL }; - static_assert((int) sizeof(value_array) / sizeof(value_array[0]) == (int)TYPE_SIZE, + portability_static_assert((int)sizeof(value_array) / sizeof(value_array[0]) == (int)TYPE_SIZE, "Value array size does not match type size."); const size_t value_names_size = sizeof(value_names) / sizeof(value_names[0]); @@ -381,26 +422,26 @@ TEST_F(serial_test, DefaultConstructor) { size_t size; - char * buffer = serial_serialize(s, value_array[iterator], &size, allocator); + char *buffer = serial_serialize(s, value_array[iterator], &size, allocator); log_write("metacall", LOG_LEVEL_DEBUG, "(%s == %s)", buffer, value_names[iterator]); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); - EXPECT_EQ((int) 0, (int) strncmp(buffer, value_names[iterator], size - 1)); + EXPECT_EQ((int)0, (int)strncmp(buffer, value_names[iterator], size - 1)); memory_allocator_deallocate(allocator, buffer); - } - value_type_destroy(value_array[iterator]); + value_type_destroy(value_array[iterator]); + } } } // Clear RapidJSON serial - EXPECT_EQ((int) 0, (int) serial_clear(serial_create(rapid_json_name()))); + EXPECT_EQ((int)0, (int)serial_clear(serial_create(rapid_json_name()))); // Clear MetaCall serial - EXPECT_EQ((int) 0, (int) serial_clear(serial_create(metacall_name()))); + EXPECT_EQ((int)0, (int)serial_clear(serial_create(metacall_name()))); // Destroy serial serial_destroy(); diff --git a/source/threading/CMakeLists.txt b/source/threading/CMakeLists.txt new file mode 100644 index 0000000000..830545d85c --- /dev/null +++ b/source/threading/CMakeLists.txt @@ -0,0 +1,188 @@ +# +# Library name and options +# + +# Target name +set(target threading) + +# Exit here if required dependencies are not met +message(STATUS "Lib ${target}") + +# Set API export file and macro +string(TOUPPER ${target} target_upper) +set(export_file "include/${target}/${target}_api.h") +set(export_macro "${target_upper}_API") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/threading.h + ${include_path}/threading_atomic.h + ${include_path}/threading_thread_id.h + ${include_path}/threading_atomic_ref_count.h + ${include_path}/threading_mutex.h +) + +set(sources + ${source_path}/threading.c + ${source_path}/threading_thread_id.c +) + +if( + (("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") OR + (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") AND ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC"))) AND + (MSVC_VERSION GREATER_EQUAL 1930) +) + set(headers + ${headers} + ${include_path}/threading_atomic_win32.h + ) +endif() + +if(WIN32) + set(sources + ${sources} + ${source_path}/threading_mutex_win32.c + ) +elseif(APPLE) + set(sources + ${sources} + ${source_path}/threading_mutex_macos.c + ) +else() + set(sources + ${sources} + ${source_path}/threading_mutex_pthread.c + ) +endif() + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create library +# + +# Build library +add_library(${target} + ${sources} + ${headers} +) + +# Create namespaced alias +add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# Export library for downstream projects +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) + +# Create API export header +generate_export_header(${target} + EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${export_macro} +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + + PUBLIC + ${DEFAULT_INCLUDE_DIRECTORIES} + + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${META_PROJECT_NAME}::version + + PUBLIC + ${DEFAULT_LIBRARIES} + + INTERFACE +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${target_upper}_EXPORTS # Export API + + PUBLIC + $<$>:${target_upper}_STATIC_DEFINE> + ${DEFAULT_COMPILE_DEFINITIONS} + + INTERFACE +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_COMPILE_OPTIONS} + + INTERFACE +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_LINKER_OPTIONS} + + INTERFACE +) diff --git a/source/threading/include/threading/threading.h b/source/threading/include/threading/threading.h new file mode 100644 index 0000000000..f0b7114816 --- /dev/null +++ b/source/threading/include/threading/threading.h @@ -0,0 +1,39 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_H +#define THREADING_H 1 + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +THREADING_API const char *threading_print_info(void); + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_H */ diff --git a/source/threading/include/threading/threading_atomic.h b/source/threading/include/threading/threading_atomic.h new file mode 100644 index 0000000000..26ba7c45b3 --- /dev/null +++ b/source/threading/include/threading/threading_atomic.h @@ -0,0 +1,75 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_ATOMIC_H +#define THREADING_ATOMIC_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Definitions -- */ + +#if defined(_WIN32) && defined(_MSC_VER) + #if (_MSC_VER < 1930 || defined(__STDC_NO_ATOMICS__)) + /* Before Visual Studio 2022 atomics are not supported, use fallback solution */ + #include + #define THREADING_ATOMIC 1 + #else + #include + #define THREADING_ATOMIC 1 + #endif +#elif defined(__STDC_VERSION__) + #if (__STDC_VERSION__ - 0L) >= 201112L + /* C11 support */ + #if defined(__STDC_NO_ATOMICS__) + #define THREADING_ATOMIC 0 + #error "Using C11 but atomics not supported, check the platform and implement support" + #elif defined __has_include + #if __has_include() + #include + #define THREADING_ATOMIC 1 + #endif + #else + #include + #define THREADING_ATOMIC 1 + #endif + #else + #define THREADING_ATOMIC 0 + #error "C11 is not supported, check the platform and implement support" + #endif +#else + /* TODO: Unknown compiler and platform, check the platform and compiler, then implement support if needed */ + #define THREADING_ATOMIC 0 +#endif + +#if !defined(THREADING_ATOMIC) || (defined(THREADING_ATOMIC) && THREADING_ATOMIC == 0) + #error "Thread atomic support not implemented." +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_ATOMIC_H */ diff --git a/source/threading/include/threading/threading_atomic_ref_count.h b/source/threading/include/threading/threading_atomic_ref_count.h new file mode 100644 index 0000000000..8a1dc1cc45 --- /dev/null +++ b/source/threading/include/threading/threading_atomic_ref_count.h @@ -0,0 +1,158 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_ATOMIC_REF_COUNT_H +#define THREADING_ATOMIC_REF_COUNT_H 1 + +/* -- Headers -- */ + +#include + +#include + +#if defined(__THREAD_SANITIZER__) + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Definitions -- */ + +#define THREADING_ATOMIC_REF_COUNT_MAX UINTMAX_MAX +#define THREADING_ATOMIC_REF_COUNT_MIN 0 + +/* -- Member Data -- */ + +typedef struct +{ +#if defined(__THREAD_SANITIZER__) + uintmax_t count; + threading_mutex_type m; +#else + atomic_uintmax_t count; +#endif +} threading_atomic_ref_count_type; + +/* -- Type Definitions -- */ + +typedef threading_atomic_ref_count_type *threading_atomic_ref_count; + +/* -- Methods -- */ + +static inline void threading_atomic_ref_count_store(threading_atomic_ref_count ref, uintmax_t v) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_store(&ref->m, &ref->count, &v, sizeof(uintmax_t)); +#else + atomic_store(&ref->count, v); +#endif +} + +static inline void threading_atomic_ref_count_initialize(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + uintmax_t init = THREADING_ATOMIC_REF_COUNT_MIN; + + threading_mutex_initialize(&ref->m); + + threading_mutex_store(&ref->m, &ref->count, &init, sizeof(uintmax_t)); +#else + threading_atomic_ref_count_store(ref, THREADING_ATOMIC_REF_COUNT_MIN); +#endif +} + +static inline uintmax_t threading_atomic_ref_count_load(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + uintmax_t result = 0; + + threading_mutex_store(&ref->m, &result, &ref->count, sizeof(uintmax_t)); + + return result; +#else + return atomic_load_explicit(&ref->count, memory_order_relaxed); +#endif +} + +static inline int threading_atomic_ref_count_increment(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_lock(&ref->m); + { + ++ref->count; + } + threading_mutex_unlock(&ref->m); +#else + if (atomic_load_explicit(&ref->count, memory_order_relaxed) == THREADING_ATOMIC_REF_COUNT_MAX) + { + return 1; + } + + atomic_fetch_add_explicit(&ref->count, 1, memory_order_relaxed); +#endif + + return 0; +} + +static inline int threading_atomic_ref_count_decrement(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_lock(&ref->m); + { + --ref->count; + } + threading_mutex_unlock(&ref->m); +#else + if (atomic_load_explicit(&ref->count, memory_order_relaxed) == THREADING_ATOMIC_REF_COUNT_MIN) + { + return 1; + } + + uintmax_t old_ref_count = atomic_fetch_sub_explicit(&ref->count, 1, memory_order_release); + + if (old_ref_count == THREADING_ATOMIC_REF_COUNT_MIN + 1) + { + atomic_thread_fence(memory_order_acquire); + } +#endif + + return 0; +} + +static inline void threading_atomic_ref_count_destroy(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_destroy(&ref->m); +#else + (void)ref; +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_ATOMIC_REF_COUNT_H */ diff --git a/source/threading/include/threading/threading_atomic_win32.h b/source/threading/include/threading/threading_atomic_win32.h new file mode 100644 index 0000000000..00c2055aa5 --- /dev/null +++ b/source/threading/include/threading/threading_atomic_win32.h @@ -0,0 +1,371 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_ATOMIC_WIN32_H +#define THREADING_ATOMIC_WIN32_H 1 + +/* -- Headers -- */ + +#include + +#include +#include +#include + +#ifndef NOMINMAX + #define NOMINMAX +#endif + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool volatile atomic_bool; +typedef char volatile atomic_char; +typedef signed char volatile atomic_schar; +typedef unsigned char volatile atomic_uchar; +typedef short volatile atomic_short; +typedef unsigned short volatile atomic_ushort; +typedef int volatile atomic_int; +typedef unsigned int volatile atomic_uint; +typedef long volatile atomic_long; +typedef unsigned long volatile atomic_ulong; +typedef long long volatile atomic_llong; +typedef unsigned long long volatile atomic_ullong; +typedef size_t volatile atomic_size_t; +typedef ptrdiff_t volatile atomic_ptrdiff_t; +typedef intmax_t volatile atomic_intmax_t; +typedef uintmax_t volatile atomic_uintmax_t; +typedef intptr_t volatile atomic_intptr_t; +typedef uintptr_t volatile atomic_uintptr_t; +typedef uint8_t volatile atomic_uint8_t; +typedef uint16_t volatile atomic_uint16_t; +typedef uint32_t volatile atomic_uint32_t; +typedef uint64_t volatile atomic_uint64_t; +typedef int8_t volatile atomic_int8_t; +typedef int16_t volatile atomic_int16_t; +typedef int32_t volatile atomic_int32_t; +typedef int64_t volatile atomic_int64_t; + +typedef enum memory_order +{ + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + +typedef LONG volatile atomic_flag; + +#if _MSC_VER > 1800 && defined(_M_IX86) + #define _InterlockedExchangeAdd64 _InlineInterlockedExchangeAdd64 + #define _InterlockedExchange64 _InlineInterlockedExchange64 +#endif + +#define ATOMIC_BOOL_LOCK_FREE 2 +#define ATOMIC_CHAR_LOCK_FREE 2 +#define ATOMIC_CHAR16_T_LOCK_FREE 2 +#define ATOMIC_CHAR32_T_LOCK_FREE 2 +#define ATOMIC_WCHAR_T_LOCK_FREE 2 +#define ATOMIC_SHORT_LOCK_FREE 2 +#define ATOMIC_INT_LOCK_FREE 2 +#define ATOMIC_LONG_LOCK_FREE 2 +#define ATOMIC_LLONG_LOCK_FREE 2 +#define ATOMIC_POINTER_LOCK_FREE 2 + +#define ATOMIC_FLAG_INIT 0 +#define ATOMIC_VAR_INIT(value) (value) /* TODO: Deprecate for C17, remove in C23 */ + +#define atomic_flag_test_and_set(obj) \ + (bool)_interlockedbittestandset((LONG *)obj, (LONG)0) + +#define atomic_flag_test_and_set_explicit(obj, order) \ + atomic_flag_test_and_set(obj) + +#define atomic_flag_clear(obj) \ + (bool)_interlockedbittestandreset((LONG *)obj, (LONG)0) + +#define atomic_flag_clear_explicit(obj, order) \ + atomic_flag_clear(obj) + +#define atomic_init(obj, desired) (*(obj) = (desired), (void)0) + +#define __atomic_is_lock_free_power_of_2(x) ((x) && !((x) & ((x)-1))) + +#define atomic_is_lock_free(obj) \ + (sizeof(obj) <= 8 && __atomic_is_lock_free_power_of_2(sizeof(obj))) + +#undef __atomic_is_lock_free_power_of_2 + +static inline void atomic_store_explicit8(CHAR volatile *obj, CHAR desired, memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange8(obj, desired); + } + else + { + atomic_init(obj, desired); + } +} + +static inline void atomic_store_explicit16(SHORT volatile *obj, SHORT desired, memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange16(obj, desired); + } + else + { + atomic_init(obj, desired); + } +} + +static inline void atomic_store_explicit32(LONG volatile *obj, LONG desired, memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange(obj, desired); + } + else + { + atomic_init(obj, desired); + } +} + +static inline void atomic_store_explicit64(LONG64 volatile *obj, LONG64 desired, memory_order order) +{ + if (order == memory_order_seq_cst) +#ifdef _M_IX86 + { + _InterlockedExchangeNoFence64(obj, desired); + } + else + { + _InterlockedExchange64(obj, desired); + } +#else + { + _InterlockedExchange64(obj, desired); + } + else + { + atomic_init(obj, desired); + } +#endif +} + +#define atomic_store(obj, desired) \ + atomic_store_explicit(obj, desired, memory_order_seq_cst) + +#define atomic_store_explicit(obj, desired, order) \ + do \ + { \ + if (sizeof *(obj) == 1) \ + { \ + atomic_store_explicit8((CHAR volatile *)(obj), (CHAR)(desired), order); \ + } \ + else if (sizeof *(obj) == 2) \ + { \ + atomic_store_explicit16((SHORT volatile *)(obj), (SHORT)(desired), order); \ + } \ + else if (sizeof *(obj) == 4) \ + { \ + atomic_store_explicit32((LONG volatile *)(obj), (LONG)(desired), order); \ + } \ + else if (sizeof *(obj) == 8) \ + { \ + atomic_store_explicit64((LONG64 volatile *)(obj), (LONG64)(desired), order); \ + } \ + else \ + { \ + abort(); \ + } \ + } while (0) + +#define atomic_load(obj) \ + atomic_fetch_or((obj), 0) + +#define atomic_load_explicit(obj, order) \ + atomic_fetch_or_explicit((obj), 0, order) + +#define atomic_exchange(obj, desired) \ + atomic_exchange_explicit(obj, desired, memory_order_seq_cst) + +#define atomic_exchange_explicit(obj, desired, order) \ + ((sizeof *(obj) == 1) ? _InterlockedExchange8((CHAR volatile *)obj, (CHAR)desired) : \ + (sizeof *(obj) == 2) ? _InterlockedExchange16((SHORT volatile *)obj, (SHORT)desired) : \ + (sizeof *(obj) == 4) ? _InterlockedExchange((LONG volatile *)obj, (LONG)desired) : \ + (sizeof *(obj) == 8) ? _InterlockedExchange64((LONG64 volatile *)obj, (LONG64)desired) : \ + (abort(), 0)) + +static inline bool atomic_compare_exchange8(CHAR volatile *obj, CHAR *expected, CHAR desired) +{ + CHAR previous = _InterlockedCompareExchange8(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +static inline bool atomic_compare_exchange16(SHORT volatile *obj, SHORT *expected, SHORT desired) +{ + SHORT previous = _InterlockedCompareExchange16(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +static inline bool atomic_compare_exchange32(LONG volatile *obj, LONG *expected, LONG desired) +{ + LONG previous = _InterlockedCompareExchange(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +static inline bool atomic_compare_exchange64(LONG64 volatile *obj, LONG64 *expected, LONG64 desired) +{ + LONG64 previous = _InterlockedCompareExchange64(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \ + ((sizeof *(obj) == 1) ? atomic_compare_exchange8((CHAR volatile *)(obj), (CHAR *)(expected), (CHAR)(desired)) : \ + (sizeof *(obj) == 2) ? atomic_compare_exchange16((SHORT volatile *)(obj), (SHORT *)(expected), (SHORT)(desired)) : \ + (sizeof *(obj) == 4) ? atomic_compare_exchange32((LONG volatile *)(obj), (LONG *)(expected), (LONG)(desired)) : \ + (sizeof *(obj) == 8) ? atomic_compare_exchange64((LONG64 volatile *)(obj), (LONG64 *)(expected), (LONG64)(desired)) : \ + (abort(), false)) + +#define atomic_compare_exchange_strong(obj, expected, desired) \ + atomic_compare_exchange_strong_explicit(obj, expected, desired, memory_order_seq_cst, memory_order_seq_cst) + +#define atomic_compare_exchange_weak atomic_compare_exchange_strong + +#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \ + atomic_compare_exchange_weak((obj), (expected), (desired)) + +#define atomic_fetch_add(obj, arg) \ + atomic_fetch_add_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_add_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedExchangeAdd8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedExchangeAdd16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedExchangeAdd((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedExchangeAdd64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define atomic_fetch_sub(obj, arg) \ + atomic_fetch_sub_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_sub_explicit(obj, arg, order) \ + atomic_fetch_add_explicit(obj, (0 - arg), order) + +#define atomic_fetch_or(obj, arg) \ + atomic_fetch_or_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_or_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedOr8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedOr16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedOr((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedOr64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define atomic_fetch_xor(obj, arg) \ + atomic_fetch_xor_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_xor_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedXor8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedXor16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedXor((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedXor64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define atomic_fetch_and(obj, arg) \ + atomic_fetch_and_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_and_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedAnd8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedAnd16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedAnd((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedAnd64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define __atomic_compiler_barrier(order) \ + do \ + { \ + if (order > memory_order_consume) \ + { \ + _ReadWriteBarrier(); \ + } \ + } while (0) + +static inline void atomic_thread_fence(memory_order order) +{ + __atomic_compiler_barrier(order); + + if (order == memory_order_seq_cst) + { + MemoryBarrier(); + __atomic_compiler_barrier(order); + } +} + +static inline void atomic_signal_fence(memory_order order) +{ + __atomic_compiler_barrier(order); +} + +#undef __atomic_compiler_barrier + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_ATOMIC_WIN32_H */ diff --git a/source/threading/include/threading/threading_mutex.h b/source/threading/include/threading/threading_mutex.h new file mode 100644 index 0000000000..c9b346c3c8 --- /dev/null +++ b/source/threading/include/threading/threading_mutex.h @@ -0,0 +1,94 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_MUTEX_H +#define THREADING_MUTEX_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Type Definitions -- */ + +#if defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) + #include + #define THREADING_MUTEX_INITIALIZE \ + { \ + 0 \ + } +typedef CRITICAL_SECTION threading_mutex_impl_type; +#elif (defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux) || defined(__gnu_linux__) || defined(__TOS_LINUX__)) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + (defined(bsdi) || defined(__bsdi__)) || \ + defined(__DragonFly__) + #include + #define THREADING_MUTEX_INITIALIZE PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t threading_mutex_impl_type; +#elif (defined(__MACOS__) || defined(macintosh) || defined(Macintosh) || defined(__TOS_MACOS__)) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #include + #define THREADING_MUTEX_INITIALIZE OS_UNFAIR_LOCK_INIT +typedef os_unfair_lock threading_mutex_impl_type; +#else + #error "Platform not supported for mutex implementation" +#endif + +#include + +/* -- Type Definitions -- */ + +typedef threading_mutex_impl_type threading_mutex_type; +typedef threading_mutex_type *threading_mutex; + +/* -- Methods -- */ + +int threading_mutex_initialize(threading_mutex m); + +int threading_mutex_lock(threading_mutex m); + +int threading_mutex_try_lock(threading_mutex m); + +int threading_mutex_unlock(threading_mutex m); + +int threading_mutex_destroy(threading_mutex m); + +static inline int threading_mutex_store(threading_mutex m, void *dest, void *src, size_t size) +{ + if (threading_mutex_lock(m) != 0) + { + return 1; + } + + memcpy(dest, src, size); + + return threading_mutex_unlock(m); +} + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_MUTEX_H */ diff --git a/source/threading/include/threading/threading_thread_id.h b/source/threading/include/threading/threading_thread_id.h new file mode 100644 index 0000000000..af111a4fe0 --- /dev/null +++ b/source/threading/include/threading/threading_thread_id.h @@ -0,0 +1,55 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_THREAD_ID_H +#define THREADING_THREAD_ID_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Definitions -- */ + +#define THREAD_ID_INVALID UINT64_MAX + +/* -- Methods -- */ + +/** +* @brief +* Return the current thread id depending on the platform +* +* @return +* Curent thread id casted to an uint64_t +*/ +THREADING_API uint64_t thread_id_get_current(void); + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_THREAD_ID_H */ diff --git a/source/threading/source/threading.c b/source/threading/source/threading.c new file mode 100644 index 0000000000..2d795819f0 --- /dev/null +++ b/source/threading/source/threading.c @@ -0,0 +1,40 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +const char *threading_print_info(void) +{ + static const char threading_info[] = + "Threading Library " METACALL_VERSION "\n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" + +#ifdef ADT_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif + + "\n"; + + return threading_info; +} diff --git a/source/threading/source/threading_mutex_macos.c b/source/threading/source/threading_mutex_macos.c new file mode 100644 index 0000000000..ed47663215 --- /dev/null +++ b/source/threading/source/threading_mutex_macos.c @@ -0,0 +1,62 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + memset(m, 0, sizeof(os_unfair_lock)); + + return 0; +} + +int threading_mutex_lock(threading_mutex m) +{ + os_unfair_lock_lock(m); + + return 0; +} + +int threading_mutex_try_lock(threading_mutex m) +{ + if (os_unfair_lock_trylock(m) == false) + { + return 1; + } + + return 0; +} + +int threading_mutex_unlock(threading_mutex m) +{ + os_unfair_lock_unlock(m); + + return 0; +} + +int threading_mutex_destroy(threading_mutex m) +{ + (void)m; + return 0; +} diff --git a/source/threading/source/threading_mutex_pthread.c b/source/threading/source/threading_mutex_pthread.c new file mode 100644 index 0000000000..4fc161d347 --- /dev/null +++ b/source/threading/source/threading_mutex_pthread.c @@ -0,0 +1,48 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + return pthread_mutex_init(m, NULL); +} + +int threading_mutex_lock(threading_mutex m) +{ + return pthread_mutex_lock(m); +} + +int threading_mutex_try_lock(threading_mutex m) +{ + return pthread_mutex_trylock(m); +} + +int threading_mutex_unlock(threading_mutex m) +{ + return pthread_mutex_unlock(m); +} + +int threading_mutex_destroy(threading_mutex m) +{ + return pthread_mutex_destroy(m); +} diff --git a/source/threading/source/threading_mutex_win32.c b/source/threading/source/threading_mutex_win32.c new file mode 100644 index 0000000000..aefdcfb1e1 --- /dev/null +++ b/source/threading/source/threading_mutex_win32.c @@ -0,0 +1,61 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + InitializeCriticalSection(m); + + return 0; +} + +int threading_mutex_lock(threading_mutex m) +{ + EnterCriticalSection(m); + + return 0; +} + +int threading_mutex_try_lock(threading_mutex m) +{ + if (TryEnterCriticalSection(m) == 0) + { + return 1; + } + + return 0; +} + +int threading_mutex_unlock(threading_mutex m) +{ + LeaveCriticalSection(m); + + return 0; +} + +int threading_mutex_destroy(threading_mutex m) +{ + DeleteCriticalSection(m); + + return 0; +} diff --git a/source/threading/source/threading_thread_id.c b/source/threading/source/threading_thread_id.c new file mode 100644 index 0000000000..0edf1db792 --- /dev/null +++ b/source/threading/source/threading_thread_id.c @@ -0,0 +1,96 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include + +#if (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #include +#endif + +#if defined(_WIN32) + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + #include + + #if defined(__MINGW32__) || defined(__MINGW64__) + #include + #endif +#elif defined(__linux__) || \ + (((defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__)) && (!defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12)) + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #include + #include + #include +#elif ((defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__)) && (defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12) + #include +#elif defined(__FreeBSD__) + #include +#elif defined(__HAIKU__) || defined(__BEOS__) + #include +#else + #error "Unsupported platform thread id" +#endif + +/* -- Methods -- */ + +uint64_t thread_id_get_current(void) +{ +#if defined(_WIN32) + return (uint64_t)GetCurrentThreadId(); +#elif defined(__linux__) + #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) + return (uint64_t)syscall(__NR_gettid); + #else + return (uint64_t)syscall(SYS_gettid); + #endif +#elif (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + uint64_t thread_id; + + pthread_threadid_np(NULL, &thread_id); + + return (uint64_t)thread_id; + #else + return (uint64_t)syscall(SYS_thread_selfid); + #endif +#elif defined(__FreeBSD__) + long thread_id = 0; + + thr_self(&thread_id); + + return (thread_id < 0) ? 0 : (uint64_t)thread_id; +#elif defined(__HAIKU__) || defined(__BEOS__) + return (uint64_t)thread_get_current_thread_id(); +#else + return THREAD_ID_INVALID; +#endif +} diff --git a/source/version/CMakeLists.txt b/source/version/CMakeLists.txt index 4326f51841..ff0bed815d 100644 --- a/source/version/CMakeLists.txt +++ b/source/version/CMakeLists.txt @@ -1,9 +1,3 @@ -# -# External dependencies -# - -# find_package(THIRDPARTY REQUIRED) - # # Library name and options # @@ -16,7 +10,6 @@ message(STATUS "Lib ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -39,22 +32,20 @@ include(SecurityFlags) string(TOUPPER ${META_PROJECT_NAME} META_PROJECT_NAME_UPPER) string(TOLOWER ${META_PROJECT_NAME} META_PROJECT_NAME_LOWER) -set(template_include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(template_source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(include_path "${CMAKE_BINARY_DIR}/source/include/${META_PROJECT_NAME_LOWER}") -set(source_path "${CMAKE_BINARY_DIR}/source/${META_PROJECT_NAME_LOWER}/source") +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(include_binary_path "${CMAKE_BINARY_DIR}/source/include/${META_PROJECT_NAME_LOWER}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") # Generate version-header -configure_file(${template_include_path}/version.h.in ${include_path}/${META_PROJECT_NAME_LOWER}_version.h) -configure_file(${template_source_path}/version.c.in ${source_path}/${META_PROJECT_NAME_LOWER}_version.c) +configure_file(${include_path}/version.h.in ${include_binary_path}/${META_PROJECT_NAME_LOWER}_version.h) set(headers - ${include_path}/${META_PROJECT_NAME_LOWER}_version.h + ${include_binary_path}/${META_PROJECT_NAME_LOWER}_version.h + ${include_path}/version.h ) set(sources - ${source_path}/${META_PROJECT_NAME_LOWER}_version.c + ${source_path}/version.c ) # Group source files @@ -62,8 +53,6 @@ set(header_group "Header Files (API)") set(source_group "Source Files") source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) # # Create library @@ -71,38 +60,15 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Build library add_library(${target} - ${sources} ${headers} + ${sources} ) # Create namespaced alias add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects -if(NOT OPTION_BUILD_DIST_LIBS) - export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -endif() - -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) # Create API export header generate_export_header(${target} @@ -130,6 +96,7 @@ target_include_directories(${target} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include + PUBLIC ${DEFAULT_INCLUDE_DIRECTORIES} @@ -158,6 +125,7 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE + ${target_upper}_EXPORTS # Export API PUBLIC $<$>:${target_upper}_STATIC_DEFINE> @@ -183,7 +151,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC @@ -191,17 +159,3 @@ target_link_libraries(${target} INTERFACE ) - -# -# Deployment -# - -# Library -if(NOT OPTION_BUILD_DIST_LIBS) - install(TARGETS ${target} - EXPORT "${target}-export" COMPONENT dev - RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime - LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime - ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev - ) -endif() diff --git a/source/version/include/version/version.h b/source/version/include/version/version.h new file mode 100644 index 0000000000..eae5523e89 --- /dev/null +++ b/source/version/include/version/version.h @@ -0,0 +1,36 @@ +/* + * CMake Versioning Utility by Parra Studios + * A template for generating versioning utilities. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef VERSION_H +#define VERSION_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +VERSION_API const char *version_print_info(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VERSION_H */ diff --git a/source/version/include/version/version.h.in b/source/version/include/version/version.h.in index deb66b3175..69341f7b5b 100644 --- a/source/version/include/version/version.h.in +++ b/source/version/include/version/version.h.in @@ -2,7 +2,7 @@ * CMake Versioning Utility by Parra Studios * A template for generating versioning utilities. * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,18 +21,6 @@ #ifndef ${META_PROJECT_NAME_UPPER}_VERSION_H #define ${META_PROJECT_NAME_UPPER}_VERSION_H 1 -/* -- Headers -- */ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Headers -- */ - -#include - /* -- Definitions -- */ #define ${META_PROJECT_NAME_UPPER}_PROJECT_NAME "@META_PROJECT_NAME@" @@ -53,89 +41,4 @@ extern "C" { #define ${META_PROJECT_NAME_UPPER}_VERSION "@META_VERSION@" #define ${META_PROJECT_NAME_UPPER}_NAME_VERSION "@META_NAME_VERSION@" -/* -- Member Data -- */ - -struct ${META_PROJECT_NAME}_version_type -{ - unsigned int major; - unsigned int minor; - unsigned int patch; - const char * revision; - const char * str; - const char * name; -}; - -/* -- Methods -- */ - -/** -* @brief -* Provide the module version struct -* -* @return -* Static struct containing unpacked version -*/ -VERSION_API const void * ${META_PROJECT_NAME}_version(void); - -/** -* @brief -* Provide the module version hexadecimal value -* with format 0xMMIIPPPP where M is @major, -* I is @minor and P is @patch -* -* @param[in] major -* Unsigned integer representing major version -* -* @param[in] minor -* Unsigned integer representing minor version -* -* @param[in] patch -* Unsigned integer representing patch version -* -* @return -* Hexadecimal integer containing packed version -*/ -VERSION_API uint32_t ${META_PROJECT_NAME}_version_hex_make(unsigned int major, unsigned int minor, unsigned int patch); - -/** -* @brief -* Provide the module version hexadecimal value -* with format 0xMMIIPPPP where M is major, -* I is minor and P is patch -* -* @return -* Hexadecimal integer containing packed version -*/ -VERSION_API uint32_t ${META_PROJECT_NAME}_version_hex(void); - -/** -* @brief -* Provide the module version string -* -* @return -* Static string containing module version -*/ -VERSION_API const char * ${META_PROJECT_NAME}_version_str(void); - -/** -* @brief -* Provide the module version revision string -* -* @return -* Static string containing module version revision -*/ -VERSION_API const char * ${META_PROJECT_NAME}_version_revision(void); - -/** -* @brief -* Provide the module version name -* -* @return -* Static string containing module version name -*/ -VERSION_API const char * ${META_PROJECT_NAME}_version_name(void); - -#ifdef __cplusplus -} -#endif - #endif /* ${META_PROJECT_NAME_UPPER}_VERSION_H */ diff --git a/source/version/source/version.c b/source/version/source/version.c new file mode 100644 index 0000000000..95cec396c5 --- /dev/null +++ b/source/version/source/version.c @@ -0,0 +1,40 @@ +/* + * CMake Versioning Utility by Parra Studios + * A template for generating versioning utilities. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +const char *version_print_info(void) +{ + static const char version_info[] = + "Version Library " METACALL_VERSION "\n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" + +#ifdef ADT_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif + + "\n"; + + return version_info; +} diff --git a/source/version/source/version.c.in b/source/version/source/version.c.in deleted file mode 100644 index d53375cf68..0000000000 --- a/source/version/source/version.c.in +++ /dev/null @@ -1,78 +0,0 @@ -/* - * CMake Versioning Utility by Parra Studios - * A template for generating versioning utilities. - * - * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* -- Headers -- */ - -#include <${META_PROJECT_NAME_LOWER}/${META_PROJECT_NAME_LOWER}_version.h> - -/* -- Methods -- */ - -const void * ${META_PROJECT_NAME}_version_data() -{ - static const struct ${META_PROJECT_NAME}_version_type version = - { - ${META_PROJECT_NAME_UPPER}_VERSION_MAJOR_ID, - ${META_PROJECT_NAME_UPPER}_VERSION_MINOR_ID, - ${META_PROJECT_NAME_UPPER}_VERSION_PATCH_ID, - ${META_PROJECT_NAME_UPPER}_VERSION_REVISION, - ${META_PROJECT_NAME_UPPER}_VERSION, - ${META_PROJECT_NAME_UPPER}_NAME_VERSION - }; - - return &version; -} - -uint32_t ${META_PROJECT_NAME}_version_hex_make(unsigned int major, unsigned int minor, unsigned int patch) -{ - const uint32_t version_hex = (major << 0x18) + (minor << 0x10) + patch; - - return version_hex; -} - -uint32_t ${META_PROJECT_NAME}_version_hex() -{ - static const uint32_t version_hex = - (${META_PROJECT_NAME_UPPER}_VERSION_MAJOR_ID << 0x18) + - (${META_PROJECT_NAME_UPPER}_VERSION_MINOR_ID << 0x10) + - ${META_PROJECT_NAME_UPPER}_VERSION_PATCH_ID; - - return version_hex; -} - -const char * ${META_PROJECT_NAME}_version_str() -{ - static const char version_string[] = ${META_PROJECT_NAME_UPPER}_VERSION; - - return version_string; -} - -const char * ${META_PROJECT_NAME}_version_revision() -{ - static const char version_revision[] = ${META_PROJECT_NAME_UPPER}_VERSION_REVISION; - - return version_revision; -} - -const char * ${META_PROJECT_NAME}_version_name() -{ - static const char version_name[] = ${META_PROJECT_NAME_UPPER}_NAME_VERSION; - - return version_name; -} diff --git a/tools/cli/Dockerfile b/tools/cli/Dockerfile index 79cdb5dd4c..0da28539b1 100644 --- a/tools/cli/Dockerfile +++ b/tools/cli/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,15 +37,15 @@ ENV LOADER_LIBRARY_PATH=/usr/local/lib \ CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json \ SERIAL_LIBRARY_PATH=/usr/local/lib \ DETOUR_LIBRARY_PATH=/usr/local/lib \ - PORT_LIBRARY_PATH=/usr/local/lib \ DEBIAN_FRONTEND=noninteractive \ - NODE_PATH=/usr/local/lib/node_modules + NODE_PATH=/usr/local/lib/node_modules \ + DOTNET_CLI_TELEMETRY_OPTOUT=true # Define working directory WORKDIR $LOADER_SCRIPT_PATH # Copy cli from builder -COPY --from=builder /usr/local/bin/metacallcli* /usr/local/bin/metacall +COPY --from=builder /usr/local/bin/metacallcli* /usr/local/bin/metacallcli # Define entry point -ENTRYPOINT [ "metacall" ] +ENTRYPOINT [ "metacallcli" ] diff --git a/tools/cli/hooks/build b/tools/cli/hooks/build deleted file mode 100755 index 1450a8d051..0000000000 --- a/tools/cli/hooks/build +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build core image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/cli/hooks/env b/tools/cli/hooks/env deleted file mode 100755 index 1ad02792bf..0000000000 --- a/tools/cli/hooks/env +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# CLI environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip -LOADER_LIBRARY_PATH=/usr/local/lib -LOADER_SCRIPT_PATH=/usr/local/scripts -CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json -SERIAL_LIBRARY_PATH=/usr/local/lib -DETOUR_LIBRARY_PATH=/usr/local/lib - -# Override docker image -IMAGE_NAME=metacall/core:cli diff --git a/tools/deps/Dockerfile b/tools/deps/Dockerfile index 00848a23aa..342bef8de3 100644 --- a/tools/deps/Dockerfile +++ b/tools/deps/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,16 +36,10 @@ ARG METACALL_PATH ARG METACALL_TOOLS_PATH # Environment variables -ENV DEBIAN_FRONTEND=noninteractive - -# Work around https://github.com/dotnet/cli/issues/1582 until Docker releases a -# fix (https://github.com/docker/docker/issues/20818). This workaround allows -# the container to be run with the default seccomp Docker settings by avoiding -# the restart_syscall made by LTTng which causes a failed assertion. -ENV LTTNG_UST_REGISTER_TIMEOUT=0 - -# Trigger the population of the local NuGet package cache -ENV NUGET_XMLDOC_MODE=skip +ENV DEBIAN_FRONTEND=noninteractive \ + LTTNG_UST_REGISTER_TIMEOUT=0 \ + NUGET_XMLDOC_MODE=skip \ + DOTNET_CLI_TELEMETRY_OPTOUT=true # Define working directory WORKDIR $METACALL_PATH @@ -54,8 +48,9 @@ WORKDIR $METACALL_PATH COPY tools/metacall-environment.sh tools/nobuildtest.patch $METACALL_TOOLS_PATH/ # Install MetaCall and runtimes then build +ARG METACALL_BUILD_TYPE ARG METACALL_INSTALL_OPTIONS RUN chmod 500 $METACALL_TOOLS_PATH/metacall-environment.sh \ - && $METACALL_TOOLS_PATH/metacall-environment.sh ${METACALL_INSTALL_OPTIONS} \ + && $METACALL_TOOLS_PATH/metacall-environment.sh ${METACALL_BUILD_TYPE} ${METACALL_INSTALL_OPTIONS} \ && rm -rf $METACALL_PATH diff --git a/tools/deps/hooks/build b/tools/deps/hooks/build deleted file mode 100644 index fb5056de52..0000000000 --- a/tools/deps/hooks/build +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build deps image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - --build-arg "METACALL_BASE_IMAGE=$METACALL_BASE_IMAGE" \ - --build-arg "METACALL_PATH=$METACALL_PATH" \ - --build-arg "METACALL_TOOLS_PATH=$METACALL_PATH/tools" \ - --build-arg "METACALL_INSTALL_OPTIONS=$METACALL_INSTALL_OPTIONS" \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/deps/hooks/env b/tools/deps/hooks/env deleted file mode 100644 index 3c2d51f549..0000000000 --- a/tools/deps/hooks/env +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# Base arguments -METACALL_INSTALL_OPTIONS="root base python ruby netcore2 nodejs file rapidjson funchook swig" # v8rep51 pack and coverage not needed in DockerHub - -# Base environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip - -# Override docker image -IMAGE_NAME=metacall/core:deps diff --git a/tools/dev/.dockerignore b/tools/dev/.dockerignore index 442125e2a4..ab1f6c4a43 100644 --- a/tools/dev/.dockerignore +++ b/tools/dev/.dockerignore @@ -6,6 +6,7 @@ !source/** !tools/metacall-configure.sh !tools/metacall-build.sh +!.clang-format !CMakeLists.txt !metacall-config.cmake.in !metacall-config-version.cmake.in @@ -13,4 +14,6 @@ !DESCRIPTION !LICENSE !README.md +!VERSION !WELCOME +source/loaders/rs_loader/rust/target diff --git a/tools/dev/Dockerfile b/tools/dev/Dockerfile index 1fc93b9607..ec35dfde43 100644 --- a/tools/dev/Dockerfile +++ b/tools/dev/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,8 +37,9 @@ ENV LOADER_LIBRARY_PATH=$METACALL_PATH/build \ CONFIGURATION_PATH=$METACALL_PATH/build/configurations/global.json \ SERIAL_LIBRARY_PATH=$METACALL_PATH/build \ DETOUR_LIBRARY_PATH=$METACALL_PATH/build \ - PORT_LIBRARY_PATH=$METACALL_PATH/build \ - DEBIAN_FRONTEND=noninteractive + DEBIAN_FRONTEND=noninteractive \ + NODE_PATH=/usr/lib/node_modules \ + DOTNET_CLI_TELEMETRY_OPTOUT=true # Define working directory WORKDIR $METACALL_PATH diff --git a/tools/dev/hooks/build b/tools/dev/hooks/build deleted file mode 100755 index 43d81de6e3..0000000000 --- a/tools/dev/hooks/build +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build dev image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - --build-arg "METACALL_PATH=$METACALL_PATH" \ - --build-arg "METACALL_BUILD_TYPE=$METACALL_BUILD_TYPE" \ - --build-arg "METACALL_BUILD_OPTIONS=$METACALL_BUILD_OPTIONS" \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/dev/hooks/env b/tools/dev/hooks/env deleted file mode 100755 index d83a81d688..0000000000 --- a/tools/dev/hooks/env +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# Core arguments -METACALL_BUILD_OPTIONS="root python ruby netcore2 nodejs file examples distributable tests scripts ports dynamic install" # v8 pack and coverage not needed in DockerHub - -# Core environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip -LOADER_LIBRARY_PATH=$METACALL_PATH/build -LOADER_SCRIPT_PATH=$METACALL_PATH/build/scripts -CONFIGURATION_PATH=$METACALL_PATH/build/configurations/global.json -SERIAL_LIBRARY_PATH=$METACALL_PATH/build -DETOUR_LIBRARY_PATH=$METACALL_PATH/build - -# Override docker image -IMAGE_NAME=metacall/core:dev diff --git a/tools/metacall-benchmarks-merge.py b/tools/metacall-benchmarks-merge.py new file mode 100644 index 0000000000..30270e269f --- /dev/null +++ b/tools/metacall-benchmarks-merge.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +import json +import os +import sys + +# Validate arguments +if len(sys.argv) != 2: + print('Invalid number of arguments, you should pass the location of the benchmarks.') + print('Usage: python3 metacall-benchmarks-merge.py ${CMAKE_BINARY_DIR}/benchmarks') + exit(1) + +# Validate the benchmark path +if not os.path.isdir(sys.argv[1]): + print('The directory \'' + sys.argv[1] + '\' does not exist or is not a valid path.') + exit(2) + +# Search all benchmarks and merge them +output = {} + +for file in os.listdir(sys.argv[1]): + if file.endswith('.json'): + f = open(os.path.join(sys.argv[1], file), 'r') + + # Sanitize the data (https://github.com/google/benchmark/issues/784) + # '-Infinity', 'Infinity', 'NaN' + def sanitize(arg): + c = { + '-Infinity': sys.float_info.min, # -float('inf') + 'Infinity': sys.float_info.max, # float('inf') + 'NaN': 0 # float('nan') + } + # TODO: Eventually solve this from the root of problem in Windows + print('Warning: Got value "' + arg + '" in the test ' + file + ', review why it is failing') + return c[arg] + + data = json.loads(f.read(), parse_constant=sanitize) + + if not output: + output = data + else: + for benchmark in data['benchmarks']: + output['benchmarks'].append(benchmark) + + f.close() + +# Check if we found data +if not output: + print('The directory \'' + sys.argv[1] + '\' does not contain any benchmark.') + exit(3) + +# Rename the binary file +output['context']['executable'] = 'benchmarks' + +# Store the result +dest = os.path.join(sys.argv[1], 'metacall-benchmarks.json') +f = open(dest, 'w+') +json.dump(output, f, indent = 4) +f.close() diff --git a/tools/metacall-build.ps1 b/tools/metacall-build.ps1 new file mode 100755 index 0000000000..ec95a2dd85 --- /dev/null +++ b/tools/metacall-build.ps1 @@ -0,0 +1,123 @@ +<# +# MetaCall Build PowerShell Script by Parra Studios +# Build and install powershell script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#> + +$Global:BUILD_TYPE = 'Release' +$Global:BUILD_TESTS = 0 +$Global:BUILD_BENCHMARKS = 0 +$Global:BUILD_COVERAGE = 0 +$Global:BUILD_INSTALL = 0 + +$Global:PROGNAME = $(Get-Item $PSCommandPath).Basename +$Global:Arguments = $args + +function Sub-Options { + for ($i = 0; $i -lt $Arguments.Length; $i++) { + $option = $Arguments[$i] + if ("$option" -eq "debug") { + echo "Build all scripts in debug mode" + $Global:BUILD_TYPE = 'Debug' + } + if ("$option" -eq "release") { + echo "Build all scripts in release mode" + $Global:BUILD_TYPE = 'Release' + } + if ("$option" -eq "relwithdebinfo") { + echo "Build all scripts in release mode with debug symbols" + $Global:BUILD_TYPE = 'RelWithDebInfo' + } + if ("$option" -eq "tests") { + echo "Build and run all tests" + $Global:BUILD_TESTS = 1 + } + if ("$option" -eq "benchmarks") { + echo "Build and run all benchmarks" + $Global:BUILD_BENCHMARKS = 1 + } + if ("$option" -eq "install") { + echo "Install all libraries" + $Global:BUILD_INSTALL = 1 + } + } +} + +function Sub-Build { + $Global:ExitCode = 0 + + # Build the project + echo "Building MetaCall..." + cmake --build . "-j$((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)" --config $BUILD_TYPE + + if (-not $?) { + $RecentExitCode = $LASTEXITCODE + echo "Failure in build with exit code: $RecentExitCode" + + $Global:ExitCode = $RecentExitCode + Exit $ExitCode + } + + # Tests + if (($BUILD_TESTS -eq 1) -or ($BUILD_BENCHMARKS -eq 1) -or ($BUILD_COVERAGE -eq 1)) { + echo "Running the tests..." + ctest "-j$((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)" --timeout 5400 --output-on-failure -C $BUILD_TYPE + + if (-not $?) { + $RecentExitCode = $LASTEXITCODE + echo "Failure in tests with exit code: $RecentExitCode" + + $Global:ExitCode = $RecentExitCode + Exit $ExitCode + } + } + + # Install + if ($BUILD_INSTALL -eq 1) { + echo "Building and installing MetaCall..." + cmake --build . --target install --config $BUILD_TYPE + + if (-not $?) { + $RecentExitCode = $LASTEXITCODE + echo "Failure in install with exit code: $RecentExitCode" + + $Global:ExitCode = $RecentExitCode + Exit $ExitCode + } + } + + Exit $ExitCode +} + +function Sub-Help { + echo "Usage: $PROGNAME list of options" + echo "Options:" + echo " debug | release | relwithdebinfo: build type" + echo " tests: build and run all tests" + echo " install: install all libraries" + echo "" +} + +switch($args.length) { + 0 { + Sub-Help + Break + } + Default { + Sub-Options + Sub-Build + } +} diff --git a/tools/metacall-build.sh b/tools/metacall-build.sh index 9e1a0d56d1..74826e22f2 100755 --- a/tools/metacall-build.sh +++ b/tools/metacall-build.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. +# MetaCall Build Shell Script by Parra Studios +# Build and install shell script utility for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,21 +19,24 @@ # limitations under the License. # -RUN_AS_ROOT=0 -SUDO_CMD=sudo +set -euxo + BUILD_TYPE=Release BUILD_TESTS=0 +BUILD_BENCHMARKS=0 BUILD_COVERAGE=0 BUILD_INSTALL=0 +# Check out for sudo +if [ "`id -u`" = '0' ]; then + SUDO_CMD="" +else + SUDO_CMD=sudo +fi + sub_options() { for option in "$@" do - if [ "$option" = 'root' ]; then - echo "Running build script as root" - RUN_AS_ROOT=1 - SUDO_CMD="" - fi if [ "$option" = 'debug' ]; then echo "Build all scripts in debug mode" BUILD_TYPE=Debug @@ -50,6 +53,10 @@ sub_options() { echo "Build and run all tests" BUILD_TESTS=1 fi + if [ "$option" = 'benchmarks' ]; then + echo "Build and run all benchmarks" + BUILD_BENCHMARKS=1 + fi if [ "$option" = 'coverage' ]; then echo "Build coverage reports" BUILD_COVERAGE=1 @@ -63,32 +70,34 @@ sub_options() { sub_build() { - # Make without distributable - make -k -j$(getconf _NPROCESSORS_ONLN) + # Build the project + make -j$(getconf _NPROCESSORS_ONLN) # Tests (coverage needs to run the tests) - if [ $BUILD_TESTS = 1 ] || [ $BUILD_COVERAGE = 1 ]; then - ctest -VV -C $BUILD_TYPE + if [ $BUILD_TESTS = 1 ] || [ $BUILD_BENCHMARKS=1 ] || [ $BUILD_COVERAGE = 1 ]; then + ctest -j$(getconf _NPROCESSORS_ONLN) --timeout 5400 --output-on-failure --test-output-size-failed 3221000000 -C $BUILD_TYPE fi # Coverage if [ $BUILD_COVERAGE = 1 ]; then - # TODO: Remove -k, solve coverage issues - make -k gcov - make -k lcov - make -k lcov-genhtml + ctest -j$(getconf _NPROCESSORS_ONLN) --timeout 5400 -T Coverage + gcovr -r ../source/ . --html-details coverage.html fi # Install if [ $BUILD_INSTALL = 1 ]; then - $SUDO_CMD make install + if [ "$SUDO_CMD" = "" ]; then + make install + else + # Needed for rustup in order to install rust loader properly + $SUDO_CMD HOME="$HOME" make install + fi fi } sub_help() { echo "Usage: `basename "$0"` list of options" echo "Options:" - echo " root: build being run by root" echo " debug | release | relwithdebinfo: build type" echo " tests: build and run all tests" echo " coverage: build coverage reports" diff --git a/tools/metacall-clear.sh b/tools/metacall-clear.sh deleted file mode 100755 index 6074ac3f0a..0000000000 --- a/tools/metacall-clear.sh +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Configuration Environment Bash Script by Parra Studios -# Remove all packages and unused data from MetaCall building and testing. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -ROOT_DIR=$(pwd) - -RUN_AS_ROOT=0 -SUDO_CMD=sudo -CLEAR_APT=0 -CLEAR_RAPIDJSON=0 -CLEAR_FUNCHOOK=0 -CLEAR_PYTHON=0 -CLEAR_RUBY=0 -CLEAR_NETCORE=0 -CLEAR_V8=0 -CLEAR_NODEJS=0 -CLEAR_TYPESCRIPT=0 -CLEAR_FILE=0 -CLEAR_SWIG=0 -CLEAR_PACK=0 -CLEAR_COVERAGE=0 -SHOW_HELP=0 -PROGNAME=$(basename $0) - -# Base packages -sub_apt(){ - echo "clean apt of C build" - $SUDO_CMD apt-get -y remove --purge build-essential git cmake wget apt-utils apt-transport-https gnupg dirmngr ca-certificates - $SUDO_CMD apt-get -y autoclean - $SUDO_CMD apt-get -y autoremove -} - -# RapidJSON -sub_rapidjson(){ - echo "clean rapidJSON" - $SUDO_CMD rm -rf /usr/local/lib/cmake - $SUDO_CMD rm -rf /usr/local/include/rapidjson -} - -# FuncHook -sub_funchook(){ - echo "clean funchook" -} - -# Python -sub_python(){ - echo "clean python" - /usr/bin/yes | $SUDO_CMD pip3 uninstall django - /usr/bin/yes | $SUDO_CMD pip3 uninstall requests - /usr/bin/yes | $SUDO_CMD pip3 uninstall rsa - /usr/bin/yes | $SUDO_CMD pip3 uninstall joblib -} - -# Ruby -sub_ruby(){ - echo "clean ruby" - # TODO: Review conflict with NodeJS (currently rails test is disabled) - #$SUDO_CMD gem uninstall rails - #$SUDO_CMD apt-get -y remove --purge nodejs -} - -# NetCore -sub_netcore(){ - echo "clean netcore" - $SUDO_CMD apt-get -y remove --purge libssl1.0-dev libkrb5-dev clang -} - -# V8 -sub_v8(){ - echo "clean v8" -} - -# NodeJS -sub_nodejs(){ - echo "clean nodejs" -} - -# TypeScript -sub_typescript(){ - echo "clean typescript" -} - -# File -sub_file(){ - echo "clean file" -} - -# SWIG -sub_swig(){ - echo "clean swig" - $SUDO_CMD apt-get -y remove --purge libpcre3-dev swig -} - -# MetaCall -sub_metacall(){ - echo "clean metacall" - $SUDO_CMD rm -rf /usr/local/share/metacall/data - $SUDO_CMD rm /usr/local/share/metacall/VERSION - $SUDO_CMD rm /usr/local/share/metacall/README.md - $SUDO_CMD rm /usr/local/share/metacall/metacall-config.cmake -} - -# Pack -sub_pack(){ - echo "clean pack" - $SUDO_CMD apt-get -y remove --purge rpm - - # Uninstall Python Port Dependencies (TODO: This must be removed when enabled the pip3 install metacall) - if [ $CLEAR_PYTHON = 1 ]; then - /usr/bin/yes | $SUDO_CMD pip3 uninstall setuptools - fi -} - -# Coverage -sub_coverage(){ - echo "clean pack" - $SUDO_CMD apt-get -y remove --purge lcov -} - -# Clear -sub_clear(){ - if [ $RUN_AS_ROOT = 1 ]; then - SUDO_CMD="" - fi - if [ $CLEAR_RAPIDJSON = 1 ]; then - sub_rapidjson - fi - if [ $CLEAR_FUNCHOOK = 1 ]; then - sub_funchook - fi - if [ $CLEAR_PYTHON = 1 ]; then - sub_python - fi - if [ $CLEAR_RUBY = 1 ]; then - sub_ruby - fi - if [ $CLEAR_NETCORE = 1 ]; then - sub_netcore - fi - if [ $CLEAR_V8 = 1 ]; then - sub_v8 - fi - if [ $CLEAR_NODEJS = 1 ]; then - sub_nodejs - fi - if [ $CLEAR_TYPESCRIPT = 1 ]; then - sub_typescript - fi - if [ $CLEAR_FILE = 1 ]; then - sub_file - fi - if [ $CLEAR_SWIG = 1 ]; then - sub_swig - fi - if [ $CLEAR_PACK = 1 ]; then - sub_pack - fi - if [ $CLEAR_COVERAGE = 1 ]; then - sub_coverage - fi - - # Clear aptitude (must be at the end) - if [ $CLEAR_APT = 1 ]; then - sub_apt - fi - - sub_metacall - - # Delete MetaCall path - rm -rf $METACALL_PATH - - echo "clean finished in workspace $ROOT_DIR" -} - -# Configuration -sub_options(){ - for var in "$@" - do - if [ "$var" = 'root' ]; then - echo "running as root" - RUN_AS_ROOT=1 - fi - if [ "$var" = 'base' ]; then - echo "apt selected" - CLEAR_APT=1 - fi - if [ "$var" = 'rapidjson' ]; then - echo "rapidjson selected" - CLEAR_RAPIDJSON=1 - fi - if [ "$var" = 'funchook' ]; then - echo "funchook selected" - CLEAR_FUNCHOOK=1 - fi - if [ "$var" = 'python' ]; then - echo "python selected" - CLEAR_PYTHON=1 - fi - if [ "$var" = 'ruby' ]; then - echo "ruby selected" - CLEAR_RUBY=1 - fi - if [ "$var" = 'netcore' ]; then - echo "netcore selected" - CLEAR_NETCORE=1 - fi - if [ "$var" = 'v8' ]; then - echo "v8 selected" - CLEAR_V8=1 - fi - if [ "$var" = 'nodejs' ]; then - echo "nodejs selected" - CLEAR_NODEJS=1 - fi - if [ "$var" = 'typescript' ]; then - echo "typescript selected" - CLEAR_NODEJS=1 - fi - if [ "$var" = 'file' ]; then - echo "file selected" - CLEAR_FILE=1 - fi - if [ "$var" = 'swig' ]; then - echo "swig selected" - CLEAR_SWIG=1 - fi - if [ "$var" = 'pack' ]; then - echo "pack selected" - CLEAR_PACK=1 - fi - if [ "$var" = 'coverage' ]; then - echo "coverage selected" - CLEAR_COVERAGE=1 - fi - done -} - -# Help -sub_help() { - echo "Usage: `basename "$0"` list of component" - echo "Components:" - echo " root" - echo " base" - echo " rapidjson" - echo " funchook" - echo " python" - echo " ruby" - echo " netcore" - echo " v8" - echo " nodejs" - echo " typescript" - echo " file" - echo " swig" - echo " pack" - echo " coverage" - echo "" -} - -case "$#" in - 0) - sub_help - ;; - *) - sub_options $@ - sub_clear - ;; -esac diff --git a/tools/metacall-configure.ps1 b/tools/metacall-configure.ps1 new file mode 100755 index 0000000000..ea3928e666 --- /dev/null +++ b/tools/metacall-configure.ps1 @@ -0,0 +1,477 @@ +<# +# MetaCall Build PowerShell Script by Parra Studios +# Build and install powershell script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#> + +$Global:ROOT_DIR = "$(pwd)" + +$Global:BUILD_TYPE = 'Release' +$Global:BUILD_PYTHON = 0 +$Global:BUILD_RUBY = 0 +$Global:BUILD_NETCORE = 0 +$Global:BUILD_NETCORE2 = 0 +$Global:BUILD_NETCORE5 = 0 +$Global:BUILD_V8 = 0 +$Global:BUILD_NODEJS = 0 +$Global:BUILD_TYPESCRIPT = 0 +$Global:BUILD_RUST = 0 +$Global:BUILD_ZIG = 0 +$Global:BUILD_FILE = 0 +$Global:BUILD_RPC = 0 +$Global:BUILD_WASM = 0 +$Global:BUILD_JAVA = 0 +$Global:BUILD_C = 0 +$Global:BUILD_COBOL = 0 +$Global:BUILD_SCRIPTS = 0 +$Global:BUILD_EXAMPLES = 0 +$Global:BUILD_TESTS = 0 +$Global:BUILD_BENCHMARKS = 0 +$Global:BUILD_PORTS = 0 +$Global:BUILD_ADDRESS_SANITIZER = 0 +$Global:BUILD_THREAD_SANITIZER = 0 +$Global:BUILD_MEMORY_SANITIZER = 0 +$Global:PROGNAME = $(Get-Item $PSCommandPath).Basename + +$Global:Arguments = $args + +function sub-options { + for ($i = 0; $i -lt $Arguments.Length; $i++) { + $option = $Arguments[$i] + if ("$option" -eq 'debug') { + echo "Build all scripts in debug mode" + $Global:BUILD_TYPE = 'Debug' + } + if ("$option" -eq 'release') { + echo "Build all scripts in release mode" + $Global:BUILD_TYPE = 'Release' + } + if ("$option" -eq 'relwithdebinfo') { + echo "Build all scripts in release mode with debug symbols" + $Global:BUILD_TYPE = 'RelWithDebInfo' + } + if ("$option" -eq 'python') { + echo "Build with python support" + $Global:BUILD_PYTHON = 1 + } + if ("$option" -eq 'ruby') { + echo "Build with ruby support" + $Global:BUILD_RUBY = 1 + } + if ("$option" -eq 'netcore') { + echo "Build with netcore support" + $Global:BUILD_NETCORE = 1 + } + if ("$option" -eq 'netcore2') { + echo "Build with netcore 2 support" + $Global:BUILD_NETCORE2 = 1 + } + if ("$option" -eq 'netcore5') { + echo "Build with netcore 5 support" + $Global:BUILD_NETCORE5 = 1 + } + if ("$option" -eq 'v8') { + echo "Build with v8 support" + $Global:BUILD_V8 = 1 + } + if ("$option" -eq 'nodejs') { + echo "Build with nodejs support" + $Global:BUILD_NODEJS = 1 + } + if ("$option" -eq 'typescript') { + echo "Build with typescript support" + $Global:BUILD_TYPESCRIPT = 1 + } + if ("$option" -eq 'rust') { + echo "Build with rust support" + $Global:BUILD_RUST = 1 + } + if ("$option" -eq 'zig') { + echo "Build with zig support" + $Global:BUILD_ZIG = 1 + } + if ("$option" -eq 'file') { + echo "Build with file support" + $Global:BUILD_FILE = 1 + } + if ("$option" -eq 'rpc') { + echo "Build with rpc support" + $Global:BUILD_RPC = 1 + } + if ("$option" -eq 'wasm') { + echo "Build with wasm support" + $Global:BUILD_WASM = 1 + } + if ("$option" -eq 'java') { + echo "Build with java support" + $Global:BUILD_JAVA = 1 + } + if ("$option" -eq 'c') { + echo "Build with c support" + $Global:BUILD_C = 1 + } + if ("$option" -eq 'cobol') { + echo "Build with cobol support" + $Global:BUILD_COBOL = 1 + } + if ("$option" -eq 'scripts') { + echo "Build all scripts" + $Global:BUILD_SCRIPTS = 1 + } + if ("$option" -eq 'examples') { + echo "Build all examples" + $Global:BUILD_EXAMPLES = 1 + } + if ("$option" -eq 'tests') { + echo "Build all tests" + $Global:BUILD_TESTS = 1 + } + if ("$option" -eq 'benchmarks') { + echo "Build all benchmarks" + $Global:BUILD_BENCHMARKS = 1 + } + if ("$option" -eq 'ports') { + echo "Build all ports" + $Global:BUILD_PORTS = 1 + } + if ("$option" -eq 'address-sanitizer') { + echo "Build with address sanitizers" + $Global:BUILD_ADDRESS_SANITIZER = 1 + } + if ("$option" -eq 'thread-sanitizer') { + echo "Build with thread sanitizers" + $Global:BUILD_THREAD_SANITIZER = 1 + } + if ("$option" -eq 'memory-sanitizer') { + echo "Build with memory sanitizers" + $Global:BUILD_MEMORY_SANITIZER = 1 + } + } +} + +function sub-configure { + $Global:BUILD_STRING = "-DOPTION_BUILD_LOG_PRETTY=Off " ` + + "-DOPTION_BUILD_LOADERS=On " ` + + "-DOPTION_BUILD_LOADERS_MOCK=On" + + # Scripts + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS=Off" + } + + # Python + if ($BUILD_PYTHON -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_PY=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_PY=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_PY=On" + } + } + + # Ruby + if ($BUILD_RUBY -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_RB=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RB=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_RB=On" + } + } + + # NetCore + if ($BUILD_NETCORE -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING " ` + + "-DOPTION_BUILD_LOADERS_CS=On " ` + + "-DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/1.1.10/" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + } + } + + # NetCore 2 + if ($BUILD_NETCORE2 -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING " ` + + "-DOPTION_BUILD_LOADERS_CS=On " ` + + "-DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + } + } + + # NetCore 5 + if ($BUILD_NETCORE5 -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING " ` + + "-DOPTION_BUILD_LOADERS_CS=On " ` + + "-DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/5.0.12/" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + } + } + + # V8 + if ($BUILD_V8 -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_JS=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_JS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_JS=On" + } + } + + # NodeJS + if ($BUILD_NODEJS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_NODE=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_NODE=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_NODE=On" + } + } + + # TypeScript + if ($BUILD_TYPESCRIPT -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_TS=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_TS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_TS=On" + } + } + + # Rust + if ($BUILD_RUST -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_RS=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_RS=On" + } + } + + # Zig + if ($BUILD_ZIG -eq 1) { + # TODO + # $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_ZIG=On" + + # if ($BUILD_SCRIPTS -eq 1) { + # $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_ZIG=On" + # } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_ZIG=On" + } + } + + # File + if ($BUILD_FILE -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_FILE=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_FILE=On" + } + } + + # RPC + if ($BUILD_RPC -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_RPC=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RPC=On" + } + } + + # WebAssembly + if ($BUILD_WASM -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_WASM=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_WASM=On" + } + } + + # Java + if ($BUILD_JAVA -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_JAVA=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_JAVA=On" + } + } + + # C + if ($BUILD_C -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_C=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_C=On" + } + } + + # Cobol + if ($BUILD_COBOL -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_COB=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_COB=On" + } + } + + # Examples + if ($BUILD_EXAMPLES -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_EXAMPLES=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_EXAMPLES=Off" + } + + # Tests + if ($BUILD_TESTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_TESTS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_TESTS=Off" + } + + # Benchmarks + if ($BUILD_BENCHMARKS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_BENCHMARKS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_BENCHMARKS=Off" + } + + # Ports + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS=Off" + } + + # Address Sanitizer + if ($BUILD_ADDRESS_SANITIZER -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=Off" + } + + # Thread Sanitizer + if ($BUILD_THREAD_SANITIZER -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=Off" + } + + # Memory Sanitizer + if ($BUILD_MEMORY_SANITIZER -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=Off" + } + + # Build type + $Global:BUILD_STRING = "$BUILD_STRING -DCMAKE_BUILD_TYPE=$BUILD_TYPE" + + # Other Environment Options + $EnvOpts = Get-Content "$ROOT_DIR\CMakeConfig.txt" + + foreach ($opt in $EnvOpts.Split([System.Environment]::NewLine)) { + $Global:BUILD_STRING = "$BUILD_STRING $opt" + } + + # Execute CMake + $CustomFlags = '-DOPTION_BUILD_SECURITY=OFF -DOPTION_FORK_SAFE=OFF -DWARNINGS_ENABLED=OFF' # TODO: Enable warnings when all tests pass + echo "BUILD COMMAND: cmake $CustomFlags $BUILD_STRING .." + cmd.exe /c "cmake $CustomFlags $BUILD_STRING .." + + Exit $LASTEXITCODE +} + +function sub-help { + echo "Usage: $PROGNAME list of options" + echo "Options:" + echo " debug | release | relwithdebinfo: build type" + echo " python: build with python support" + echo " ruby: build with ruby support" + echo " netcore: build with netcore support" + echo " netcore2: build with netcore 2 support" + echo " netcore5: build with netcore 5 support" + echo " v8: build with v8 support" + echo " nodejs: build with nodejs support" + echo " typescript: build with typescript support" + echo " rust: build with rust support" + echo " zig: build with zig support" + echo " file: build with file support" + echo " rpc: build with rpc support" + echo " wasm: build with wasm support" + echo " java: build with java support" + echo " c: build with c support" + echo " cobol: build with cobol support" + echo " scripts: build all scripts" + echo " examples: build all examples" + echo " tests: build and run all tests" + echo " benchmarks: build and run all benchmarks" + echo " install: install all libraries" + echo " static: build as static libraries" + echo " ports: build all ports" + echo " address-sanitizer: build with address sanitizer" + echo " thread-sanitizer: build with thread sanitizer" + echo " memory-sanitizer: build with memory sanitizer" + echo "" +} + +switch($args.length) { + 0 { + sub-help + break + } + Default { + sub-options + sub-configure + } +} diff --git a/tools/metacall-configure.sh b/tools/metacall-configure.sh index 9ffa7e653e..7847a0d347 100755 --- a/tools/metacall-configure.sh +++ b/tools/metacall-configure.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. +# MetaCall Build Shell Script by Parra Studios +# Build and install shell script utility for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,33 +19,64 @@ # limitations under the License. # -RUN_AS_ROOT=0 -SUDO_CMD=sudo +set -euxo + +ROOT_DIR=$(pwd) BUILD_TYPE=Release BUILD_PYTHON=0 BUILD_RUBY=0 BUILD_NETCORE=0 BUILD_NETCORE2=0 +BUILD_NETCORE5=0 +BUILD_NETCORE7=0 +BUILD_NETCORE8=0 BUILD_V8=0 BUILD_NODEJS=0 BUILD_TYPESCRIPT=0 BUILD_FILE=0 +BUILD_RPC=0 +BUILD_WASM=0 +BUILD_JAVA=0 +BUILD_C=0 +BUILD_COBOL=0 +BUILD_GO=0 +BUILD_RUST=0 +BUILD_ZIG=0 BUILD_SCRIPTS=0 BUILD_EXAMPLES=0 -BUILD_DISTRIBUTABLE=0 BUILD_TESTS=0 BUILD_BENCHMARKS=0 BUILD_PORTS=0 +BUILD_SANDBOX=0 BUILD_COVERAGE=0 +BUILD_ADDRESS_SANITIZER=0 +BUILD_THREAD_SANITIZER=0 +BUILD_MEMORY_SANITIZER=0 + +# Operative System detection +case "$(uname -s)" in + Linux*) OPERATIVE_SYSTEM=Linux;; + Darwin*) OPERATIVE_SYSTEM=Darwin;; + CYGWIN*) OPERATIVE_SYSTEM=Cygwin;; + MINGW*) OPERATIVE_SYSTEM=MinGW;; + *) OPERATIVE_SYSTEM="Unknown" +esac + +# Linux Distro detection +if [ -f /etc/os-release ]; then # Either Debian or Ubuntu + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_DISTRO=$(cat /etc/os-release | grep "^ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_VERSION_ID=$(cat /etc/os-release | grep "^VERSION_ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') +else + # TODO: Implement more distros or better detection + LINUX_DISTRO=unknown + LINUX_VERSION_ID=unknown +fi sub_options() { for option in "$@" do - if [ "$option" = 'root' ]; then - echo "Running build script as root" - RUN_AS_ROOT=1 - SUDO_CMD="" - fi if [ "$option" = 'debug' ]; then echo "Build all scripts in debug mode" BUILD_TYPE=Debug @@ -74,6 +105,18 @@ sub_options() { echo "Build with netcore 2 support" BUILD_NETCORE2=1 fi + if [ "$option" = 'netcore5' ]; then + echo "Build with netcore 5 support" + BUILD_NETCORE5=1 + fi + if [ "$option" = 'netcore7' ]; then + echo "Build with netcore 7 support" + BUILD_NETCORE7=1 + fi + if [ "$option" = 'netcore8' ]; then + echo "Build with netcore 8 support" + BUILD_NETCORE8=1 + fi if [ "$option" = 'v8' ]; then echo "Build with v8 support" BUILD_V8=1 @@ -90,6 +133,38 @@ sub_options() { echo "Build with file support" BUILD_FILE=1 fi + if [ "$option" = 'rpc' ]; then + echo "Build with rpc support" + BUILD_RPC=1 + fi + if [ "$option" = 'wasm' ]; then + echo "Build with wasm support" + BUILD_WASM=1 + fi + if [ "$option" = 'java' ]; then + echo "Build with java support" + BUILD_JAVA=1 + fi + if [ "$option" = 'c' ]; then + echo "Build with c support" + BUILD_C=1 + fi + if [ "$option" = 'cobol' ]; then + echo "Build with cobol support" + BUILD_COBOL=1 + fi + if [ "$option" = 'go' ]; then + echo "Build with go support" + BUILD_GO=1 + fi + if [ "$option" = 'rust' ]; then + echo "Build with rust support" + BUILD_RUST=1 + fi + if [ "$option" = 'zig' ]; then + echo "Build with zig support" + BUILD_ZIG=1 + fi if [ "$option" = 'scripts' ]; then echo "Build all scripts" BUILD_SCRIPTS=1 @@ -98,10 +173,6 @@ sub_options() { echo "Build all examples" BUILD_EXAMPLES=1 fi - if [ "$option" = 'distributable' ]; then - echo "Build distributable libraries" - BUILD_DISTRIBUTABLE=1 - fi if [ "$option" = 'tests' ]; then echo "Build all tests" BUILD_TESTS=1 @@ -114,18 +185,45 @@ sub_options() { echo "Build all ports" BUILD_PORTS=1 fi + if [ "$option" = 'sandbox' ]; then + echo "Build with sandboxing support" + BUILD_SANDBOX=1 + fi if [ "$option" = 'coverage' ]; then echo "Build all coverage reports" BUILD_COVERAGE=1 fi + if [ "$option" = 'address-sanitizer' ]; then + echo "Build with address sanitizer" + BUILD_ADDRESS_SANITIZER=1 + fi + if [ "$option" = 'thread-sanitizer' ]; then + echo "Build with thread sanitizer" + BUILD_THREAD_SANITIZER=1 + fi + if [ "$option" = 'memory-sanitizer' ]; then + echo "Build with memory sanitizer" + BUILD_MEMORY_SANITIZER=1 + fi done } +sub_find_dotnet_runtime() { + NETCORE_BASE_PATH=`dotnet --list-runtimes | grep -m 1 "Microsoft.NETCore.App $1"` + echo "`echo \"$NETCORE_BASE_PATH\" | awk '{ print $3 }' | tail -c +2 | head -c -2`/`echo \"$NETCORE_BASE_PATH\" | awk '{ print $2 }'`/" +} + sub_configure() { BUILD_STRING="-DOPTION_BUILD_LOG_PRETTY=Off \ -DOPTION_BUILD_LOADERS=On \ -DOPTION_BUILD_LOADERS_MOCK=On" + + # Enable build with musl libc + if [ "$LINUX_DISTRO" = "alpine" ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_MUSL=On" + fi + # Scripts if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS=On" @@ -135,9 +233,13 @@ sub_configure() { # Python if [ $BUILD_PYTHON = 1 ]; then - BUILD_STRING="$BUILD_STRING \ - -DPYTHON_EXECUTABLE=/usr/bin/python3.7 \ - -DOPTION_BUILD_LOADERS_PY=On" + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_PY=On" + + # Patch for Darwin Python headers + if [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + BUILD_STRING="$BUILD_STRING -DPython3_INCLUDE_DIR=$(python3 -c "import sysconfig; print(sysconfig.get_path('include'))")" + # BUILD_STRING="$BUILD_STRING -DPython3_LIBRARY=$(python3 -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")" + fi if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_PY=On" @@ -165,7 +267,7 @@ sub_configure() { if [ $BUILD_NETCORE = 1 ]; then BUILD_STRING="$BUILD_STRING \ -DOPTION_BUILD_LOADERS_CS=On \ - -DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/1.1.10/" + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 1`" if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" @@ -180,7 +282,52 @@ sub_configure() { if [ $BUILD_NETCORE2 = 1 ]; then BUILD_STRING="$BUILD_STRING \ -DOPTION_BUILD_LOADERS_CS=On \ - -DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/" + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 2`" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi + fi + + # NetCore 5 + if [ $BUILD_NETCORE5 = 1 ]; then + BUILD_STRING="$BUILD_STRING \ + -DOPTION_BUILD_LOADERS_CS=On \ + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 5`" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi + fi + + # NetCore 7 + if [ $BUILD_NETCORE7 = 1 ]; then + BUILD_STRING="$BUILD_STRING \ + -DOPTION_BUILD_LOADERS_CS=On \ + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 7`" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi + fi + + # NetCore 8 + if [ $BUILD_NETCORE8 = 1 ]; then + BUILD_STRING="$BUILD_STRING \ + -DOPTION_BUILD_LOADERS_CS=On \ + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 8`" if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" @@ -239,6 +386,96 @@ sub_configure() { fi fi + # RPC + if [ $BUILD_RPC = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_RPC=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RPC=On" + fi + fi + + # WebAssembly + if [ $BUILD_WASM = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_WASM=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_WASM=On" + fi + fi + + # Java + if [ $BUILD_JAVA = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_JAVA=On" + + if [ "$LINUX_DISTRO" = "alpine" ]; then + BUILD_STRING="$BUILD_STRING -DJAVA_HOME=/usr/lib/jvm/java-1.8-openjdk" + fi + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_JAVA=On" + fi + fi + + # C + if [ $BUILD_C = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_C=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_C=On" + fi + fi + + # Cobol + if [ $BUILD_COBOL = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_COB=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_COB=On" + fi + fi + + # Go + if [ $BUILD_GO = 1 ]; then + # TODO + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_GO=On" + + # if [ $BUILD_SCRIPTS = 1 ]; then + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_GO=On" + # fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_GO=On" + fi + fi + + # Rust + if [ $BUILD_RUST = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_RS=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_RS=On" + fi + fi + + # Zig + if [ $BUILD_ZIG = 1 ]; then + # TODO + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_ZIG=On" + + # if [ $BUILD_SCRIPTS = 1 ]; then + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_ZIG=On" + # fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_ZIG=On" + fi + fi + # Examples if [ $BUILD_EXAMPLES = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_EXAMPLES=On" @@ -246,13 +483,6 @@ sub_configure() { BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_EXAMPLES=Off" fi - # Distributable - if [ $BUILD_DISTRIBUTABLE = 1 ]; then - BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_DIST_LIBS=On" - else - BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_DIST_LIBS=Off" - fi - # Tests if [ $BUILD_TESTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_TESTS=On" @@ -274,6 +504,13 @@ sub_configure() { BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS=Off" fi + # Sandbox + if [ $BUILD_SANDBOX = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PLUGINS_SANDBOX=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PLUGINS_SANDBOX=Off" + fi + # Coverage if [ $BUILD_COVERAGE = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_COVERAGE=On" @@ -281,36 +518,78 @@ sub_configure() { BUILD_STRING="$BUILD_STRING -DOPTION_COVERAGE=Off" fi + # Address Sanitizer + if [ $BUILD_ADDRESS_SANITIZER = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=Off" + fi + + # Thread Sanitizer + if [ $BUILD_THREAD_SANITIZER = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=Off" + fi + + # Memory Sanitizer + if [ $BUILD_MEMORY_SANITIZER = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=Off" + fi + + # Split cmake config file line by line and add each line to the build string + CMAKE_CONFIG_FILE="$ROOT_DIR/CMakeConfig.txt" + if [ -f $CMAKE_CONFIG_FILE ]; then + while IFS= read -r line + do + BUILD_STRING="$BUILD_STRING $line" + done < "$CMAKE_CONFIG_FILE" + fi + # Build type BUILD_STRING="$BUILD_STRING -DCMAKE_BUILD_TYPE=$BUILD_TYPE" - + # Execute CMake - cmake -Wno-dev $BUILD_STRING .. + cmake -Wno-dev -DOPTION_GIT_HOOKS=Off $BUILD_STRING .. } sub_help() { echo "Usage: `basename "$0"` list of options" echo "Options:" - echo " root: build being run by root" echo " debug | release | relwithdebinfo: build type" echo " python: build with python support" echo " ruby: build with ruby support" echo " netcore: build with netcore support" echo " netcore2: build with netcore 2 support" + echo " netcore5: build with netcore 5 support" + echo " netcore7: build with netcore 7 support" + echo " netcore8: build with netcore 8 support" echo " v8: build with v8 support" echo " nodejs: build with nodejs support" echo " typescript: build with typescript support" echo " file: build with file support" + echo " rpc: build with rpc support" + echo " wasm: build with wasm support" + echo " java: build with java support" + echo " c: build with c support" + echo " cobol: build with cobol support" + echo " go: build with go support" + echo " rust: build with rust support" + echo " zig: build with zig support" echo " scripts: build all scripts" echo " examples: build all examples" - echo " distributable: build distributable libraries" echo " tests: build and run all tests" echo " benchmarks: build and run all benchmarks" echo " install: install all libraries" echo " static: build as static libraries" - echo " dynamic: build as dynamic libraries" echo " ports: build all ports" + echo " sandbox: build with sandboxing support" echo " coverage: build all coverage reports" + echo " address-sanitizer: build with address sanitizer" + echo " thread-sanitizer: build with thread sanitizer" + echo " memory-sanitizer: build with memory sanitizer" echo "" } diff --git a/tools/metacall-environment.ps1 b/tools/metacall-environment.ps1 new file mode 100755 index 0000000000..486edf7713 --- /dev/null +++ b/tools/metacall-environment.ps1 @@ -0,0 +1,412 @@ +<# +# MetaCall Build PowerShell Script by Parra Studios +# Build and install powershell script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#> + +$PSDefaultParameterValues['*:Encoding'] = 'utf8' + +$Global:ROOT_DIR = "$(Get-Location)" +$Global:SHOW_HELP = 0 +$Global:PROGNAME = $(Get-Item $PSCommandPath).Basename +$Global:Arguments = $args + +function Set-Python { + Write-Output "Install Python" + Set-Location $ROOT_DIR + + $PythonVersion = '3.9.7' + $RuntimeDir = "$env:ProgramFiles\Python3" + $DepsDir = "$ROOT_DIR\dependencies" + + mkdir -Force $DepsDir + mkdir -Force $RuntimeDir + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\python_installer.exe")) { + # Download installer + Write-Output "Python installer not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://www.python.org/ftp/python/$PythonVersion/python-$PythonVersion-amd64.exe", "$(Get-Location)\python_installer.exe") + } + + Write-Output "Installing python $PythonVersion" + + # Install Python + ./python_installer.exe /quiet "TargetDir=$RuntimeDir" Include_debug=1 Include_symbols=1 PrependPath=1 CompileAll=1 + + # Set environment variables + Add-to-Path $RuntimeDir + Add-to-Path "$RuntimeDir\Scripts" + [Environment]::SetEnvironmentVariable("PIP_TARGET", "$RuntimeDir\Lib") + + # No patch, save vars for later use + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + $PythonRuntimeDir = $RuntimeDir.Replace('\', '/') + + Write-Output "-DPython3_VERSION=$PythonVersion" >> $EnvOpts + Write-Output "-DPython3_ROOT_DIR=""$PythonRuntimeDir""" >> $EnvOpts + Write-Output "-DPython3_EXECUTABLE=""$PythonRuntimeDir/python.exe""" >> $EnvOpts + Write-Output "-DPython3_INCLUDE_DIRS=""$PythonRuntimeDir/include""" >> $EnvOpts + Write-Output "-DPython3_LIBRARIES=""$PythonRuntimeDir/libs/python39_d.lib;$PythonRuntimeDir/libs/python39.lib""" >> $EnvOpts + Write-Output "-DPython3_Development_FOUND=1" >> $EnvOpts + Write-Output "-DPython3_FIND_REGISTRY=NEVER" >> $EnvOpts + + # Install dependencies for tests + pip3 install requests + pip3 install setuptools + pip3 install wheel + pip3 install rsa + pip3 install scipy + pip3 install numpy + pip3 install scikit-learn + pip3 install joblib +} + +function Set-Nodejs { + Write-Output "Install Node.js" + Set-Location $ROOT_DIR + + $DepsDir = "$ROOT_DIR\dependencies" + $NodeVersion = "14.18.2" + $DLLReleaseVer = "v0.0.1" + $RuntimeDir = "$env:ProgramFiles\nodejs" + + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\node.msi")) { + # Download installer + Write-Output "Nodejs MSI installer not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://nodejs.org/download/release/v$NodeVersion/node-v$NodeVersion-x64.msi", "$DepsDir\node.msi") + } + + if (!(Test-Path -Path "$DepsDir\node_headers.tar.gz")) { + # Download installer + Write-Output "Nodejs headers installer not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://nodejs.org/download/release/v$NodeVersion/node-v$NodeVersion-headers.tar.gz", "$DepsDir\node_headers.tar.gz") + } + + msiexec.exe /quiet /i "$DepsDir\node.msi" + + Add-to-Path $RuntimeDir + Add-to-Path "$RuntimeDir\include" + + cmake -E tar xzf node_headers.tar.gz + + mkdir "$RuntimeDir\include" + Robocopy.exe /move /e "$DepsDir\node-v$NodeVersion\include" "$RuntimeDir\include" /NFL /NDL /NJH /NJS /NC /NS /NP + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\node_dll.zip")) { + # Download installer + Write-Output "Nodejs Custom DLLs not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://github.com/metacall/node.dll/releases/download/$DLLReleaseVer/node-shared-v$NodeVersion-x64.zip", "$DepsDir\node_dll.zip") + } + + Expand-Archive -Path "node_dll.zip" -DestinationPath "$RuntimeDir\lib" + + $NodeDir = $RuntimeDir.Replace('\', '/') + + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + Write-Output "-DNodeJS_VERSION=""$NodeVersion""" >> $EnvOpts + Write-Output "-DNodeJS_INCLUDE_DIRS=""$NodeDir/include/node""" >> $EnvOpts + Write-Output "-DNodeJS_LIBRARY=""$NodeDir/lib/libnode.lib""" >> $EnvOpts + Write-Output "-DNodeJS_EXECUTABLE=""$NodeDir/node.exe""" >> $EnvOpts + Write-Output "-DNodeJS_LIBRARY_NAME=""libnode.dll""" >> $EnvOpts + Write-Output "-DNodeJS_LIBRARY_NAME_PATH=""$NodeDir/lib/libnode.dll""" >> $EnvOpts + + if ($Arguments -contains "c") { + # Required for test source/tests/metacall_node_port_c_lib_test + if (!(Test-Path -Path "$DepsDir\libgit2")) { + # Clone libgit2 + git clone --depth 1 --branch v1.8.4 https://github.com/libgit2/libgit2 + } + + $InstallDir = "$DepsDir\libgit2\build\dist" + + mkdir "$DepsDir\libgit2\build" + mkdir "$InstallDir" + Set-Location "$DepsDir\libgit2\build" + + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=OFF -DBUILD_CLI=OFF .. + cmake --build . "-j$((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)" + cmake --install . --prefix "$InstallDir" + + Write-Output "-DLibGit2_LIBRARY=""$InstallDir\lib\git2.lib""" >> $EnvOpts + Write-Output "-DLibGit2_INCLUDE_DIR=""$InstallDir\include""" >> $EnvOpts + } +} + +function Set-Java { + Write-Output "Install Java" + $JAVA_VERSION = "17.0.5" + $RuntimeDir = "$env:ProgramFiles\openjdk" + $DepsDir = "$ROOT_DIR\dependencies" + + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\openjdk.zip")) { + # Download installer + Write-Output "OpenJDK not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://aka.ms/download-jdk/microsoft-jdk-$JAVA_VERSION-windows-x64.zip", "$DepsDir\openjdk.zip") + } + + Expand-Archive -Path "openjdk.zip" -DestinationPath "$RuntimeDir" + robocopy /move /e "$RuntimeDir\jdk-$JAVA_VERSION+8" "$RuntimeDir" /NFL /NDL /NJH /NJS /NC /NS /NP + + Add-to-Path "JAVA_HOME=$RuntimeDir" + Add-to-Path "$RuntimeDir\bin" + Add-to-Path "$RuntimeDir\bin\server" +} + +function Set-Ruby { + Write-Output "Install Ruby" + + Set-Location $ROOT_DIR + $RuntimeDir = "$env:ProgramFiles\ruby" + $DepsDir = "$ROOT_DIR\dependencies" + + if (!(Test-Path -Path "$DepsDir\ruby-mswin.7z")) { + # Download installer + Write-Output "Ruby not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://github.com/metacall/ruby-loco/releases/download/ruby-master/ruby-mswin.7z", "$DepsDir\ruby-mswin.7z") + } + + mkdir "$DepsDir\ruby-mswin" + 7z x "$DepsDir\ruby-mswin.7z" -o"$DepsDir" + + robocopy /move /e "$DepsDir\ruby-mswin\" $RuntimeDir + + Add-to-Path "$RuntimeDir\bin" + + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + $RubyDir = $RuntimeDir.Replace('\', '/') + + Write-Output "-DRuby_VERSION_STRING=""3.5.0""" >> $EnvOpts + Write-Output "-DRuby_INCLUDE_DIR=""$RubyDir/include/ruby-3.5.0+0""" >> $EnvOpts + Write-Output "-DRuby_EXECUTABLE=""$RubyDir/bin/ruby.exe""" >> $EnvOpts + Write-Output "-DRuby_LIBRARY=""$RubyDir/lib/x64-vcruntime140-ruby350.lib""" >> $EnvOpts + Write-Output "-DRuby_LIBRARY_NAME=""$RubyDir/bin/x64-vcruntime140-ruby350.dll""" >> $EnvOpts + Write-Output "-DRuby_LIBRARY_SEARCH_PATHS=""$RubyDir/bin/ruby_builtin_dlls""" >> $EnvOpts +} + +function Set-TypeScript { + Write-Output "Install TypeScript" + npm i react@latest -g + npm i react-dom@latest -g +} + +function Set-Curl { + Write-Output "Installing cURL" + + Set-Location $ROOT_DIR + $RuntimeDir = "$env:ProgramFiles\curl" + $DepsDir = "$ROOT_DIR\dependencies" + $Version = "8.3.0_2" + + if (!(Test-Path -Path "$DepsDir\curl.zip")) { + # Download installer + Write-Output "Curl not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://curl.se/windows/dl-$Version/curl-$Version-win64-mingw.zip", "$DepsDir\curl.zip") + } + + Set-Location $DepsDir + + 7z x "$DepsDir\curl.zip" + + robocopy /move /e "$DepsDir\curl-$Version-win64-mingw" $RuntimeDir + + Add-to-Path "$RuntimeDir\bin" + + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + $CurlDir = $RuntimeDir.Replace('\', '/') + + $CURL_INCLUDE_DIR="$CurlDir/include" + $CURL_LIB="$CurlDir/lib/libcurl.dll.a" + $CURL_LIB_NAME="$CurlDir/bin/libcurl-x64.dll" + + Write-Output "-DCURL_INCLUDE_DIR=""$CURL_INCLUDE_DIR""" >> $EnvOpts + Write-Output "-DCURL_INCLUDE_DIRS=""$CURL_INCLUDE_DIR""" >> $EnvOpts + Write-Output "-DCURL_LIBRARY=""$CURL_LIB""" >> $EnvOpts + Write-Output "-DCURL_LIBRARY_NAME=""$CURL_LIB_NAME""" >> $EnvOpts +} + +function Add-to-Path { + $GivenPath = $args[0] + + $NewPath = "$GivenPath;$Env:PATH" + setx /M PATH $NewPath + $Env:PATH = $NewPath + $GivenPath >> $env:GITHUB_PATH + + if ($Null -ne $Env:GITHUB_ENV) { + Write-Output "PATH=$Env:PATH" >> $Env:GITHUB_ENV + } + + refreshenv + + Write-Output "PATH:: " $Env:PATH +} + +function Set-Base { + $DepsDir = "$ROOT_DIR\dependencies" + + # Check if 7zip is installed + if (!(Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | ?{$_.DisplayName -like "7-Zip*"})) { + Write-Output "Install 7zip" + + if (!(Test-Path -Path "$DepsDir\7zip.exe")) { + # Download installer + (New-Object Net.WebClient).DownloadFile("https://www.7-zip.org/a/7z2201-x64.exe", "$DepsDir\7zip.exe") + } + + # https://gist.github.com/dansmith65/7dd950f183af5f5deaf9650f2ad3226c + $installerPath = "$DepsDir\7zip.exe" + Start-Process -FilePath $installerPath -Args "/S" -Verb RunAs -Wait + Add-to-Path "$env:ProgramFiles\7-Zip" + } +} + +# Configure +function Configure { + # Create option variables file + mkdir "$ROOT_DIR\build" + New-Item -Path "$ROOT_DIR\build\CMakeConfig.txt" + + # Install base requirements + Set-Base + + for ($i = 0; $i -lt $Arguments.Length; $i++) { + $var = $Arguments[$i] + if ("$var" -eq 'python') { + Write-Output "python selected" + Set-Python + } + if ("$var" -eq 'ruby') { + Write-Output "ruby selected" + Set-Ruby + } + if ("$var" -eq 'netcore') { + Write-Output "netcore selected" + } + if ("$var" -eq 'netcore2') { + Write-Output "netcore 2 selected" + } + if ("$var" -eq 'netcore5') { + Write-Output "netcore 5 selected" + } + if ("$var" -eq 'rapidjson') { + Write-Output "rapidjson selected" + } + if (("$var" -eq 'v8') -or ("$var" -eq 'v8rep54')) { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep57') { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep58') { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep52') { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep51') { + Write-Output "v8 selected" + } + if ("$var" -eq 'nodejs') { + Write-Output "nodejs selected" + Set-Nodejs + } + if ("$var" -eq 'typescript') { + Write-Output "typescript selected" + Set-TypeScript + } + if ("$var" -eq 'file') { + Write-Output "file selected" + } + if ("$var" -eq 'rpc') { + Write-Output "rpc selected" + Set-Curl + } + if ("$var" -eq 'wasm') { + Write-Output "wasm selected" + } + if ("$var" -eq 'java') { + Write-Output "java selected" + Set-Java + } + if ("$var" -eq 'c') { + Write-Output "c selected" + } + if ("$var" -eq 'cobol') { + Write-Output "cobol selected" + } + if ("$var" -eq 'go') { + Write-Output "go selected" + } + if ("$var" -eq 'rust') { + Write-Output "rust selected" + } + if ("$var" -eq 'metacall') { + Write-Output "metacall selected" + } + if ("$var" -eq 'pack') { + Write-Output "pack selected" + } + if ("$var" -eq 'clangformat') { + Write-Output "clangformat selected" + } + } +} + +# Help +function Help { + Write-Output "Usage: $PROGNAME list of component" + Write-Output "Components:" + Write-Output " python" + Write-Output " ruby" + Write-Output " netcore" + Write-Output " netcore2" + Write-Output " netcore5" + Write-Output " rapidjson" + Write-Output " v8" + Write-Output " v8rep51" + Write-Output " v8rep54" + Write-Output " v8rep57" + Write-Output " v8rep58" + Write-Output " nodejs" + Write-Output " typescript" + Write-Output " file" + Write-Output " rpc" + Write-Output " wasm" + Write-Output " java" + Write-Output " c" + Write-Output " cobol" + Write-Output " go" + Write-Output " metacall" + Write-Output " pack" + Write-Output " clangformat" + Write-Output "" +} + +switch($args.length) { + 0 { + Help + Break + } + Default { + Configure + } +} diff --git a/tools/metacall-environment.sh b/tools/metacall-environment.sh index 881c318793..577889af99 100755 --- a/tools/metacall-environment.sh +++ b/tools/metacall-environment.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Configuration Environment Bash Script by Parra Studios +# MetaCall Configuration Environment Shell Script by Parra Studios # Configure and install MetaCall environment script utility. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,19 +19,22 @@ # limitations under the License. # +set -euxo + ROOT_DIR=$(pwd) -RUN_AS_ROOT=0 -SUDO_CMD=sudo APT_CACHE=0 APT_CACHE_CMD="" -INSTALL_APT=1 +BUILD_TYPE=Release +INSTALL_BASE=1 INSTALL_PYTHON=0 INSTALL_RUBY=0 INSTALL_RAPIDJSON=0 -INSTALL_FUNCHOOK=0 INSTALL_NETCORE=0 INSTALL_NETCORE2=0 +INSTALL_NETCORE5=0 +INSTALL_NETCORE7=0 +INSTALL_NETCORE8=0 INSTALL_V8=0 INSTALL_V8REPO=0 INSTALL_V8REPO58=0 @@ -42,58 +45,150 @@ INSTALL_V8REPO51=0 INSTALL_NODEJS=0 INSTALL_TYPESCRIPT=0 INSTALL_FILE=0 +INSTALL_RPC=0 INSTALL_WASM=0 -INSTALL_SWIG=0 -INSTALL_METACALL=0 +INSTALL_JAVA=0 +INSTALL_C=0 +INSTALL_COBOL=0 +INSTALL_GO=0 +INSTALL_RUST=0 INSTALL_PACK=0 INSTALL_COVERAGE=0 +INSTALL_CLANGFORMAT=0 +INSTALL_BACKTRACE=0 +INSTALL_SANDBOX=0 SHOW_HELP=0 PROGNAME=$(basename $0) -# Base packages -sub_apt(){ - echo "configure apt" - cd $ROOT_DIR - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install build-essential git cmake wget apt-utils apt-transport-https gnupg dirmngr ca-certificates -} - -# Swig -sub_swig(){ - echo "configure swig" - cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install g++ libpcre3-dev tar +# Operative System detection +case "$(uname -s)" in + Linux*) OPERATIVE_SYSTEM=Linux;; + Darwin*) OPERATIVE_SYSTEM=Darwin;; + CYGWIN*) OPERATIVE_SYSTEM=Cygwin;; + MINGW*) OPERATIVE_SYSTEM=MinGW;; + *) OPERATIVE_SYSTEM="Unknown" +esac - wget http://prdownloads.sourceforge.net/swig/swig-4.0.1.tar.gz +# Architecture detection +case "$(uname -m)" in + x86_64) ARCHITECTURE="amd64";; + arm64) ARCHITECTURE="arm64";; + *) ARCHITECTURE="Unknown";; +esac - tar -xzf swig-4.0.1.tar.gz - cd swig-4.0.1 - ./configure --prefix=/usr/local - make - $SUDO_CMD make install - cd .. - rm -rf swig-4.0.1 +# Check out for sudo +if [ "`id -u`" = '0' ]; then + SUDO_CMD="" +else + SUDO_CMD=sudo +fi - # Install Python Port Dependencies (TODO: This must be transformed into pip3 install metacall) - $SUDO_CMD pip3 install setuptools +# Linux Distro detection +if [ -f /etc/os-release ]; then # Either Debian or Ubuntu + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_DISTRO=$(cat /etc/os-release | grep "^ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_VERSION_ID=$(cat /etc/os-release | grep "^VERSION_ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') +else + # TODO: Implement more distros or better detection + LINUX_DISTRO=unknown + LINUX_VERSION_ID=unknown +fi +# Base packages +sub_base(){ + echo "configure base packages" + cd $ROOT_DIR + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends build-essential git cmake wget apt-utils apt-transport-https gnupg dirmngr ca-certificates + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk update + $SUDO_CMD apk add --no-cache g++ make git cmake wget gnupg ca-certificates + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install llvm cmake git wget gnupg ca-certificates + fi } # Python sub_python(){ echo "configure python" cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install python3 python3-dev python3-pip - $SUDO_CMD pip3 install django - $SUDO_CMD pip3 install requests - $SUDO_CMD pip3 install setuptools - $SUDO_CMD pip3 install wheel - $SUDO_CMD pip3 install rsa - $SUDO_CMD pip3 install scipy - $SUDO_CMD pip3 install numpy - $SUDO_CMD pip3 install scikit-learn - $SUDO_CMD pip3 install joblib + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + if [ "${BUILD_TYPE}" = "Debug" ]; then + PYTHON3_PKG=python3-dbg + else + PYTHON3_PKG=python3 + fi + + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends $PYTHON3_PKG python3-dev python3-pip + + # Python test dependencies + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends \ + python3-requests \ + python3-setuptools \ + python3-wheel \ + python3-rsa \ + python3-scipy \ + python3-numpy \ + python3-sklearn \ + python3-joblib + + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + # Fix to a lower Python version (3.9) in order avoid conflicts with Python dependency of Clang from C Loader + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main python3=3.9.16-r0 python3-dev=3.9.16-r0 + + # Python test dependencies + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/community \ + py3-pip=20.3.4-r1 \ + py3-rsa=4.7.2-r0 \ + py3-scipy=1.7.2-r0 \ + py3-numpy=1.21.4-r0 \ + py3-scikit-learn=0.24.0-r1 \ + py3-joblib=1.0.1-r1 + + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main \ + py3-requests=2.26.0-r1 \ + py3-setuptools=52.0.0-r4 \ + py3-wheel=0.36.2-r2 + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install pyenv openssl + export PKG_CONFIG_PATH=$(brew --prefix openssl)/lib/pkgconfig + export PYTHON_CONFIGURE_OPTS="--enable-shared" + PYTHON_VERSION_SMALL="3.13" + PYTHON_VERSION="${PYTHON_VERSION_SMALL}.0" + pyenv install ${PYTHON_VERSION} + pyenv global ${PYTHON_VERSION} + pyenv rehash + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + ENV_FILE="$ROOT_DIR/build/.env" + + echo eval "$(pyenv init -)" >> $ENV_FILE + . $ENV_FILE + + echo "-DPython3_INCLUDE_DIRS=$HOME/.pyenv/versions/${PYTHON_VERSION}/include/python${PYTHON_VERSION_SMALL}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_LIBRARY=$HOME/.pyenv/versions/${PYTHON_VERSION}/lib/libpython${PYTHON_VERSION_SMALL}.dylib" >> $CMAKE_CONFIG_PATH + echo "-DPython3_EXECUTABLE=$HOME/.pyenv/versions/${PYTHON_VERSION}/bin/python${PYTHON_VERSION_SMALL}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_ROOT=$HOME/.pyenv/versions/${PYTHON_VERSION}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_VERSION=${PYTHON_VERSION}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_FIND_FRAMEWORK=NEVER" >> $CMAKE_CONFIG_PATH + + pip3 install requests + pip3 install setuptools + pip3 install wheel + pip3 install rsa + pip3 install scipy + pip3 install numpy + pip3 install joblib + pip3 install scikit-learn + fi } # Ruby @@ -101,48 +196,47 @@ sub_ruby(){ echo "configure ruby" cd $ROOT_DIR - # TODO: Remove this when using ruby2.5 (not available yet because it fails on loading a script with a malloc error) - $SUDO_CMD mv /etc/apt/sources.list /etc/apt/sources.list.backup - $SUDO_CMD sh -c "echo \"deb http://ftp.debian.org/debian/ stretch main\" > /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb-src http://ftp.debian.org/debian/ stretch main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb http://security.debian.org/debian-security stretch/updates main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb-src http://security.debian.org/debian-security stretch/updates main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb http://ftp.debian.org/debian/ stretch-updates main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb-src http://ftp.debian.org/debian/ stretch-updates main\" >> /etc/apt/sources.list" - - $SUDO_CMD apt-get update - #$SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --allow-remove-essential install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev ruby2.5 libruby2.5 ruby2.5-dev - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --allow-remove-essential --allow-downgrades install libgmp10=2:6.1.2+dfsg-1 libgmp-dev libncurses5 libtinfo5 ruby2.3 libruby2.3 ruby2.3-dev - - # TODO: Review conflict with NodeJS (currently rails test is disabled) - #wget https://deb.nodesource.com/setup_4.x | $SUDO_CMD bash - - #$SUDO_CMD apt-get -y --no-install-recommends install nodejs - #$SUDO_CMD gem install rails - - # TODO: Remove this when using ruby2.5 (not available yet because it fails on loading a script with a malloc error) - $SUDO_CMD mv /etc/apt/sources.list.backup /etc/apt/sources.list + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends ruby ruby-dev + + # TODO: Review conflict with NodeJS (currently rails test is disabled) + #wget https://deb.nodesource.com/setup_4.x | $SUDO_CMD bash - + #$SUDO_CMD apt-get -y --no-install-recommends install nodejs + #$SUDO_CMD gem install rails + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache ruby ruby-dev + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install ruby@3.2 + brew link ruby@3.2 --force --overwrite + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + RUBY_PREFIX="$(brew --prefix ruby@3.2)" + RUBY_VERSION="$(ruby -e 'puts RUBY_VERSION')" + echo "-DRuby_INCLUDE_DIR=$RUBY_PREFIX/include/ruby-3.2.0" >> $CMAKE_CONFIG_PATH + echo "-DRuby_LIBRARY=$RUBY_PREFIX/lib/libruby.3.2.dylib" >> $CMAKE_CONFIG_PATH + echo "-DRuby_EXECUTABLE=$RUBY_PREFIX/bin/ruby" >> $CMAKE_CONFIG_PATH + echo "-DRuby_VERSION=$RUBY_VERSION" >> $CMAKE_CONFIG_PATH + fi } # RapidJSON sub_rapidjson(){ echo "configure rapidjson" cd $ROOT_DIR - git clone https://github.com/miloyip/rapidjson.git - cd rapidjson - git checkout v1.1.0 - mkdir build - cd build - cmake -DRAPIDJSON_BUILD_DOC=Off -DRAPIDJSON_BUILD_EXAMPLES=Off -DRAPIDJSON_BUILD_TESTS=Off .. - make - $SUDO_CMD make install - cd ../.. && rm -rf ./rapidjson -} -# FuncHook -sub_funchook(){ - echo "configure funchook" - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install cmake + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + git clone https://github.com/Tencent/rapidjson.git + cd rapidjson + git checkout 24b5e7a8b27f42fa16b96fc70aade9106cf7102f + mkdir build + cd build + cmake -DRAPIDJSON_BUILD_DOC=Off -DRAPIDJSON_BUILD_EXAMPLES=Off -DRAPIDJSON_BUILD_TESTS=Off .. + make -j$(getconf _NPROCESSORS_ONLN) + $SUDO_CMD make install + cd ../.. && rm -rf ./rapidjson + fi } # NetCore @@ -150,28 +244,31 @@ sub_netcore(){ echo "configure netcore" cd $ROOT_DIR - # Debian Stretch - - $SUDO_CMD apt-get update && apt-get $APT_CACHE_CMD install -y --no-install-recommends \ - libc6 libcurl3 libgcc1 libgssapi-krb5-2 libicu57 liblttng-ust0 libssl1.0.2 libstdc++6 libunwind8 libuuid1 zlib1g + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Debian Stretch + $SUDO_CMD apt-get update && $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends \ + libc6 libcurl3 libgcc1 libgssapi-krb5-2 libicu57 liblttng-ust0 libssl1.0.2 libstdc++6 libunwind8 libuuid1 zlib1g - # Install .NET Sdk - DOTNET_SDK_VERSION=1.1.11 - DOTNET_SDK_DOWNLOAD_URL=https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-dev-debian.9-x64.$DOTNET_SDK_VERSION.tar.gz + # Install .NET Sdk + DOTNET_SDK_VERSION=1.1.11 + DOTNET_SDK_DOWNLOAD_URL=https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-dev-debian.9-x64.$DOTNET_SDK_VERSION.tar.gz - wget $DOTNET_SDK_DOWNLOAD_URL -O dotnet.tar.gz - mkdir -p /usr/share/dotnet - tar -zxf dotnet.tar.gz -C /usr/share/dotnet - rm dotnet.tar.gz - ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet + wget $DOTNET_SDK_DOWNLOAD_URL -O dotnet.tar.gz + mkdir -p /usr/share/dotnet + tar -zxf dotnet.tar.gz -C /usr/share/dotnet + rm dotnet.tar.gz + ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet - # Trigger the population of the local package cache - mkdir warmup - cd warmup - dotnet new - cd .. - rm -rf warmup - rm -rf /tmp/NuGetScratch + # Trigger the population of the local package cache + mkdir warmup + cd warmup + dotnet new + cd .. + rm -rf warmup + rm -rf /tmp/NuGetScratch + fi + fi } # NetCore 2 @@ -179,61 +276,155 @@ sub_netcore2(){ echo "configure netcore 2" cd $ROOT_DIR - # Set up repository - wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb - $SUDO_CMD dpkg -i packages-microsoft-prod.deb - rm packages-microsoft-prod.deb + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Set up repository + wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb - # Install .NET Core Sdk - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends apt-transport-https - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-2.2 + # Install .NET Core Sdk + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends apt-transport-https + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-2.2 + fi + fi } -# V8 Repository -sub_v8repo(){ - echo "configure v8 from repository" +# NetCore 5 +sub_netcore5(){ + echo "configure netcore 5" cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install software-properties-common - # V8 5.1 - if [ $INSTALL_V8REPO51 = 1 ]; then - $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv851.list" - $SUDO_CMD sh -c "echo \"deb http://archive.ubuntu.com/ubuntu trusty main\" > /etc/apt/sources.list.d/libicu52.list" - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --allow-unauthenticated install libicu52 libv8-5.1.117 libv8-5.1-dev - fi + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Set up repository + wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb - # V8 5.4 - if [ $INSTALL_V8REPO54 = 1 ]; then - $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-5.4/ubuntu xenial main\" > /etc/apt/sources.list.d/libv854.list" - wget http://launchpadlibrarian.net/234847357/libicu55_55.1-7_amd64.deb - $SUDO_CMD dpkg -i libicu55_55.1-7_amd64.deb - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --allow-unauthenticated install libicu55 libv8-5.4-dev - $SUDO_CMD rm libicu55_55.1-7_amd64.deb + # Install .NET Core Sdk + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends apt-transport-https + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-5.0 + fi fi +} + +# NetCore 7 +sub_netcore7(){ + echo "configure netcore 7" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + # Set up repository + wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + # Install .NET Core Sdk + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends apt-transport-https + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-7.0 + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ]; then + CODENAME="unstable" + else + CODENAME="${VERSION_CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + fi + ;; + esac - # V8 5.2 - if [ $INSTALL_V8REPO52 = 1 ]; then - $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.2 - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install libicu55 libv8-5.2-dev + if [ "${CODENAME}" = "noble" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends software-properties-common + $SUDO_CMD add-apt-repository ppa:dotnet/backports + fi + + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-7.0 + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache dotnet7-sdk + fi fi +} - # V8 5.7 - if [ $INSTALL_V8REPO57 = 1 ]; then - $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.7 - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install libicu55 libv8-5.7-dev +# NetCore 8 +sub_netcore8(){ + echo "configure netcore 8" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + wget -O - https://dot.net/v1/dotnet-install.sh | $SUDO_CMD bash -s -- --version 8.0.408 --install-dir /usr/local/bin + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache dotnet8-sdk + fi fi +} + +# V8 Repository +sub_v8repo(){ + echo "configure v8 from repository" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends software-properties-common + + # V8 5.1 + if [ $INSTALL_V8REPO51 = 1 ]; then + $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv851.list" + $SUDO_CMD sh -c "echo \"deb http://archive.ubuntu.com/ubuntu trusty main\" > /etc/apt/sources.list.d/libicu52.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends --allow-unauthenticated libicu52 libv8-5.1.117 libv8-5.1-dev + fi + + # V8 5.4 + if [ $INSTALL_V8REPO54 = 1 ]; then + $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-5.4/ubuntu xenial main\" > /etc/apt/sources.list.d/libv854.list" + wget http://launchpadlibrarian.net/234847357/libicu55_55.1-7_amd64.deb + $SUDO_CMD dpkg -i libicu55_55.1-7_amd64.deb + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends --allow-unauthenticated libicu55 libv8-5.4-dev + $SUDO_CMD rm libicu55_55.1-7_amd64.deb + fi - # V8 5.8 - if [ $INSTALL_V8REPO58 = 1 ]; then - $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv8-archived.list" - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install libicu57 libv8-5.8.283 libv8-5.8-dev + # V8 5.2 + if [ $INSTALL_V8REPO52 = 1 ]; then + $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.2 + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends libicu55 libv8-5.2-dev + fi + + # V8 5.7 + if [ $INSTALL_V8REPO57 = 1 ]; then + $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.7 + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends libicu55 libv8-5.7-dev + fi + + # V8 5.8 + if [ $INSTALL_V8REPO58 = 1 ]; then + $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv8-archived.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends libicu57 libv8-5.8.283 libv8-5.8-dev + fi fi } @@ -241,40 +432,142 @@ sub_v8repo(){ sub_v8(){ echo "configure v8" cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install python - git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git - export PATH=`pwd`/depot_tools:"$PATH" - export GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends python + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git + export PATH=`pwd`/depot_tools:"$PATH" - fetch v8 - cd v8 - git checkout 5.1-lkgr - gclient sync + export GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" - patch build/all.gyp $ROOT_DIR/nobuildtest.patch - GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" make library=shared native + fetch v8 + cd v8 + git checkout 5.1-lkgr + gclient sync + + patch build/all.gyp $ROOT_DIR/nobuildtest.patch + GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" make -j$(getconf _NPROCESSORS_ONLN) library=shared native + fi } # NodeJS sub_nodejs(){ - # TODO: Review conflicts with Ruby Rails and NodeJS 4.x echo "configure nodejs" cd $ROOT_DIR - $SUDO_CMD apt-get update - # Install python 2.7 to build node (gyp) - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install python g++ make nodejs curl + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ $INSTALL_C = 1 ]; then + # Required for test source/tests/metacall_node_port_c_lib_test + INSTALL_LIBGIT2="libgit2-dev" + else + INSTALL_LIBGIT2="" + fi + + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Note that Python is required for GYP + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends python3 g++ make nodejs npm curl $INSTALL_LIBGIT2 + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache python3 g++ make nodejs nodejs-dev npm curl $INSTALL_LIBGIT2 + + # Build dependencies (note libexecinfo-dev is not available in Alpine 3.17) + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main linux-headers libexecinfo libexecinfo-dev + + # Build NodeJS shared library + $SUDO_CMD apk add --no-cache --virtual .build-nodejs-deps \ + git \ + alpine-sdk \ + alpine-conf \ + ccache \ + brotli-dev \ + c-ares-dev \ + icu-dev \ + linux-headers \ + nghttp2-dev \ + openssl-dev \ + samurai \ + zlib-dev + + # Fix to a lower Python version (3.9) in order avoid conflicts with Python dependency of Clang from C Loader + $SUDO_CMD apk add --no-cache --virtual .build-nodejs-python-deps --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main \ + python3=3.9.16-r0 \ + py3-jinja2=3.0.1-r0 + + git clone --depth 1 --branch v3.17.3 https://git.alpinelinux.org/aports + cd aports/main/nodejs + sed -i 's/--shared-brotli\ \\/--shared \\\n --shared-brotli\ \\/g' APKBUILD + if [ "$SUDO_CMD" = "" ]; then + ABUILD_ROOT=-F + else + ABUILD_ROOT= + fi + echo "abuild-key" | abuild-keygen -a + $SUDO_CMD cp abuild-key.pub /etc/apk/keys + $SUDO_CMD abuild $ABUILD_ROOT checksum + $SUDO_CMD abuild $ABUILD_ROOT || true + cp pkg/nodejs/usr/bin/node /usr/bin/node + cp src/node-v18.14.2/out/Release/lib/libnode.so.108 /usr/lib/. + cd ../../.. + rm -rf aports + $SUDO_CMD apk del .build-nodejs-deps + $SUDO_CMD apk del .build-nodejs-python-deps + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + # Install NodeJS (required for source build or NPM itself) + brew install node@22 + # Make node 22 the default + brew link node@22 --force --overwrite + # Execute post install scripts + brew postinstall node@22 + # Define node location + NODE_PREFIX=$(brew --prefix node@22) + + # Configure NodeJS paths + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + + # Configure NPM path + echo "-DNPM_ROOT=$NODE_PREFIX/bin" >> $CMAKE_CONFIG_PATH - # Install and update npm and node-gyp - curl -L https://npmjs.org/install.sh | $SUDO_CMD sh - npm i npm@latest -g - npm i node-gyp@latest -g + # Make npm available for subsequent calls + export PATH="$NODE_PREFIX/bin:$PATH" + + # Build either using pre-compiled binaries or building node from source + if [ -z "${NodeJS_BUILD_FROM_SOURCE:-}" ]; then + # Define node location + NODE_PREFIX="$ROOT_DIR/build" + # Install NodeJS + wget -qO- https://github.com/metacall/libnode/releases/download/v22.9.0/libnode-${ARCHITECTURE}-macos.tar.xz | tar xvJ -C $NODE_PREFIX + # Configure NodeJS path + echo "-DNodeJS_EXECUTABLE=$NODE_PREFIX/node" >> $CMAKE_CONFIG_PATH + echo "-DNodeJS_LIBRARY=$NODE_PREFIX/libnode.dylib" >> $CMAKE_CONFIG_PATH + else + # Include binaries into PATH + export PATH="$NODE_PREFIX/bin:$PATH" + # Define executable path + echo "-DNodeJS_EXECUTABLE=$NODE_PREFIX/bin/node" >> $CMAKE_CONFIG_PATH + fi + + if [ $INSTALL_C = 1 ]; then + # Required for test source/tests/metacall_node_port_c_lib_test + brew install libgit2@1.8 + brew link libgit2@1.8 --force --overwrite + fi + fi } # TypeScript sub_typescript(){ - echo "configure typesecript" + echo "configure typescript" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + # Install React dependencies in order to run the tests + $SUDO_CMD npm i react@latest -g + $SUDO_CMD npm i react-dom@latest -g + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + # Install React dependencies in order to run the tests + npm i react@latest -g + npm i react-dom@latest -g + fi } # File @@ -282,65 +575,326 @@ sub_file(){ echo "configure file" } +# RPC +sub_rpc(){ + echo "cofingure rpc" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Install development files and documentation for libcurl (OpenSSL flavour) + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends libcurl4-openssl-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache curl-dev + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install curl + fi +} + # WebAssembly sub_wasm(){ echo "configure webassembly" - # TODO + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing wasmtime libwasmtime + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install wasmtime + fi +} + +# Java +sub_java(){ + echo "configure java" - # $SUDO_CMD apt-get update - # $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --fix-broken install lib32gcc-6-dev g++-multilib + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends default-jdk default-jre + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache openjdk8 openjdk8-jre + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install openjdk@17 + sudo ln -sfn $(brew --prefix openjdk@17)/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk + JAVA_PREFIX=$(/usr/libexec/java_home -v 17) + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + echo "-DJAVA_HOME=$JAVA_PREFIX" >> $CMAKE_CONFIG_PATH + echo "-DJAVA_INCLUDE_PATH=$JAVA_PREFIX/include" >> $CMAKE_CONFIG_PATH + echo "-DJAVA_INCLUDE_PATH2=$JAVA_PREFIX/include/darwin" >> $CMAKE_CONFIG_PATH + echo "-DJAVA_AWT_INCLUDE_PATH=$JAVA_PREFIX/include" >> $CMAKE_CONFIG_PATH + fi } -# MetaCall -sub_metacall(){ - # TODO: Update this or deprecate it - echo "configure metacall" +# C +sub_c(){ + echo "configure c" cd $ROOT_DIR - git clone --recursive https://github.com/metacall/core.git - mkdir core/build && cd core/build + LLVM_VERSION_STRING=14 - if [ $INSTALL_NETCORE = 1 ]; then - NETCORE_VERSION=1.1.10 - elif [ INSTALL_NETCORE2 = 1 ]; then - NETCORE_VERSION=2.2.8 - else - NETCORE_VERSION=0 + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + # For now bookworm || trixie == sid, change when trixie is released + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ] || [ "${VERSION_CODENAME}" = "bookworm" ] || [ "${VERSION_CODENAME}" = "trixie" ]; then + CODENAME="unstable" + LINKNAME="" + else + # "stable" Debian release + CODENAME="${VERSION_CODENAME}" + LINKNAME="-${CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + if [ -n "${CODENAME}" ]; then + LINKNAME="-${CODENAME}" + fi + fi + ;; + esac + + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | $SUDO_CMD tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + $SUDO_CMD sh -c "echo \"deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD sh -c "echo \"deb-src http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get install -y --no-install-recommends libffi-dev libclang-${LLVM_VERSION_STRING}-dev + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libffi-dev libclang-${LLVM_VERSION_STRING}-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libffi-dev + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main clang-libs=13.0.1-r1 clang-dev=13.0.1-r1 + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install libffi + brew install llvm@$LLVM_VERSION_STRING + brew link llvm@$LLVM_VERSION_STRING --force --overwrite + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + LIBCLANG_PREFIX=$(brew --prefix llvm@$LLVM_VERSION_STRING) + echo "-DLibClang_INCLUDE_DIR=${LIBCLANG_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DLibClang_LIBRARY=${LIBCLANG_PREFIX}/lib/libclang.dylib" >> $CMAKE_CONFIG_PATH + fi +} + +# Cobol +sub_cobol(){ + echo "configure cobol" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + echo "deb http://deb.debian.org/debian/ unstable main" | $SUDO_CMD tee -a /etc/apt/sources.list > /dev/null + + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD -t unstable install -y --no-install-recommends gnucobol + + # Remove unstable from sources.list + $SUDO_CMD head -n -2 /etc/apt/sources.list + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends gnucobol4 + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache --virtual .build-cobol-deps build-base tar libaio libnsl libc6-compat binutils abuild make gcc gmp-dev db-dev libxml2-dev ncurses-dev + + # Install gnucobol + wget https://sourceforge.net/projects/gnucobol/files/gnucobol/3.1/gnucobol-3.1-rc1.tar.gz/download -O gnucobol-3.1-rc1.tar.gz + tar xvfz gnucobol-3.1-rc1.tar.gz + cd gnucobol-3.1-rc1 + ./configure + make -j$(getconf _NPROCESSORS_ONLN) + make install + cd .. + rm -rf gnucobol-3.1-rc1 gnucobol-3.1-rc1.tar.gz + + # Clean build deps + $SUDO_CMD apk del .build-cobol-deps + + # Runtime deps + $SUDO_CMD apk add --no-cache db ncurses + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install gnucobol + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + COBOL_PREFIX=$(brew --prefix gnucobol) + echo "-DCOBOL_EXECUTABLE=${COBOL_PREFIX}/bin/cobc" >> $CMAKE_CONFIG_PATH + echo "-DCOBOL_INCLUDE_DIR=${COBOL_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DCOBOL_LIBRARY=${COBOL_PREFIX}/lib/libcob.dylib" >> $CMAKE_CONFIG_PATH fi +} - cmake -Wno-dev ../ -DPYTHON_EXECUTABLE=/usr/bin/python3.7 -DOPTION_BUILD_EXAMPLES=off -DOPTION_BUILD_LOADERS_PY=on -DOPTION_BUILD_LOADERS_RB=on -DOPTION_BUILD_LOADERS_CS=on -DOPTION_BUILD_LOADERS_JS=on -DCMAKE_BUILD_TYPE=Release -DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/$NETCORE_VERSION/ - make - make test && echo "test ok!" +# Go +sub_go(){ + echo "configure go" + cd $ROOT_DIR - echo "configure with cmake .. " + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends golang + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache go + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install go + fi +} + +# Rust +sub_rust(){ + echo "configure rust" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends curl autoconf automake + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache curl musl-dev linux-headers libgcc + fi + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly-2021-12-04 --profile default + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly-2021-12-04 --profile default + brew install patchelf + fi } # Pack sub_pack(){ echo "configure pack" cd $ROOT_DIR - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends rpm + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends rpm + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache rpm + fi + fi } # Coverage sub_coverage(){ echo "configure coverage" cd $ROOT_DIR - $SUDO_CMD apt-get update - $SUDO_CMD apt-get install -y --no-install-recommends lcov + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends lcov python3 python3-pip + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache lcov python3 py3-pip + fi + + PIP_BREAK_SYSTEM_PACKAGES=`python3 -c 'import sys; sys.version_info.major >= 3 and sys.version_info.minor >= 11 and print("--break-system-packages", end="")'` + + pip3 install ${PIP_BREAK_SYSTEM_PACKAGES} gcovr==7.2 + fi +} + +# Clang format +sub_clangformat(){ + echo "configure clangformat" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + LLVM_VERSION_STRING=12 + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + # For now bookworm || trixie == sid, change when trixie is released + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ] || [ "${VERSION_CODENAME}" = "bookworm" ] || [ "${VERSION_CODENAME}" = "trixie" ]; then + CODENAME="unstable" + LINKNAME="" + else + # "stable" Debian release + CODENAME="${VERSION_CODENAME}" + LINKNAME="-${CODENAME}" + fi + ;; + *) + # ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + if [ -n "${CODENAME}" ]; then + LINKNAME="-${CODENAME}" + fi + fi + ;; + esac + + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | $SUDO_CMD tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + $SUDO_CMD sh -c "echo \"deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD sh -c "echo \"deb-src http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get install -y --no-install-recommends clang-format-${LLVM_VERSION_STRING} + $SUDO_CMD ln -s /usr/bin/clang-format-${LLVM_VERSION_STRING} /usr/bin/clang-format + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main clang-extra-tools=12.0.1-r1 + fi + fi +} + +# Backtrace (this only improves stack traces verbosity but backtracing is enabled by default) +sub_backtrace(){ + echo "configure backtrace" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libdw-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache binutils-dev + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install dwarfutils + brew install libelf + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + LIBDWARD_PREFIX=$(brew --prefix dwarfutils) + LIBELF_PREFIX=$(brew --prefix libelf) + echo "-DLIBDWARF_INCLUDE_DIR=${LIBDWARD_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DLIBELF_LIBRARY=${LIBELF_PREFIX}/lib/libelf.a" >> $CMAKE_CONFIG_PATH + echo "-DLIBELF_INCLUDE_DIR=${LIBELF_PREFIX}/include" >> $CMAKE_CONFIG_PATH + fi +} + +# Sandbox (this provides sandboxing features in Linux through BFS filters with libseccomp) +sub_sandbox(){ + echo "configure sandbox" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libseccomp-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libseccomp-dev + fi + fi } # Install sub_install(){ - if [ $RUN_AS_ROOT = 1 ]; then - SUDO_CMD="" - fi if [ $APT_CACHE = 1 ]; then - APT_CACHE_CMD=-o dir::cache::archives="$APT_CACHE_DIR" + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + APT_CACHE_CMD=-o dir::cache::archives="$APT_CACHE_DIR" + fi fi - if [ $INSTALL_APT = 1 ]; then - sub_apt + if [ $INSTALL_BASE = 1 ]; then + sub_base fi if [ $INSTALL_PYTHON = 1 ]; then sub_python @@ -351,15 +905,21 @@ sub_install(){ if [ $INSTALL_RAPIDJSON = 1 ]; then sub_rapidjson fi - if [ $INSTALL_FUNCHOOK = 1 ]; then - sub_funchook - fi if [ $INSTALL_NETCORE = 1 ]; then sub_netcore fi if [ $INSTALL_NETCORE2 = 1 ]; then sub_netcore2 fi + if [ $INSTALL_NETCORE5 = 1 ]; then + sub_netcore5 + fi + if [ $INSTALL_NETCORE7 = 1 ]; then + sub_netcore7 + fi + if [ $INSTALL_NETCORE8 = 1 ]; then + sub_netcore8 + fi if [ $INSTALL_V8 = 1 ]; then sub_v8 fi @@ -375,14 +935,26 @@ sub_install(){ if [ $INSTALL_FILE = 1 ]; then sub_file fi + if [ $INSTALL_RPC = 1 ]; then + sub_rpc + fi if [ $INSTALL_WASM = 1 ]; then sub_wasm fi - if [ $INSTALL_SWIG = 1 ]; then - sub_swig + if [ $INSTALL_JAVA = 1 ]; then + sub_java + fi + if [ $INSTALL_C = 1 ]; then + sub_c + fi + if [ $INSTALL_COBOL = 1 ]; then + sub_cobol + fi + if [ $INSTALL_GO = 1 ]; then + sub_go fi - if [ $INSTALL_METACALL = 1 ]; then - sub_metacall + if [ $INSTALL_RUST = 1 ]; then + sub_rust fi if [ $INSTALL_PACK = 1 ]; then sub_pack @@ -390,107 +962,157 @@ sub_install(){ if [ $INSTALL_COVERAGE = 1 ]; then sub_coverage fi - + if [ $INSTALL_CLANGFORMAT = 1 ]; then + sub_clangformat + fi + if [ $INSTALL_BACKTRACE = 1 ]; then + sub_backtrace + fi + if [ $INSTALL_SANDBOX = 1 ]; then + sub_sandbox + fi echo "install finished in workspace $ROOT_DIR" } # Configuration sub_options(){ - for var in "$@" + for option in "$@" do - if [ "$var" = 'root' ]; then - echo "running as root" - RUN_AS_ROOT=1 + if [ "$option" = 'cache' ]; then + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + echo "apt caching selected" + APT_CACHE=1 + fi + fi + if [ "$option" = 'debug' ]; then + echo "Install dependencies in debug mode" + BUILD_TYPE=Debug fi - if [ "$var" = 'cache' ]; then - echo "apt caching selected" - APT_CACHE=1 + if [ "$option" = 'release' ] || [ "$option" = 'relwithdebinfo' ]; then + echo "Install dependencies release mode" + BUILD_TYPE=Release fi - if [ "$var" = 'base' ]; then + if [ "$option" = 'base' ]; then echo "apt selected" - INSTALL_APT=1 + INSTALL_BASE=1 fi - if [ "$var" = 'python' ]; then + if [ "$option" = 'python' ]; then echo "python selected" INSTALL_PYTHON=1 fi - if [ "$var" = 'ruby' ]; then + if [ "$option" = 'ruby' ]; then echo "ruby selected" INSTALL_RUBY=1 fi - if [ "$var" = 'netcore' ]; then + if [ "$option" = 'netcore' ]; then echo "netcore selected" INSTALL_NETCORE=1 fi - if [ "$var" = 'netcore2' ]; then + if [ "$option" = 'netcore2' ]; then echo "netcore 2 selected" INSTALL_NETCORE2=1 fi - if [ "$var" = 'rapidjson' ]; then + if [ "$option" = 'netcore5' ]; then + echo "netcore 5 selected" + INSTALL_NETCORE5=1 + fi + if [ "$option" = 'netcore7' ]; then + echo "netcore 7 selected" + INSTALL_NETCORE7=1 + fi + if [ "$option" = 'netcore8' ]; then + echo "netcore 8 selected" + INSTALL_NETCORE8=1 + fi + if [ "$option" = 'rapidjson' ]; then echo "rapidjson selected" INSTALL_RAPIDJSON=1 fi - if [ "$var" = 'funchook' ]; then - echo "funchook selected" - INSTALL_FUNCHOOK=1 - fi - if [ "$var" = 'v8' ] || [ "$var" = 'v8rep54' ]; then + if [ "$option" = 'v8' ] || [ "$option" = 'v8rep54' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO54=1 fi - if [ "$var" = 'v8rep57' ]; then + if [ "$option" = 'v8rep57' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO57=1 fi - if [ "$var" = 'v8rep58' ]; then + if [ "$option" = 'v8rep58' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO58=1 fi - if [ "$var" = 'v8rep52' ]; then + if [ "$option" = 'v8rep52' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO52=1 fi - if [ "$var" = 'v8rep51' ]; then + if [ "$option" = 'v8rep51' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO51=1 fi - if [ "$var" = 'nodejs' ]; then + if [ "$option" = 'nodejs' ]; then echo "nodejs selected" INSTALL_NODEJS=1 fi - if [ "$var" = 'typescript' ]; then + if [ "$option" = 'typescript' ]; then echo "typescript selected" INSTALL_TYPESCRIPT=1 fi - if [ "$var" = 'file' ]; then + if [ "$option" = 'file' ]; then echo "file selected" INSTALL_FILE=1 fi - if [ "$var" = 'wasm' ]; then + if [ "$option" = 'rpc' ]; then + echo "rpc selected" + INSTALL_RPC=1 + fi + if [ "$option" = 'wasm' ]; then echo "wasm selected" INSTALL_WASM=1 fi - if [ "$var" = 'swig' ]; then - echo "swig selected" - INSTALL_SWIG=1 + if [ "$option" = 'java' ]; then + echo "java selected" + INSTALL_JAVA=1 + fi + if [ "$option" = 'c' ]; then + echo "c selected" + INSTALL_C=1 fi - if [ "$var" = 'metacall' ]; then - echo "metacall selected" - INSTALL_METACALL=1 + if [ "$option" = 'cobol' ]; then + echo "cobol selected" + INSTALL_COBOL=1 fi - if [ "$var" = 'pack' ]; then + if [ "$option" = 'go' ]; then + echo "go selected" + INSTALL_GO=1 + fi + if [ "$option" = 'rust' ]; then + echo "rust selected" + INSTALL_RUST=1 + fi + if [ "$option" = 'pack' ]; then echo "pack selected" INSTALL_PACK=1 fi - if [ "$var" = 'coverage' ]; then + if [ "$option" = 'coverage' ]; then echo "coverage selected" INSTALL_COVERAGE=1 fi + if [ "$option" = 'clangformat' ]; then + echo "clangformat selected" + INSTALL_CLANGFORMAT=1 + fi + if [ "$option" = 'backtrace' ]; then + echo "backtrace selected" + INSTALL_BACKTRACE=1 + fi + if [ "$option" = 'sandbox' ]; then + echo "sandbox selected" + INSTALL_SANDBOX=1 + fi done } @@ -498,15 +1120,17 @@ sub_options(){ sub_help() { echo "Usage: `basename "$0"` list of component" echo "Components:" - echo " root" + echo " debug | release | relwithdebinfo" echo " cache" echo " base" echo " python" echo " ruby" echo " netcore" echo " netcore2" + echo " netcore5" + echo " netcore7" + echo " netcore8" echo " rapidjson" - echo " funchook" echo " v8" echo " v8rep51" echo " v8rep54" @@ -515,11 +1139,17 @@ sub_help() { echo " nodejs" echo " typescript" echo " file" + echo " rpc" echo " wasm" - echo " swig" - echo " metacall" + echo " java" + echo " c" + echo " cobol" + echo " go" echo " pack" echo " coverage" + echo " clangformat" + echo " backtrace" + echo " sandbox" echo "" } diff --git a/tools/metacall-license.sh b/tools/metacall-license.sh index 7f9c6a2434..2be232899b 100755 --- a/tools/metacall-license.sh +++ b/tools/metacall-license.sh @@ -4,7 +4,7 @@ # MetaCall License Bash Script by Parra Studios # License bash script utility for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ # limitations under the License. # +set -euxo pipefail + # Warning echo "WARNING: Do not run the script multiple times. Uncomment the 'exit 0' in the code to continue." exit 0 @@ -33,7 +35,7 @@ find "$EXEC_PATH" -type f \ -exec sh -c ' \ # Copyright - COPYRIGHT="Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia $" + COPYRIGHT="Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia $" # License LICENSE=$(cat <<-END diff --git a/tools/metacall-runtime.sh b/tools/metacall-runtime.sh index 58cb4eff8c..9ef4afba2d 100755 --- a/tools/metacall-runtime.sh +++ b/tools/metacall-runtime.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Configuration Environment Bash Script by Parra Studios +# MetaCall Configuration Environment Shell Script by Parra Studios # Configure and install MetaCall environment script utility. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,24 +19,61 @@ # limitations under the License. # +set -euxo + ROOT_DIR=$(pwd) -RUN_AS_ROOT=0 -SUDO_CMD=sudo +BUILD_TYPE=Release INSTALL_APT=1 INSTALL_PYTHON=0 INSTALL_RUBY=0 INSTALL_NETCORE=0 INSTALL_NETCORE2=0 +INSTALL_NETCORE5=0 INSTALL_V8=0 INSTALL_NODEJS=0 INSTALL_TYPESCRIPT=0 INSTALL_FILE=0 +INSTALL_RPC=0 +INSTALL_WASM=0 +INSTALL_JAVA=0 +INSTALL_C=0 +INSTALL_COBOL=0 +INSTALL_BACKTRACE=0 +INSTALL_SANDBOX=0 INSTALL_PORTS=0 INSTALL_CLEAN=0 SHOW_HELP=0 PROGNAME=$(basename $0) +# Operative System detection +case "$(uname -s)" in + Linux*) OPERATIVE_SYSTEM=Linux;; + Darwin*) OPERATIVE_SYSTEM=Darwin;; + CYGWIN*) OPERATIVE_SYSTEM=Cygwin;; + MINGW*) OPERATIVE_SYSTEM=MinGW;; + *) OPERATIVE_SYSTEM="Unknown" +esac + +# Check out for sudo +if [ "`id -u`" = '0' ]; then + SUDO_CMD="" +else + SUDO_CMD=sudo +fi + +# Linux Distro detection +if [ -f /etc/os-release ]; then # Either Debian or Ubuntu + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_DISTRO=$(cat /etc/os-release | grep "^ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_VERSION_ID=$(cat /etc/os-release | grep "^VERSION_ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') +else + # TODO: Implement more distros or better detection + LINUX_DISTRO=unknown + LINUX_VERSION_ID=unknown +fi + # Install and mark packages to avoid autoremove sub_apt_install_hold(){ $SUDO_CMD apt-get -y install --no-install-recommends $@ @@ -54,7 +91,12 @@ sub_apt(){ sub_python(){ echo "configure python" cd $ROOT_DIR - sub_apt_install_hold python3 libpython3.7 + + if [ "${BUILD_TYPE}" = "Debug" ]; then + sub_apt_install_hold libpython3-dbg + else + sub_apt_install_hold libpython3-dev + fi } # Ruby @@ -62,22 +104,8 @@ sub_ruby(){ echo "configure ruby" cd $ROOT_DIR - # TODO: Remove this when using ruby2.5 (not available yet because it fails on loading a script with a malloc error) - $SUDO_CMD mv /etc/apt/sources.list /etc/apt/sources.list.backup - $SUDO_CMD sh -c "echo \"deb http://ftp.debian.org/debian/ stretch main\" > /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb-src http://ftp.debian.org/debian/ stretch main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb http://security.debian.org/debian-security stretch/updates main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb-src http://security.debian.org/debian-security stretch/updates main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb http://ftp.debian.org/debian/ stretch-updates main\" >> /etc/apt/sources.list" - $SUDO_CMD sh -c "echo \"deb-src http://ftp.debian.org/debian/ stretch-updates main\" >> /etc/apt/sources.list" - $SUDO_CMD apt-get update - # sub_apt_install_hold ruby2.5 libruby2.5 - $SUDO_CMD apt-get -y install --no-install-recommends --allow-remove-essential --allow-downgrades libssl1.1 libffi6 zlib1g libyaml-0-2 libgmp10=2:6.1.2+dfsg-1 libreadline7 libxml2 libncurses5 libtinfo5 ruby2.3 libruby2.3 - $SUDO_CMD apt-mark hold libssl1.1 libffi6 zlib1g libyaml-0-2 libgmp10 libreadline7 libxml2 libncurses5 libtinfo5 ruby2.3 libruby2.3 - - # TODO: Remove this when using ruby2.5 (not available yet because it fails on loading a script with a malloc error) - $SUDO_CMD mv /etc/apt/sources.list.backup /etc/apt/sources.list + sub_apt_install_hold ruby libruby } # NetCore @@ -86,7 +114,6 @@ sub_netcore(){ cd $ROOT_DIR # Debian Stretch - sub_apt_install_hold libc6 libcurl3 libgcc1 libgssapi-krb5-2 libicu57 \ liblttng-ust0 libssl1.0.2 libstdc++6 libunwind8 libuuid1 zlib1g ca-certificates @@ -115,6 +142,43 @@ sub_netcore2(){ sub_apt_install_hold dotnet-runtime-2.2=2.2.8-1 } +# NetCore 5 +sub_netcore5(){ + echo "configure netcore 5" + cd $ROOT_DIR + + # Install NET Core Runtime 5.x + wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + $SUDO_CMD apt-get update + sub_apt_install_hold dotnet-runtime-5.0=5.0.17-1 +} + +# NetCore 7 +sub_netcore7(){ + echo "configure netcore 7" + cd $ROOT_DIR + + # Install NET Core Runtime 7.x + wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + $SUDO_CMD apt-get update + sub_apt_install_hold dotnet-runtime-7.0=7.0.5-1 +} + +# NetCore 8 +sub_netcore8(){ + echo "configure netcore 8" + cd $ROOT_DIR + + # Install NET Core Runtime 8.x + wget -O - https://dot.net/v1/dotnet-install.sh | $SUDO_CMD bash -s -- --version 8.0.408 --install-dir /usr/local/bin --runtime dotnet +} + # V8 sub_v8(){ echo "configure v8" @@ -126,7 +190,7 @@ sub_nodejs(){ echo "configure node" # Install NodeJS library - sub_apt_install_hold libnode64 + sub_apt_install_hold libnode-dev } # TypeScript @@ -143,6 +207,191 @@ sub_file(){ # Nothing needed } +# RPC +sub_rpc(){ + echo "configure rpc" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ]; then + CODENAME="unstable" + else + CODENAME="${VERSION_CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + fi + ;; + esac + + if [ "${CODENAME}" = "trixie" ] || [ "${CODENAME}" = "noble" ] || [ "${CODENAME}" = "unstable" ]; then + sub_apt_install_hold libcurl4t64 + else + sub_apt_install_hold libcurl4 + fi + fi + fi +} + +# WebAssembly +sub_wasm(){ + echo "configure wasm" + + # TODO +} + +# Java +sub_java(){ + echo "configure java" + + sub_apt_install_hold default-jre +} + +# C +sub_c(){ + echo "configure c" + cd $ROOT_DIR + LLVM_VERSION_STRING=14 + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + # For now bookworm || trixie == sid, change when trixie is released + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ] || [ "${VERSION_CODENAME}" = "bookworm" ] || [ "${VERSION_CODENAME}" = "trixie" ]; then + CODENAME="unstable" + LINKNAME="" + else + # "stable" Debian release + CODENAME="${VERSION_CODENAME}" + LINKNAME="-${CODENAME}" + fi + ;; + *) + # ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + if [ -n "${CODENAME}" ]; then + LINKNAME="-${CODENAME}" + fi + fi + ;; + esac + + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | $SUDO_CMD apt-key add + $SUDO_CMD sh -c "echo \"deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD sh -c "echo \"deb-src http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD apt-get update + sub_apt_install_hold libffi libclang-${LLVM_VERSION_STRING} + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + sub_apt_install_hold libffi libclang-${LLVM_VERSION_STRING} + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libffi-dev + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main clang-libs=13.0.1-r1 clang-dev=13.0.1-r1 + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install libffi + brew install llvm@$LLVM_VERSION_STRING + brew link llvm@$LLVM_VERSION_STRING --force --overwrite + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + LIBCLANG_PREFIX=$(brew --prefix llvm@$LLVM_VERSION_STRING) + echo "-DLibClang_INCLUDE_DIR=${LIBCLANG_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DLibClang_LIBRARY=${LIBCLANG_PREFIX}/lib/libclang.dylib" >> $CMAKE_CONFIG_PATH + fi +} + +# Cobol +sub_cobol(){ + echo "configure cobol" + + if [ "${LINUX_DISTRO}" == "debian" ]; then + echo "deb http://deb.debian.org/debian/ unstable main" | $SUDO_CMD tee -a /etc/apt/sources.list > /dev/null + + $SUDO_CMD apt-get update + sub_apt_install_hold libcob4 + + # Remove unstable from sources.list + $SUDO_CMD head -n -2 /etc/apt/sources.list + $SUDO_CMD apt-get update + elif [ "${LINUX_DISTRO}" == "ubuntu" ]; then + sub_apt_install_hold libcob4 + fi +} + +# Backtrace (this only improves stack traces verbosity but backtracing is enabled by default) +sub_backtrace(){ + echo "configure backtrace" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ]; then + CODENAME="unstable" + else + CODENAME="${VERSION_CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + fi + ;; + esac + + if [ "${CODENAME}" = "trixie" ] || [ "${CODENAME}" = "noble" ] || [ "${CODENAME}" = "unstable" ]; then + sub_apt_install_hold libdw1t64 libelf1t64 + else + sub_apt_install_hold libdw1 + fi + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache binutils + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install dwarfutils + brew install libelf + fi +} + +# Sandbox (this provides sandboxing features in Linux through BFS filters with libseccomp) +sub_sandbox(){ + echo "configure sandbox" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libseccomp + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libseccomp + fi + fi +} + # Ports sub_ports(){ echo "configure ports" @@ -152,9 +401,6 @@ sub_ports(){ # Install sub_install(){ - if [ $RUN_AS_ROOT = 1 ]; then - SUDO_CMD="" - fi if [ $INSTALL_APT = 1 ]; then sub_apt fi @@ -170,6 +416,9 @@ sub_install(){ if [ $INSTALL_NETCORE2 = 1 ]; then sub_netcore2 fi + if [ $INSTALL_NETCORE5 = 1 ]; then + sub_netcore5 + fi if [ $INSTALL_V8 = 1 ]; then sub_v8 fi @@ -182,6 +431,27 @@ sub_install(){ if [ $INSTALL_FILE = 1 ]; then sub_file fi + if [ $INSTALL_RPC = 1 ]; then + sub_rpc + fi + if [ $INSTALL_WASM = 1 ]; then + sub_wasm + fi + if [ $INSTALL_JAVA = 1 ]; then + sub_java + fi + if [ $INSTALL_C = 1 ]; then + sub_c + fi + if [ $INSTALL_COBOL = 1 ]; then + sub_cobol + fi + if [ $INSTALL_BACKTRACE = 1 ]; then + sub_backtrace + fi + if [ $INSTALL_SANDBOX = 1 ]; then + sub_sandbox + fi if [ $INSTALL_PORTS = 1 ]; then sub_ports fi @@ -202,53 +472,89 @@ sub_clean(){ # Configuration sub_options(){ - for var in "$@" + for option in "$@" do - if [ "$var" = 'root' ]; then - echo "running as root" - RUN_AS_ROOT=1 + if [ "$option" = 'debug' ]; then + echo "debug mode selected" + BUILD_TYPE=Debug + fi + if [ "$option" = 'release' ] || [ "$option" = 'relwithdebinfo' ]; then + echo "release mode selected" + BUILD_TYPE=Release fi - if [ "$var" = 'base' ]; then + if [ "$option" = 'base' ]; then echo "apt selected" INSTALL_APT=1 fi - if [ "$var" = 'python' ]; then + if [ "$option" = 'python' ]; then echo "python selected" INSTALL_PYTHON=1 fi - if [ "$var" = 'ruby' ]; then + if [ "$option" = 'ruby' ]; then echo "ruby selected" INSTALL_RUBY=1 fi - if [ "$var" = 'netcore' ]; then + if [ "$option" = 'netcore' ]; then echo "netcore selected" INSTALL_NETCORE=1 fi - if [ "$var" = 'netcore2' ]; then + if [ "$option" = 'netcore2' ]; then echo "netcore 2 selected" INSTALL_NETCORE2=1 fi - if [ "$var" = 'v8' ]; then + if [ "$option" = 'netcore5' ]; then + echo "netcore 5 selected" + INSTALL_NETCORE5=1 + fi + if [ "$option" = 'v8' ]; then echo "v8 selected" INSTALL_V8=1 fi - if [ "$var" = 'nodejs' ]; then + if [ "$option" = 'nodejs' ]; then echo "nodejs selected" INSTALL_NODEJS=1 fi - if [ "$var" = 'typescript' ]; then + if [ "$option" = 'typescript' ]; then echo "typescript selected" INSTALL_TYPESCRIPT=1 fi - if [ "$var" = 'file' ]; then + if [ "$option" = 'file' ]; then echo "file selected" INSTALL_FILE=1 fi - if [ "$var" = 'ports' ]; then + if [ "$option" = 'rpc' ]; then + echo "rpc selected" + INSTALL_RPC=1 + fi + if [ "$option" = 'wasm' ]; then + echo "wasm selected" + INSTALL_WASM=1 + fi + if [ "$option" = 'java' ]; then + echo "java selected" + INSTALL_JAVA=1 + fi + if [ "$option" = 'c' ]; then + echo "c selected" + INSTALL_C=1 + fi + if [ "$option" = 'cobol' ]; then + echo "cobol selected" + INSTALL_COBOL=1 + fi + if [ "$option" = 'backtrace' ]; then + echo "backtrace selected" + INSTALL_BACKTRACE=1 + fi + if [ "$option" = 'sandbox' ]; then + echo "sandbox selected" + INSTALL_SANDBOX=1 + fi + if [ "$option" = 'ports' ]; then echo "ports selected" INSTALL_PORTS=1 fi - if [ "$var" = 'clean' ]; then + if [ "$option" = 'clean' ]; then echo "clean selected" INSTALL_CLEAN=1 fi @@ -259,7 +565,7 @@ sub_options(){ sub_help() { echo "Usage: `basename "$0"` list of component" echo "Components:" - echo " root" + echo " debug | release | relwithdebinfo" echo " base" echo " python" echo " ruby" @@ -269,6 +575,13 @@ sub_help() { echo " nodejs" echo " typescript" echo " file" + echo " rpc" + echo " wasm" + echo " java" + echo " c" + echo " cobol" + echo " backtrace" + echo " sandbox" echo " ports" echo " clean" echo "" diff --git a/tools/metacall-sanitizer.sh b/tools/metacall-sanitizer.sh new file mode 100755 index 0000000000..cd3f08b402 --- /dev/null +++ b/tools/metacall-sanitizer.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# +# MetaCall Sanitizer Bash Script by Parra Studios +# Install, build and sanitizer test bash script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -euxo pipefail + +BUILD_SANITIZER=${1:-address-sanitizer} +BUILD_LANGUAGES=( + python ruby netcore8 nodejs typescript file rpc wasm java c cobol rust +) +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) +ROOT_DIR=$(dirname "$SCRIPT_DIR") +BUILD_DIR="${ROOT_DIR}/build" + +if [ "${BUILD_SANITIZER}" != "address-sanitizer" ] && [ "${BUILD_SANITIZER}" != "thread-sanitizer" ] && [ "${BUILD_SANITIZER}" != "memory-sanitizer" ]; then + echo "Sanitizer '${BUILD_SANITIZER}' not supported, use 'address-sanitizer' or 'thread-sanitizer' or 'memory-sanitizer'." + exit 1 +fi + +# Install +"${SCRIPT_DIR}/metacall-environment.sh" base ${BUILD_LANGUAGES[@]} rapidjson pack backtrace + +# Configure and Build +export NODE_PATH="/usr/lib/node_modules" +export LOADER_LIBRARY_PATH="${BUILD_DIR}" +export LOADER_SCRIPT_PATH="${BUILD_DIR}/scripts" +export CONFIGURATION_PATH="${BUILD_DIR}/configurations/global.json" +export SERIAL_LIBRARY_PATH="${BUILD_DIR}" +export DETOUR_LIBRARY_PATH="${BUILD_DIR}" + +BUILD_OPTIONS=( + ${BUILD_SANITIZER} debug ${BUILD_LANGUAGES[@]} examples tests scripts ports install pack benchmarks +) + +mkdir -p ${BUILD_DIR} +cd ${BUILD_DIR} +"${SCRIPT_DIR}/metacall-configure.sh" ${BUILD_OPTIONS[@]} +"${SCRIPT_DIR}/metacall-build.sh" ${BUILD_OPTIONS[@]} diff --git a/tools/metacall-tests-find-deadlocks.js b/tools/metacall-tests-find-deadlocks.js new file mode 100644 index 0000000000..d5ffd834fd --- /dev/null +++ b/tools/metacall-tests-find-deadlocks.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node +'use strict'; + +/* This script gets the output of ctest and returns + * the non-finished tests or the failed ones, useful + * for detecting deadlocks. Use it like: + * $ ctest -VV &> ctest_output_file.txt + * $ node metacall-tests-find-deadlocks.js ctest_output_file.txt + */ + +const { readFileSync } = require('fs'); + +if (!process.argv[2]) { + console.error('No file specified'); + process.exit(1); +} + +const file = readFileSync(process.argv[2]); + +if (!file) { + console.error('Failed to read the file'); + process.exit(2); +} + +const tests = file.toString().split('\n').reduce((tests, line) => { + const splitted = line.split(' ').filter(element => element); + if (splitted[0] === 'Start') { + tests.start.push(splitted[2]); + } else if (splitted[5] === 'Passed') { + tests.finish.push(splitted[3]); + } + return tests; +}, { start: [], finish: [] }); + +const deadlock = tests.start.filter(x => !tests.finish.includes(x)); + +console.log(deadlock); diff --git a/tools/runtime/Dockerfile b/tools/runtime/Dockerfile index 51bc322ead..4ac47f5184 100644 --- a/tools/runtime/Dockerfile +++ b/tools/runtime/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,7 +35,8 @@ LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ ARG METACALL_PATH # Environment variables -ENV DEBIAN_FRONTEND=noninteractive +ENV DEBIAN_FRONTEND=noninteractive \ + DOTNET_CLI_TELEMETRY_OPTOUT=true # Define working directory WORKDIR $METACALL_PATH @@ -60,9 +61,9 @@ ENV LOADER_LIBRARY_PATH=/usr/local/lib \ CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json \ SERIAL_LIBRARY_PATH=/usr/local/lib \ DETOUR_LIBRARY_PATH=/usr/local/lib \ - PORT_LIBRARY_PATH=/usr/local/lib \ DEBIAN_FRONTEND=noninteractive \ - NODE_PATH=/usr/local/lib/node_modules + NODE_PATH=/usr/local/lib/node_modules \ + DOTNET_CLI_TELEMETRY_OPTOUT=true # Define working directory WORKDIR $METACALL_PATH @@ -80,14 +81,20 @@ RUN mkdir -p /usr/local/scripts \ && rm -rf $METACALL_PATH/metacall-runtime.sh # Copy libraries from builder -COPY --from=builder /usr/local/lib/*.so /usr/local/lib/*.so* /usr/local/lib/*.dll /usr/local/lib/*.js /usr/local/lib/*.node /usr/local/lib/ - -# Copy node dependencies (and port) from builder -COPY --from=builder /usr/local/lib/node_modules/ /usr/local/lib/node_modules/ - -# Copy python dependencies and port from builder -COPY --from=builder /usr/local/lib/python3.7/dist-packages/metacall/__init__.py /usr/local/lib/python3.7/dist-packages/metacall/ -COPY --from=builder /usr/local/lib/python3.7/dist-packages/metacall/__pycache__/__init__.cpython-37.pyc /usr/local/lib/python3.7/dist-packages/metacall/__pycache__/ +COPY --from=builder /usr/local/lib/ /usr/local/lib/ + +# Delete unwanted files from libraries +RUN ls /usr/local/lib/ \ + | grep -v '.*\.so$' \ + | grep -v '.*\.so.*' \ + | grep -v '.*\.dll$' \ + | grep -v '.*\.js$' \ + | grep -v '.*\.ts$' \ + | grep -v '.*\.node$' \ + | grep -v '^plugins$' \ + | grep -v '^node_modules$' \ + | grep -v '^python3\..*' \ + | xargs rm -rf # Copy headers from builder COPY --from=builder /usr/local/include/metacall /usr/local/include/metacall diff --git a/tools/runtime/hooks/build b/tools/runtime/hooks/build deleted file mode 100644 index d4d97c116e..0000000000 --- a/tools/runtime/hooks/build +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build core image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - --build-arg "METACALL_PATH=$METACALL_PATH" \ - --build-arg "METACALL_BASE_IMAGE=$METACALL_BASE_IMAGE" \ - --build-arg "METACALL_RUNTIME_OPTIONS=$METACALL_RUNTIME_OPTIONS" \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/runtime/hooks/env b/tools/runtime/hooks/env deleted file mode 100644 index 55950bbe3d..0000000000 --- a/tools/runtime/hooks/env +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# Core arguments -METACALL_RUNTIME_OPTIONS="root base python ruby netcore2 nodejs file ports clean" # v8 - -# Core environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip -LOADER_LIBRARY_PATH=/usr/local/lib -LOADER_SCRIPT_PATH=/usr/local/scripts -CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json -SERIAL_LIBRARY_PATH=/usr/local/lib -DETOUR_LIBRARY_PATH=/usr/local/lib - -# Override docker image -IMAGE_NAME=metacall/core:runtime