diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 00000000000..d118cf11ec7 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,486 @@ +name: Compile SOFA and run Tests +on: + workflow_call: + inputs: + sofa-branch-name: + type: string + required: true + sofa-commit-sha: + type: string + required: true + pr-owner-url: + type: string + required: false + pr-branch-name: + type: string + required: false + pr-commit-sha: + type: string + required: false + preset: + type: string + required: true + python-version: + type: string + required: true + ci-depends-on: + type: string + required: false + with-all-tests: + type: boolean + required: false + default: false + force-full-build: + type: boolean + required: false + default: false + generate-binaries: + type: boolean + required: false + default: false + external-plugins: + type: string + required: false + additionnal-cmake-flags: + type: string + required: false + builder-os: + type: string + required: true + default: '["sh-ubuntu_gcc_release"]' + dash-info: + type: string + required: false + default: 'NONE' + description: 'Json scructure with three parameters {"COMMIT_HASH":"fullhsashofthecommit", }' + +jobs: + display-inputs: + runs-on: ubuntu-latest + steps: + - name: Display + shell: bash + run: | + echo "Build and test launched with following parameters:" + echo "sofa-branch-name : ${{ inputs.sofa-branch-name }}" + echo "sofa-commit-sha : ${{ inputs.sofa-commit-sha }}" + echo "pr-owner-url : ${{ inputs.pr-owner-url }}" + echo "pr-branch-name : ${{ inputs.pr-branch-name }}" + echo "pr-commit-sha : ${{ inputs.pr-commit-sha }}" + echo "preset : ${{ inputs.preset }}" + echo "python-version : ${{ inputs.python-version }}" + echo "ci-depends-on : ${{ inputs.ci-depends-on }}" + echo "with-all-tests : ${{ inputs.with-all-tests }}" + echo "force-full-build : ${{ inputs.force-full-build }}" + echo "generate-binaries : ${{ inputs.generate-binaries }}" + echo "external-plugins : ${{ inputs.external-plugins }}" + echo "additionnal-cmake-flags : ${{ inputs.additionnal-cmake-flags }}" + echo "builder-os : ${{ inputs.builder-os }}" + + + build: + strategy: + fail-fast: false + matrix: + os: ${{ fromJson(inputs.builder-os) }} + runs-on: ${{ matrix.os }} + name: ${{ matrix.os }} + steps: + - name: Configure builder + id: configure + shell: bash + run: | + ## Go to workspace given by the runner (this file is generated by the pre-build script) + WORKSPACE=$(cat $GITHUB_WORKFLOW_SHA)/${{ matrix.os }} + echo "WORKSPACE=$WORKSPACE" >> $GITHUB_ENV + echo "LOG_DIR=$WORKSPACE" >> $GITHUB_ENV + echo "BUILDER_OS=${{ matrix.os }}" >> $GITHUB_ENV + + if [ ! -d $WORKSPACE ]; then + mkdir -p $WORKSPACE + fi + cd $WORKSPACE + + echo "Workspace folder for this build will be $WORKSPACE" + + ## Setup github + # Git config (needed by CMake ExternalProject) + if ! git config --get user.name; then + git config --system user.name 'SOFA Bot' > /dev/null 2>&1 || + git config --global user.name 'SOFA Bot' > /dev/null 2>&1 || + git config user.name 'SOFA Bot' > /dev/null 2>&1 || + echo "WARNING: cannot setup git config" + fi + if ! git config --get user.email; then + git config --system user.email '<>' > /dev/null 2>&1 || + git config --global user.email '<>' > /dev/null 2>&1 || + git config user.email '<>' > /dev/null 2>&1 || + echo "WARNING: cannot setup git config" + fi + + ##TODO (if required) fix path too long for windows (see main.sh:201) + + + - name: Clone SOFA and CI + id: clone + shell: bash + run: | + cd $WORKSPACE + + ## Clone sofa and merge origin master + echo "Cloning SOFA at commit $GITHUB_WORKFLOW_SHA" + if [ -d $WORKSPACE/sofa ]; then + rm -rf $WORKSPACE/sofa + fi + SRC_DIR=$(pwd)/sofa + echo "SRC_DIR=$SRC_DIR" >> $GITHUB_ENV + + if [ ! -z "${{ inputs.pr-owner-url }}" ]; then + echo "This is a PR, merging branch ${{ inputs.pr-branch-name }} from remote ${{ inputs.pr-owner-url }} into origin branch ${{ inputs.sofa-branch-name }}" + git clone -b ${{ inputs.sofa-branch-name }} --single-branch https://www.github.com/sofa-framework/sofa + cd sofa + git remote add pr ${{ inputs.pr-owner-url }} + git fetch pr + + if [ "${{ inputs.pr-commit-sha }}" == "HEAD" ]; then + git merge ${{ inputs.pr-branch-name }} + else + git merge ${{ inputs.pr-commit-sha }} + fi + else + echo "This is not a PR: checking out sha ${{ inputs.sofa-commit-sha }} from branch ${{ inputs.sofa-branch-name }}" + git clone -b ${{ inputs.sofa-branch-name }} --single-branch https://www.github.com/bakpaul/sofa ## TODO : REMOVE THIS WITH SOFA_FRAMEWORK @@@@@@@@@@@@@ + cd sofa + git checkout ${{ inputs.sofa-commit-sha }} + fi + + cd $WORKSPACE + + ## Clone CI and use ci-depends-on structure + ### TODO: UNCOMMENT THIS WHEN PUSHIN TO REAL SOFA + # ci_branch=${{ inputs.sofa-branch-name }} + # ci_repo_url="https://www.github.com/sofa-framework/ci" + ### AND REMOVE THOSE + ci_branch=jenkins_gha_migration + ci_repo_url="https://www.github.com/bakpaul/ci" + + + # check if ci has a ci-depends-on + ci_ci_depends_on=$(echo "${{ inputs.ci-depends-on }}" | jq .ci) + if [ -n "${{ inputs.ci-depends-on }}" ] && [ "$ci_ci_depends_on" != "null" ]; then + echo "ci-depends-on for ci repository detected." + ci_repo_url=$(echo "${{ inputs.ci-depends-on }}" | jq .ci.repo_url) + ci_branch=$(echo "${{ inputs.ci-depends-on }}" | jq .ci.branch_name) + fi + echo "CI_BRANCH=$ci_branch" >> $GITHUB_ENV + + + echo "Cloning CI from remote ${ci_repo_url}, selecting branch ${ci_branch}" + + if [ -d $WORKSPACE/ci ]; then + rm -rf $WORKSPACE/ci + fi + CI_DIR=$WORKSPACE/ci + echo "CI_DIR=$CI_DIR" >> $GITHUB_ENV + + git clone -b ${ci_branch//\"} --single-branch ${ci_repo_url//\"} + + cd $WORKSPACE + + ## Setup build folder + if [ "${{ inputs.force-full-build }}" == "true" ] && [ -d $WORKSPACE/build ]; then + rm -rf $WORKSPACE/build + fi + if [ ! -d $WORKSPACE/build ]; then + mkdir $WORKSPACE/build + fi + + BUILD_DIR=$WORKSPACE/build + echo "BUILD_DIR=$BUILD_DIR" >> $GITHUB_ENV + + if [ -n "${{ inputs.external-plugins }}" ]; then + echo "Setting up external plugins." + for plugin in ${{ inputs.external-plugins }}; do + plugin_base=${plugin%@*} + plugin_branch=${plugin##*@} + plugin_repo=$(basename "$plugin_base") + + echo "Adding line 'sofa_add_external(plugin $plugin_repo GIT_REF $plugin_branch GIT_REPOSITORY $plugin_base ON)' to file ${SRC_DIR}/applications/CMakeLists.txt" + echo "sofa_add_external(plugin $plugin_repo GIT_REF $plugin_branch GIT_REPOSITORY $plugin_base ON)" >> "${SRC_DIR}/applications/CMakeLists.txt" + done + fi + + - name: Build + id: build-step + shell: bash + run: | + ## Configure the build: setup cmake variables + # retrive build type and compiler + BUILD_TYPE=$(echo ${{ matrix.os }} | awk -F '_' '{print $3}') + CONFIG=$(echo ${{ matrix.os }} | awk -F '_' '{print $1"_"$2}') + # Needed by configure-and-build + if [[ "$RUNNER_OS" == "Linux" ]]; then + NODE_NAME="$(hostname)" + else + NODE_NAME="$(scutil --get LocalHostName)" + fi + echo "NODE_NAME=$NODE_NAME" >> $GITHUB_ENV + + ## Deal with ci-depends-on + # Loop over each key-value pair in the JSON avoiding ci repository if inside + CMAKE_OPTIONS="${{inputs.additionnal-cmake-flags}}" + for key in $(echo "${{ inputs.ci-depends-on }}" | jq -r 'keys[]' | grep -v '^ci$'); do + + repo_url=$(echo "${{ inputs.ci-depends-on }}" | jq -r ".\"$key\".repo_url") + branch_name=$(echo "${{ inputs.ci-depends-on }}" | jq -r ".\"$key\".branch_name") + + # Format the CMake flags for this key and append to the result + fixed_name=$(echo "$key" | awk '{gsub(/\./, "_"); print toupper($0)}') + flag_repository="-D${fixed_name}_GIT_REPOSITORY=\"$repo_url\"" + flag_tag="-D${fixed_name}_GIT_TAG=\"$branch_name\"" + + # Append both flags to the result string with a space + CMAKE_OPTIONS="$CMAKE_OPTIONS $flag_repository $flag_tag " + done + + if [[ ! -n "$CMAKE_OPTIONS" ]]; then + CMAKE_OPTIONS="no-additionnal-cmake-flags" + echo "No ci-depends-on detected." + else + echo "Adding the following cmake variable : $CMAKE_OPTIONS" + fi + + echo "" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo "------ Before build (and docker) ------" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo "Main folders:" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - WORKSPACE = $WORKSPACE" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - SRC_DIR = $SRC_DIR" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - CI_DIR = $CI_DIR" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - BUILD_DIR = $BUILD_DIR" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - LOG_DIR = ${{ env.LOG_DIR }}" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo "" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo "Configuration:" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - NODE_NAME = $NODE_NAME" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - BUILD_TYPE = $BUILD_TYPE" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - CONFIG = $CONFIG" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - PYTHON_VERSION = ${{ inputs.python-version }}" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - PRESET = ${{ inputs.preset }}" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - GENERATE_BINARIES = ${{ inputs.generate-binaries }}" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo " - ADDITIONNAL_CMAKE_OPTIONS = $CMAKE_OPTIONS" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo "---------------------------------------" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + echo "" | tee -a "${{ env.LOG_DIR }}/cmake-output.txt" + + #If os has got docker then use it + if [[ "${{ matrix.os }}" == *"ubuntu"* ]] || [[ "${{ matrix.os }}" == *"fedora"* ]]; then + builder_type=$(echo "${{ matrix.os }}" | awk -F '_' '{print $1}' | awk -F '-' '{print $2}') + if [[ "${{ inputs.sofa-branch-name }}" == "master" ]] || [[ "${{ inputs.sofa-branch-name }}" =~ ^v[0-9]{2}\.[0-9]{2}$ ]]; then + default_tag=${{ inputs.sofa-branch-name }} + else + default_tag=master + fi + + echo "${DOCKERHUB_TOKEN}" | docker login -u sofaframework --password-stdin + + echo "Pulling Docker image sofaframework/sofabuilder_${builder_type}:${CI_BRANCH} ..." + docker pull --quiet sofaframework/sofabuilder_${builder_type}:${CI_BRANCH} || true + DOCKER_IMAGE="sofaframework/sofabuilder_${builder_type}:${CI_BRANCH}" + + if [[ "$(docker image list --format "table {{.Repository}}:{{.Tag}}")" != *"sofaframework/sofabuilder_${builder_type}:${CI_BRANCH}"* ]]; then + echo "Docker image sofaframework/sofabuilder_${builder_type}:${CI_BRANCH} doesn't exist, pulling default tag ${default_tag} " + docker pull --quiet sofaframework/sofabuilder_${builder_type}:${default_tag} + + DOCKER_IMAGE="sofaframework/sofabuilder_${builder_type}:${default_tag}" + fi + + echo "DOCKER_IMAGE=$DOCKER_IMAGE" >> $GITHUB_ENV + + DOCKER_SRC_DIR="/workspace/$(realpath --relative-to=$WORKSPACE $SRC_DIR)" + DOCKER_BUILD_DIR="/workspace/$(realpath --relative-to=$WORKSPACE $BUILD_DIR)" + DOCKER_CI_DIR="/workspace/$(realpath --relative-to=$WORKSPACE $CI_DIR)" + DOCKER_LOG_DIR="/workspace/$(realpath --relative-to=$WORKSPACE ${{ env.LOG_DIR }})" + + echo "DOCKER_SRC_DIR=$DOCKER_SRC_DIR" >> $GITHUB_ENV + echo "DOCKER_BUILD_DIR=$DOCKER_BUILD_DIR" >> $GITHUB_ENV + echo "DOCKER_CI_DIR=$DOCKER_CI_DIR" >> $GITHUB_ENV + echo "DOCKER_LOG_DIR=$DOCKER_LOG_DIR" >> $GITHUB_ENV + + export XDG_CACHE_HOME="/cache" + + echo "Launching configuration and build through docker using image ${DOCKER_IMAGE}. Running ./ci/scripts/configure-and-build.sh /workspace/build/ /workspace/sofa/ /workspace/ci/scripts/ \"${NODE_NAME}\" \"${BUILD_TYPE}\" \"${CONFIG}\" \"${{ inputs.python-version }}\" \"${{ inputs.preset }}\" \"${{ inputs.generate-binaries }}\" \"${CMAKE_OPTIONS}\"" + docker run --rm --env XDG_CACHE_HOME -v $BUILDER_CACHE_DIR:/cache\ + --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ + ${DOCKER_IMAGE} \ + /bin/bash -c "env; cd /workspace; ccache -M 5; /bin/bash $DOCKER_CI_DIR/scripts/configure-and-build.sh $DOCKER_BUILD_DIR $DOCKER_SRC_DIR $DOCKER_CI_DIR/scripts/ $DOCKER_LOG_DIR \"${NODE_NAME}\" \"${BUILD_TYPE}\" \"${CONFIG}\" \"${{ inputs.python-version }}\" \"${{ inputs.preset }}\" \"${{ inputs.generate-binaries }}\" \"${CMAKE_OPTIONS}\"" + else + echo "Launching configuration and build" + + bash ${CI_DIR}/scripts/configure-and-build.sh "${BUILD_DIR}" "${SRC_DIR}" "${CI_DIR}/scripts/" "${{ env.LOG_DIR }}" "${NODE_NAME}" "${BUILD_TYPE}" "${CONFIG}" "${{ inputs.python-version }}" "${{ inputs.preset }}" "${{ inputs.generate-binaries }}" "${CMAKE_OPTIONS}" + fi + + + - name: Unit tests + id: unit-tests-step + if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false + shell: bash + run: | + # If os has got docker then use it + # Here no need to fetch it because it has already been taken care of in the build step + if [[ "${{ matrix.os }}" == *"ubuntu"* ]] || [[ "${{ matrix.os }}" == *"fedora"* ]]; then + + echo "Launching unit test suite through docker using image ${DOCKER_IMAGE}." + docker run --rm \ + --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ + ${DOCKER_IMAGE} \ + /bin/bash -c "env ; cd /workspace; /bin/bash $DOCKER_CI_DIR/scripts/test.sh $DOCKER_BUILD_DIR $DOCKER_SRC_DIR $DOCKER_CI_DIR/scripts/ ${NODE_NAME} ${{ inputs.python-version }} UNIT" + else + echo "Launching unit test suite." + + bash ${CI_DIR}/scripts/test.sh "${BUILD_DIR}" "${SRC_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" "UNIT" + fi + + if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" == *"unit-tests_"* ]]; then + exit 1 + fi + + - name: Scene tests + id: scene-tests-step + if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true + shell: bash + run: | + # If os has got docker then use it + # Here no need to fetch it because it has already been taken care of in the build step + if [[ "${{ matrix.os }}" == *"ubuntu"* ]] || [[ "${{ matrix.os }}" == *"fedora"* ]]; then + + echo "Launching scene test suite through docker using image ${DOCKER_IMAGE}." + docker run --rm \ + --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ + ${DOCKER_IMAGE} \ + /bin/bash -c "env ; cd /workspace; /bin/bash $DOCKER_CI_DIR/scripts/test.sh $DOCKER_BUILD_DIR $DOCKER_SRC_DIR $DOCKER_CI_DIR/scripts/ ${NODE_NAME} ${{ inputs.python-version }} SCENE" + else + echo "Launching scene test suite." + + bash ${CI_DIR}/scripts/test.sh "${BUILD_DIR}" "${SRC_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" "SCENE" + fi + + if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" == *"scene-tests_"* ]]; then + exit 1 + fi + + - name: Regression tests + id: regression-tests-step + if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true + shell: bash + run: | + # If os has got docker then use it + # Here no need to fetch it because it has already been taken care of in the build step + if [[ "${{ matrix.os }}" == *"ubuntu"* ]] || [[ "${{ matrix.os }}" == *"fedora"* ]]; then + + echo "Launching regression test suite through docker using image ${DOCKER_IMAGE}." + docker run --rm \ + --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ + ${DOCKER_IMAGE} \ + /bin/bash -c "env ; cd /workspace; /bin/bash $DOCKER_CI_DIR/scripts/test.sh $DOCKER_BUILD_DIR $DOCKER_SRC_DIR $DOCKER_CI_DIR/scripts/ ${NODE_NAME} ${{ inputs.python-version }} REGRESSION" + else + echo "Launching regression test suite" + + bash ${CI_DIR}/scripts/test.sh "${BUILD_DIR}" "${SRC_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" "REGRESSION" + fi + + if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" == *"regression-tests_"* ]]; then + exit 1 + fi + + + - name: Publish artifacts + if: steps.build-step.outcome == 'success' && inputs.generate-binaries == true + uses: actions/upload-artifact@v4 + with: + name: binaries_${{ env.BUILDER_OS }} + path: | + ${{ env.BUILD_DIR }}/SOFA_v* + + + - name: Publish build logs + id: publish-build-logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: build-logs_${{ env.BUILDER_OS }} + path: | + ${{ env.LOG_DIR }}/make-output.txt + ${{ env.LOG_DIR }}/cmake-output.txt + + + - name: Publish tests logs + id: publish-tests-logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: tests-logs_${{ env.BUILDER_OS }} + path: | + ${{ env.BUILD_DIR }}/tests_results/ + + + - name: Summarize results + if: always() + shell: bash + run: | + bold="\033[1m" + normal="\033[0m" + if [[ "${{ steps.configure.outcome }}" == "failure" || "${{ steps.clone.outcome }}" == "failure" ]]; then + echo "❌ Something whent wrong during the configure or clone steps..." + echo -e "${bold}Please check those actions logs${normal}" + exit 1 + else + echo "✅ Setup OK." + fi + + if [[ "${{ steps.build-step.outcome }}" == "failure" ]]; then + echo "❌ Build step failed" + echo -e "${bold}You can download all logs here : ${{ steps.publish-build-logs.outputs.artifact-url }}${normal}" + exit 1 + else + echo "✅ Build OK." + fi + + if [[ "${{ inputs.generate-binaries }}" == "false" ]]; then + if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" != "" ]]; then + echo "❌ Tests have resulted in : " + echo "" + python3 ${{ env.CI_DIR }}/scripts/generate-tests-table.py "${{ env.BUILD_DIR }}/tests_results/" + echo "" + echo "#----------------------------------------#" + echo "#---------------Quick Logs---------------#" + echo "#----------------------------------------#" + for testType in 'unit' 'scene' 'regression'; + do + for errorType in 'errors' 'crashes' 'failures'; + do + if [ -f "${{ env.BUILD_DIR }}/tests_results/${testType}-tests_${errorType}" ]; then + capitalized="$(echo "${testType:0:1}" | tr '[:lower:]' '[:upper:]')${testType:1}" + echo "" + echo -e "${bold}${capitalized} ${errorType}:${normal}" + echo "" + cat "${{ env.BUILD_DIR }}/tests_results/${testType}-tests_${errorType}" + echo "" + echo "#----------------------------------------#" + fi + done + done + echo -e "${bold}You can download all logs here : ${{ steps.publish-tests-logs.outputs.artifact-url }}${normal}" + exit 1 + elif [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]]; then + echo "❌ Something whent wrong during one of the tests steps" + echo -e "${bold}Please check this action logs, and download tests logs here: ${{ steps.publish-tests-logs.outputs.artifact-url }}${normal}" + exit 1 + else + echo "✅ Tests where successful : " + echo "" + python3 ${{ env.CI_DIR }}/scripts/generate-tests-table.py "${{ env.BUILD_DIR }}/tests_results/" + echo "" + fi + fi + + + + diff --git a/.github/workflows/nightly-build-and-tests.yml b/.github/workflows/nightly-build-and-tests.yml new file mode 100644 index 00000000000..c9789cb85e6 --- /dev/null +++ b/.github/workflows/nightly-build-and-tests.yml @@ -0,0 +1,122 @@ +name: Nightly build and tests + +# =============================================================== +# =============================================================== + + +on: + # Nightly build + schedule: + - cron: '0 2 * * *' # Evevery night + +# =============================================================== +# =============================================================== + + +jobs: + # Nightly build triggered once a day + nightly_build: + runs-on: ubuntu-latest + strategy: + matrix: + sofa_version: [master] + ### if: ${{ github.repository_owner == 'sofa-framework' }} + outputs: + SOFA_BRANCH_NAME: ${{ steps.export-vars.outputs.SOFA_BRANCH_NAME }} + SOFA_COMMIT_SHA: ${{ steps.export-vars.outputs.SOFA_COMMIT_SHA }} + PRESET: ${{ steps.export-vars.outputs.PRESET }} + PYTHON_VERSION: ${{ steps.export-vars.outputs.PYTHON_VERSION }} + CI_DEPENDS_ON: ${{ steps.export-vars.outputs.CI_DEPENDS_ON }} + WITH_ALL_TESTS: ${{ steps.export-vars.outputs.WITH_ALL_TESTS }} + FORCE_FULL_BUILD: ${{ steps.export-vars.outputs.FORCE_FULL_BUILD }} + EXTERNAL_PLUGINS: ${{ steps.export-vars.outputs.EXTERNAL_PLUGINS }} + ADDITIONNAL_CMAKE_FLAGS: ${{ steps.export-vars.outputs.ADDITIONNAL_CMAKE_FLAGS }} + GENERATE_BINARIES: ${{ steps.export-vars.outputs.GENERATE_BINARIES }} + PR_OWNER_URL: ${{ steps.export-vars.outputs.PR_OWNER_URL }} + PR_BRANCH_NAME: ${{ steps.export-vars.outputs.PR_BRANCH_NAME }} + PR_COMMIT_SHA: ${{ steps.export-vars.outputs.PR_COMMIT_SHA }} + BUILDER_OS: ${{ steps.export-vars.outputs.BUILDER_OS }} + + steps: + - name: Default values of environment variables + run: | + echo "SOFA_BRANCH_NAME=master" >> $GITHUB_ENV # SOFA_BRANCH_NAME: "master" + echo "SOFA_COMMIT_SHA=HEAD" >> $GITHUB_ENV # SOFA_COMMIT_SHA: "HEAD" + echo "PRESET=full" >> $GITHUB_ENV # PRESET: "full" + echo "PYTHON_VERSION=3.12" >> $GITHUB_ENV # PYTHON_VERSION: "3.12" + echo "CI_DEPENDS_ON=" >> $GITHUB_ENV # CI_DEPENDS_ON: "" + echo "WITH_ALL_TESTS=false" >> $GITHUB_ENV # WITH_ALL_TESTS: false + echo "FORCE_FULL_BUILD=false" >> $GITHUB_ENV # FORCE_FULL_BUILD: false + echo "EXTERNAL_PLUGINS=" >> $GITHUB_ENV # EXTERNAL_PLUGINS: "" + echo "ADDITIONNAL_CMAKE_FLAGS=" >> $GITHUB_ENV # ADDITIONNAL_CMAKE_FLAGS: "" + echo "GENERATE_BINARIES=false" >> $GITHUB_ENV # GENERATE_BINARIES: false + echo "PR_OWNER_URL=" >> $GITHUB_ENV # PR_OWNER_URL: "" + echo "PR_BRANCH_NAME=" >> $GITHUB_ENV # PR_BRANCH_NAME: "" + echo "PR_COMMIT_SHA=HEAD" >> $GITHUB_ENV # PR_COMMIT_SHA: "HEAD" + echo 'BUILDER_OS=["sh-ubuntu_gcc_release","sh-macos_clang_release"]' >> $GITHUB_ENV # BUILDER_OS: ["sh-ubuntu_gcc_release","sh-macos_clang_release"] + + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install pip packages + run: | + pip install python-graphql-client + pip install requests + + - name: Check out code + uses: actions/checkout@v2 + + - name: Run when nightly + run: | + echo "This step runs only for nightly builds." + echo "SOFA_BRANCH_NAME=${{ matrix.sofa_version }}" >> $GITHUB_ENV + echo "PRESET=standard-dev" >> $GITHUB_ENV + echo "WITH_ALL_TESTS=true" >> $GITHUB_ENV + echo "GENERATE_BINARIES=true" >> $GITHUB_ENV + echo 'BUILDER_OS=["sh-ubuntu_gcc_release","sh-macos_clang_release"]' >> $GITHUB_ENV + + - name: Export environment variables as outputs + id: export-vars + run: | + echo "SOFA_BRANCH_NAME=${SOFA_BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "SOFA_COMMIT_SHA=${SOFA_COMMIT_SHA}" >> $GITHUB_OUTPUT + echo "PRESET=${PRESET}" >> $GITHUB_OUTPUT + echo "PYTHON_VERSION=${PYTHON_VERSION}" >> $GITHUB_OUTPUT + echo "CI_DEPENDS_ON=${CI_DEPENDS_ON}" >> $GITHUB_OUTPUT + echo "WITH_ALL_TESTS=${WITH_ALL_TESTS}" >> $GITHUB_OUTPUT + echo "FORCE_FULL_BUILD=${FORCE_FULL_BUILD}" >> $GITHUB_OUTPUT + echo "EXTERNAL_PLUGINS=${EXTERNAL_PLUGINS}" >> $GITHUB_OUTPUT + echo "ADDITIONNAL_CMAKE_FLAGS=${ADDITIONNAL_CMAKE_FLAGS}" >> $GITHUB_OUTPUT + echo "GENERATE_BINARIES=${GENERATE_BINARIES}" >> $GITHUB_OUTPUT + echo "PR_OWNER_URL=${PR_OWNER_URL}" >> $GITHUB_OUTPUT + echo "PR_BRANCH_NAME=${PR_BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "PR_COMMIT_SHA=${PR_COMMIT_SHA}" >> $GITHUB_OUTPUT + echo "BUILDER_OS=${BUILDER_OS}" >> $GITHUB_OUTPUT + + + + + # =============================================================== + # =============================================================== + + # Trigger the build and sharing all parameters from nightly_build > outputs + build-on: + needs: nightly_build + uses: bakpaul/sofa/.github/workflows/build-and-test.yml@master + with: + sofa-branch-name: ${{ needs.nightly_build.outputs.SOFA_BRANCH_NAME }} + sofa-commit-sha: ${{ needs.nightly_build.outputs.SOFA_COMMIT_SHA }} + preset: ${{ needs.nightly_build.outputs.PRESET }} + python-version: ${{ needs.nightly_build.outputs.PYTHON_VERSION }} + ci-depends-on: ${{ needs.nightly_build.outputs.CI_DEPENDS_ON }} + with-all-tests: ${{ needs.nightly_build.outputs.WITH_ALL_TESTS == 'true'}} + force-full-build: ${{ needs.nightly_build.outputs.FORCE_FULL_BUILD == 'true'}} + external-plugins: ${{ needs.nightly_build.outputs.EXTERNAL_PLUGINS }} + additionnal-cmake-flags: ${{ needs.nightly_build.outputs.ADDITIONNAL_CMAKE_FLAGS }} + generate-binaries: ${{ needs.nightly_build.outputs.GENERATE_BINARIES == 'true'}} + pr-owner-url: ${{ needs.nightly_build.outputs.PR_OWNER_URL }} + pr-branch-name: ${{ needs.nightly_build.outputs.PR_BRANCH_NAME }} + pr-commit-sha: ${{ needs.nightly_build.outputs.PR_COMMIT_SHA }} + builder-os: ${{ needs.nightly_build.outputs.BUILDER_OS }} diff --git a/.github/workflows/reboot_builders.yml b/.github/workflows/reboot_builders.yml new file mode 100644 index 00000000000..082df4d9d5e --- /dev/null +++ b/.github/workflows/reboot_builders.yml @@ -0,0 +1,81 @@ +name: Reboot builders + +# =============================================================== +# =============================================================== + + +on: + # On-demand binary generation + workflow_dispatch: + inputs: + builder-os: + type: choice + description: On which OS run the binaries generation + options: + - '["sofa-ci-dev-ubuntu2204-0"]' + - '["sofa-ci-dev-ubuntu2204-1"]' + - '["sofa-ci-dev-ubuntu2204-2"]' + - '["sofa-ci-dev-ubuntu2204-3"]' + - '["sofa-ci-dev-ubuntu2204-4"]' + - '["consortium-macos-2"]' + - '["consortium-macos-3"]' + - '["sofa-ci-dev-ubuntu2204-0","sofa-ci-dev-ubuntu2204-1","sofa-ci-dev-ubuntu2204-2","sofa-ci-dev-ubuntu2204-3","sofa-ci-dev-ubuntu2204-4","consortium-macos-2","consortium-macos-3"]' + default: '["sofa-ci-dev-ubuntu2204-0","sofa-ci-dev-ubuntu2204-1","sofa-ci-dev-ubuntu2204-2","sofa-ci-dev-ubuntu2204-3","sofa-ci-dev-ubuntu2204-4","consortium-macos-2","consortium-macos-3"]' + + + + # Nightly build + schedule: + - cron: '30 1 * * *' # Evevery night @1:30 a.m. + +# =============================================================== +# =============================================================== + + +jobs: + # Filter build handling : push in master, commits in PR, comments in PR and dispatch + reboot_builders_by_hand: + if: ${{ github.event_name != 'schedule'}} + strategy: + fail-fast: false + matrix: + os: ${{ fromJson(inputs.builder-os) }} + runs-on: ${{ matrix.os }} + steps: + - name: Clear ccache + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + if [[ -n "$BUILDER_CACHE_DIR" ]]; then + rm -rf $BUILDER_CACHE_DIR/* + fi + + - name: Reboot builder + shell: bash + run: | + # WARNING: this command needs to add in /etc/sudoers this line: + # ALL ALL=NOPASSWD: /usr/sbin/reboot, /usr/sbin/shutdown, /sbin/reboot, /sbin/shutdown + echo '' | sudo -S /sbin/shutdown -r +1 & + + rebot_builder_nightly: + if: ${{ github.event_name == 'schedule'}} + strategy: + fail-fast: false + matrix: + os: ["sofa-ci-dev-ubuntu2204-0","sofa-ci-dev-ubuntu2204-1","sofa-ci-dev-ubuntu2204-2","sofa-ci-dev-ubuntu2204-3","sofa-ci-dev-ubuntu2204-4","consortium-macos-2","consortium-macos-3"] + runs-on: ${{ matrix.os }} + steps: + - name: Clear ccache + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + if [[ -n "$BUILDER_CACHE_DIR" ]]; then + rm -rf $BUILDER_CACHE_DIR/* + fi + + - name: Reboot builder + shell: bash + run: | + # WARNING: this command needs to add in /etc/sudoers this line: + # ALL ALL=NOPASSWD: /usr/sbin/reboot, /usr/sbin/shutdown, /sbin/reboot, /sbin/shutdown + echo '' | sudo -S /sbin/shutdown -r +1 & \ No newline at end of file diff --git a/.github/workflows/trigger-build-and-tests.yml b/.github/workflows/trigger-build-and-tests.yml new file mode 100644 index 00000000000..69a8abc0afa --- /dev/null +++ b/.github/workflows/trigger-build-and-tests.yml @@ -0,0 +1,224 @@ +name: Trigger build and tests + +# =============================================================== +# =============================================================== + + +on: + # On-demand binary generation + workflow_dispatch: + inputs: + branch: + description: 'Specify the stable branch to use to generate the new binaries' + required: true + type: string + python_version: + description: 'Version of Python used' + required: true + default: '3.12' + type: string + external-plugins: + description: 'List of github repository and branch name to add to build tree, separated by a space. Syntax: https://www.github.com/me/myrepo@mybranch' + required: false + type: string + additionnal-cmake-flags: + description: 'CMake flags to add during the CMake call. This is to be used along external-plugins. The syntax is identical to a CMake call' + required: false + type: string + preset: + type: choice + description: Which preset to use for compilation + options: + - standard + - supported-plugins + - full + - standard-dev + - supported-plugins-dev + - full-dev + default: 'full' + builder-os: + type: choice + description: On which OS run the binaries generation + options: + - '["sh-ubuntu_gcc_release"]' + - '["sh-macos_clang_release"]' + - '["sh-ubuntu_gcc_release","sh-macos_clang_release"]' + default: '["sh-ubuntu_gcc_release","sh-macos_clang_release"]' + + # PR-related build (open, labels, push) + pull_request: + types: [opened, synchronize] + + # CI for dashboard master + push: + branches: + - 'master' + - 'v25.12' + +# =============================================================== +# =============================================================== + + +jobs: + # Filter build handling : push in master, commits in PR, comments in PR and dispatch + filter_build: + runs-on: ubuntu-latest + outputs: + SOFA_BRANCH_NAME: ${{ steps.export-vars.outputs.SOFA_BRANCH_NAME }} + SOFA_COMMIT_SHA: ${{ steps.export-vars.outputs.SOFA_COMMIT_SHA }} + PRESET: ${{ steps.export-vars.outputs.PRESET }} + PYTHON_VERSION: ${{ steps.export-vars.outputs.PYTHON_VERSION }} + CI_DEPENDS_ON: ${{ steps.export-vars.outputs.CI_DEPENDS_ON }} + WITH_ALL_TESTS: ${{ steps.export-vars.outputs.WITH_ALL_TESTS }} + FORCE_FULL_BUILD: ${{ steps.export-vars.outputs.FORCE_FULL_BUILD }} + EXTERNAL_PLUGINS: ${{ steps.export-vars.outputs.EXTERNAL_PLUGINS }} + ADDITIONNAL_CMAKE_FLAGS: ${{ steps.export-vars.outputs.ADDITIONNAL_CMAKE_FLAGS }} + GENERATE_BINARIES: ${{ steps.export-vars.outputs.GENERATE_BINARIES }} + PR_OWNER_URL: ${{ steps.export-vars.outputs.PR_OWNER_URL }} + PR_BRANCH_NAME: ${{ steps.export-vars.outputs.PR_BRANCH_NAME }} + PR_COMMIT_SHA: ${{ steps.export-vars.outputs.PR_COMMIT_SHA }} + BUILDER_OS: ${{ steps.export-vars.outputs.BUILDER_OS }} + + steps: + - name: Default values of environment variables + run: | + echo "SOFA_BRANCH_NAME=master" >> $GITHUB_ENV # SOFA_BRANCH_NAME: "master" + echo "SOFA_COMMIT_SHA=HEAD" >> $GITHUB_ENV # SOFA_COMMIT_SHA: "HEAD" + echo "PRESET=full" >> $GITHUB_ENV # PRESET: "full" + echo "PYTHON_VERSION=3.12" >> $GITHUB_ENV # PYTHON_VERSION: "3.12" + echo "CI_DEPENDS_ON=" >> $GITHUB_ENV # CI_DEPENDS_ON: "" + echo "WITH_ALL_TESTS=false" >> $GITHUB_ENV # WITH_ALL_TESTS: false + echo "FORCE_FULL_BUILD=false" >> $GITHUB_ENV # FORCE_FULL_BUILD: false + echo "EXTERNAL_PLUGINS=" >> $GITHUB_ENV # EXTERNAL_PLUGINS: "" + echo "ADDITIONNAL_CMAKE_FLAGS=" >> $GITHUB_ENV # ADDITIONNAL_CMAKE_FLAGS: "" + echo "GENERATE_BINARIES=false" >> $GITHUB_ENV # GENERATE_BINARIES: false + echo "PR_OWNER_URL=" >> $GITHUB_ENV # PR_OWNER_URL: "" + echo "PR_BRANCH_NAME=" >> $GITHUB_ENV # PR_BRANCH_NAME: "" + echo "PR_COMMIT_SHA=HEAD" >> $GITHUB_ENV # PR_COMMIT_SHA: "HEAD" + echo 'BUILDER_OS=["sh-ubuntu_gcc_release"]' >> $GITHUB_ENV # BUILDER_OS: ["sh-ubuntu_gcc_release"] + + - name: Run on dispatch + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo "This step runs only for binary generation." + + BRANCH=${{ github.event.inputs.branch }} + PYTHON=${{ github.event.inputs.python_version }} + + # Validate branch format (e.g., v25.06) + if [ [ ! "$BRANCH" =~ ^v[0-9]{2}\.[0-9]{2}$ ] && [ "$BRANCH" != "master" ] ]; then + echo "Error: Invalid branch name format: $BRANCH." + echo "Branch name should be either master or any release branch (e.g., v25.06)" + exit 1 + fi + echo "Branch name $BRANCH is valid." + + # Validate Python version format (e.g., 3.12) + if [[ ! "$PYTHON" =~ ^[0-9]{1}\.[0-9]{2}$ ]]; then + if [[ ! "$PYTHON" =~ ^3\.(9|1[0-8])$ ]]; then + echo "Error: Invalid Python version format: $PYTHON" + exit 1 + fi + fi + echo "Python version $PYTHON is valid." + + # Save all information in environment variables + echo "SOFA_BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENV + echo "SOFA_COMMIT_SHA=HEAD" >> $GITHUB_ENV + echo "PRESET=${{ github.event.inputs.preset }}" >> $GITHUB_ENV + echo "PYTHON_VERSION=${{ github.event.inputs.python_version }}" >> $GITHUB_ENV + echo "GENERATE_BINARIES=true" >> $GITHUB_ENV + echo 'BUILDER_OS=${{ github.event.inputs.builder-os }}' >> $GITHUB_ENV + echo "FORCE_FULL_BUILD=true" >> $GITHUB_ENV + echo "EXTERNAL_PLUGINS=${{ inputs.external-plugins }}" >> $GITHUB_ENV + echo "ADDITIONNAL_CMAKE_FLAGS=${{ inputs.additionnal-cmake-flags }}" >> $GITHUB_ENV + + + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install pip packages + run: | + pip install python-graphql-client + pip install requests + + - name: Check out code + uses: actions/checkout@v5 + with: + repository: bakpaul/ci + ref: jenkins_gha_migration + path: ./ci + + - name: Check push on master case (e.g. merge) + if: ${{ github.event_name == 'push'}} + run: | + echo "This step runs only for push on the master branch." + echo "SOFA_COMMIT_SHA=${{ github.sha }}">> $GITHUB_ENV + echo "WITH_ALL_TESTS=true" >> $GITHUB_ENV + echo "FORCE_FULL_BUILD=true" >> $GITHUB_ENV + echo 'BUILDER_OS=["sh-ubuntu_gcc_release","sh-ubuntu_clang_release","sh-ubuntu_clang_debug","sh-fedora_clang_release","sh-macos_clang_release"]' >> $GITHUB_ENV + + - name: Run when PR is opened or a commit is pushed in a PR + if: ${{ github.event_name == 'pull_request' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + OWNER_NAME: ${{ github.event.pull_request.head.repo.owner.login }} + PR_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} + run: | + echo "This step runs only when a PR is opened or synchronized." + + # Trigger the Build action + # Warning ! This script also changes ENV variables + python ci/scripts/checkPRInfoBeforeBuild.py + + - name: Export environment variables as outputs + id: export-vars + run: | + # Validate branch format (e.g., v25.06) + if [ [ ! "$SOFA_BRANCH_NAME" =~ ^v[0-9]{2}\.[0-9]{2}$ ] && [ "$SOFA_BRANCH_NAME" != "master" ] ]; then + echo "Error: Invalid branch name format: $SOFA_BRANCH_NAME." + echo "Branch name should be either master or any release branch (e.g., v25.06)" + exit 1 + fi + + echo "SOFA_BRANCH_NAME=${SOFA_BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "SOFA_COMMIT_SHA=${SOFA_COMMIT_SHA}" >> $GITHUB_OUTPUT + echo "PRESET=${PRESET}" >> $GITHUB_OUTPUT + echo "PYTHON_VERSION=${PYTHON_VERSION}" >> $GITHUB_OUTPUT + echo "CI_DEPENDS_ON=${CI_DEPENDS_ON}" >> $GITHUB_OUTPUT + echo "WITH_ALL_TESTS=${WITH_ALL_TESTS}" >> $GITHUB_OUTPUT + echo "FORCE_FULL_BUILD=${FORCE_FULL_BUILD}" >> $GITHUB_OUTPUT + echo "EXTERNAL_PLUGINS=${EXTERNAL_PLUGINS}" >> $GITHUB_OUTPUT + echo "ADDITIONNAL_CMAKE_FLAGS=${ADDITIONNAL_CMAKE_FLAGS}" >> $GITHUB_OUTPUT + echo "GENERATE_BINARIES=${GENERATE_BINARIES}" >> $GITHUB_OUTPUT + echo "PR_OWNER_URL=${PR_OWNER_URL}" >> $GITHUB_OUTPUT + echo "PR_BRANCH_NAME=${PR_BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "PR_COMMIT_SHA=${PR_COMMIT_SHA}" >> $GITHUB_OUTPUT + echo "BUILDER_OS=${BUILDER_OS}" >> $GITHUB_OUTPUT + + + # =============================================================== + # =============================================================== + + # Trigger the build and sharing all parameters from filter_build > outputs + build-on: + needs: filter_build + uses: bakpaul/sofa/.github/workflows/build-and-test.yml@master + with: + sofa-branch-name: ${{ needs.filter_build.outputs.SOFA_BRANCH_NAME }} + sofa-commit-sha: ${{ needs.filter_build.outputs.SOFA_COMMIT_SHA }} + preset: ${{ needs.filter_build.outputs.PRESET }} + python-version: ${{ needs.filter_build.outputs.PYTHON_VERSION }} + ci-depends-on: ${{ needs.filter_build.outputs.CI_DEPENDS_ON }} + with-all-tests: ${{ needs.filter_build.outputs.WITH_ALL_TESTS == 'true'}} + force-full-build: ${{ needs.filter_build.outputs.FORCE_FULL_BUILD == 'true'}} + external-plugins: ${{ needs.filter_build.outputs.EXTERNAL_PLUGINS }} + additionnal-cmake-flags: ${{ needs.filter_build.outputs.ADDITIONNAL_CMAKE_FLAGS }} + generate-binaries: ${{ needs.filter_build.outputs.GENERATE_BINARIES == 'true'}} + pr-owner-url: ${{ needs.filter_build.outputs.PR_OWNER_URL }} + pr-branch-name: ${{ needs.filter_build.outputs.PR_BRANCH_NAME }} + pr-commit-sha: ${{ needs.filter_build.outputs.PR_COMMIT_SHA }} + builder-os: ${{ needs.filter_build.outputs.BUILDER_OS }} diff --git a/CMakeLists.txt b/CMakeLists.txt index e5e1ecb2401..9e28dd1776d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON) # https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_PACKAGE_TARGETS_GLOBAL.html endif() - + # Manually define VERSION set(Sofa_VERSION_MAJOR 25) set(Sofa_VERSION_MINOR 12) diff --git a/Sofa/framework/Helper/src/sofa/helper/system/FileSystem.cpp b/Sofa/framework/Helper/src/sofa/helper/system/FileSystem.cpp index e41aec5b5a1..dfbb2cd5ecd 100644 --- a/Sofa/framework/Helper/src/sofa/helper/system/FileSystem.cpp +++ b/Sofa/framework/Helper/src/sofa/helper/system/FileSystem.cpp @@ -146,10 +146,26 @@ bool FileSystem::createDirectory(const std::string& path) return true; } #else - if (mkdir(path.c_str(), 0755)) + int status = mkdir(path.c_str(), 0755); + if(status) { - msg_error(error) << path << ": " << strerror(errno); - return true; + if (errno != EEXIST) + { + msg_error(error) << path << ": " << strerror(errno); + return true; + } + else + { + struct stat st_buf; + if (stat(path.c_str(), &st_buf) == 0) + { + if ((st_buf.st_mode & S_IFMT) != S_IFDIR) { + msg_error(error) << path << ": File exists and is not a directoy"; + return true; + } + } + + } } #endif else diff --git a/Sofa/framework/Helper/test/system/FileSystem_test.cpp b/Sofa/framework/Helper/test/system/FileSystem_test.cpp index 63881207d00..1eadead4e92 100644 --- a/Sofa/framework/Helper/test/system/FileSystem_test.cpp +++ b/Sofa/framework/Helper/test/system/FileSystem_test.cpp @@ -143,18 +143,16 @@ TEST(FileSystemTest, createDirectory_alreadyExists) // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; - { - EXPECT_MSG_NOEMIT(Error) ; - FileSystem::createDirectory("createDirectoryTestDir"); - } - { - EXPECT_MSG_EMIT(Error) ; - EXPECT_TRUE(FileSystem::createDirectory("createDirectoryTestDir")); - } - { - EXPECT_MSG_NOEMIT(Error) ; - FileSystem::removeDirectory("createDirectoryTestDir"); - } + EXPECT_MSG_NOEMIT(Error) ; + + EXPECT_FALSE(FileSystem::createDirectory("createDirectoryTestDir")); + EXPECT_TRUE(FileSystem::exists("createDirectoryTestDir")); + EXPECT_TRUE(FileSystem::isDirectory("createDirectoryTestDir")); + EXPECT_TRUE(FileSystem::createDirectory("createDirectoryTestDir")); + + // Cleanup + FileSystem::removeDirectory("createDirectoryTestDir"); + } TEST(FileSystemTest, removeDirectory)