diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a81d5a15dcb..9a3bf0c0ec8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -222,20 +222,26 @@ jobs: if: ${{ matrix.build-type == 'Debug' }} timeout-minutes: 2 run: | - - trap 'echo "MULTIPASS_TESTS_EXIT_CODE=$?" >> $GITHUB_ENV' EXIT - instance_name=`/snap/bin/lxc --project snapcraft --format=csv --columns=n list | grep multipass` + instance_name=$(/snap/bin/lxc --project snapcraft --format=csv --columns=n list | grep multipass) + trap ' + EXIT_STATUS=$? + echo "MULTIPASS_TESTS_EXIT_CODE=$EXIT_STATUS" >> $GITHUB_ENV + # Check if any files were generated in /cores/ + if /snap/bin/lxc --project snapcraft exec "$instance_name" -- bash -c "[ \"\$(ls -A /coredump 2>/dev/null)\" ]"; then + echo "CORE_DUMPS_FOUND=true" >> $GITHUB_ENV + fi + ' EXIT /snap/bin/lxc --project snapcraft start $instance_name # Let's print the core pattern so we can check if it's successfully propagated to the container. /snap/bin/lxc --project snapcraft exec $instance_name -- bash -c 'cat /proc/sys/kernel/core_pattern' - # Create the directory for the coredumps - /snap/bin/lxc --project snapcraft exec $instance_name -- bash -c 'mkdir -p /coredump' + # Create the directory for the coredumps and ensure the directory is empty + /snap/bin/lxc --project snapcraft exec $instance_name -- bash -c 'mkdir -p /coredump && rm -f /coredump/*' # Enable coredumps by setting the core dump size to "unlimited", and run the tests. /snap/bin/lxc --project snapcraft exec $instance_name -- bash -c "\ + cd /root/parts/multipass/build && \ ulimit -c unlimited && \ - env CTEST_OUTPUT_ON_FAILURE=1 \ LD_LIBRARY_PATH=/root/stage/usr/lib/x86_64-linux-gnu/:/root/stage/lib/:/root/parts/multipass/build/lib/ \ - /root/parts/multipass/build/bin/multipass_tests" + ctest -V" - name: Measure coverage id: measure-coverage @@ -279,7 +285,7 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Pull coredump and executable from LXC container - if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0'}} + if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' && env.CORE_DUMPS_FOUND }} # do not cause job to fail if there are no coredumps available. continue-on-error: true run: | @@ -294,16 +300,26 @@ jobs: instance_name=`/snap/bin/lxc --project snapcraft --format=csv --columns=n list | grep multipass` # Pull the crashed executable from the container /snap/bin/lxc --project snapcraft file pull \ - -p -r "$instance_name/root/parts/multipass/build/bin/multipass_tests" /tmp/coredump/multipass_tests + -p -r "$instance_name/root/parts/multipass/build/bin/multipass_cpp_tests" /tmp/coredump/multipass_cpp_tests echo "Pulled the executable." # Pull the coredump folder /snap/bin/lxc --project snapcraft file pull -p -r "$instance_name/coredump" /tmp/coredump echo "Pulled the coredumps folder." + + mapfile -t BINS < <(/snap/bin/lxc --project snapcraft exec "$instance_name" -- bash -c " + cd /root/parts/multipass/src/rxx + cargo test --workspace --no-run --message-format=json --target-dir /root/parts/multipass/build/rxx | \ + jq -r 'select(.reason == \"compiler-artifact\" and .executable != null) | .executable' + ") + for bin in "${BINS[@]}"; do + /snap/bin/lxc --project snapcraft file pull -p -r "$instance_name/$bin" "/tmp/coredump/$(basename "$bin")" + done + echo "Pulled the rust executables" set +o xtrace - name: Upload test coredump uses: actions/upload-artifact@v7 - if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' }} + if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' && env.CORE_DUMPS_FOUND }} with: name: buildandtest-test-crash-${{ runner.os }}-${{ matrix.build-type }} path: /tmp/coredump/** diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 2122b180dec..120914864c2 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -75,6 +75,7 @@ jobs: env: BUILD_DIR: ${{ github.workspace }}/build SCCACHE_DIR: ${{ github.workspace }}/.sccache + RUST_DIR: ${{ github.workspace }}/rxx RUSTC_WRAPPER: "sccache" steps: @@ -110,6 +111,9 @@ jobs: mkdir -p ~/.pip echo -e "[install]\nbreak-system-packages = true" > ~/.pip/pip.conf + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + - name: Install dependencies from brew run: | # Avoid reinstalling cmake @@ -229,16 +233,42 @@ jobs: - name: Build run: cmake --build ${{ env.BUILD_DIR }} + - name: Collect rust binary paths + working-directory: ${{ env.RUST_DIR }} + run: | + { + echo 'RUST_EXEC_PATHS<<__EOF__' + cargo test --workspace --no-run --message-format=json --target-dir ${{ env.BUILD_DIR }}/rxx \ + | jq -r 'select(.reason == "compiler-artifact" and .executable != null) | .executable' \ + | while IFS= read -r exe; do + [ -n "$exe" ] && echo "$exe" + done + echo '__EOF__' + } >> "$GITHUB_ENV" + - name: Enable core dump generation working-directory: ${{ env.BUILD_DIR }} shell: bash run : | # Allow core dumps sudo sysctl -w kern.coredump=1 + # Force core dump location/pattern to match the upload step expectations + sudo sysctl -w kern.corefile=/cores/core.%P # This entitlement is needed for a proc to produce a coredump /usr/libexec/PlistBuddy -c "Add :com.apple.security.get-task-allow bool true" segv.entitlements # Code-sign the test executable to grant core dump entitlement - codesign -s - -f --entitlements segv.entitlements bin/multipass_tests + codesign -s - -f --entitlements segv.entitlements bin/multipass_cpp_tests + + RUST_EXECUTABLES=() + while IFS= read -r line; do + [[ -n "$line" ]] || continue + RUST_EXECUTABLES+=("$line") + done <<< "$RUST_EXEC_PATHS" + for RUST_EXEC in "${RUST_EXECUTABLES[@]}"; + do + codesign -s - -f --entitlements segv.entitlements "$RUST_EXEC" + done + # Ensure destination exists and is writable sudo mkdir -p /cores || true sudo chmod 1777 /cores @@ -250,21 +280,31 @@ jobs: - name: Test working-directory: ${{ env.BUILD_DIR }} run: | - # Catch and save the test executable's exit code - trap 'echo "MULTIPASS_TESTS_EXIT_CODE=$?" >> $GITHUB_ENV' EXIT + # Define the trap to catch the exit code AND check for core dumps + trap ' + EXIT_STATUS=$? + echo "MULTIPASS_TESTS_EXIT_CODE=$EXIT_STATUS" >> $GITHUB_ENV + # Check if any files were generated in /cores/ + if [ "$(ls -A /cores/ 2>/dev/null)" ]; then + echo "CORE_DUMPS_FOUND=true" >> $GITHUB_ENV + fi + ' EXIT + # Clean existing core dumps for coredump detection + sudo rm -f /cores/* # Set soft limit for the core file size (512MiB) ulimit -c 1048576 - bin/multipass_tests + ctest -V - name: Upload test coredump uses: actions/upload-artifact@v7 - if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' }} + if: ${{ failure() && env.MULTIPASS_TESTS_EXIT_CODE != '0' && env.CORE_DUMPS_FOUND }} with: name: buildandtest-test-crash-${{ matrix.runs-on }}-${{ matrix.build-type }} retention-days: 7 path: | /cores/** - ${{ env.BUILD_DIR }}/bin/multipass_tests + ${{ env.BUILD_DIR }}/bin/multipass_cpp_tests + ${{ env.RUST_EXEC_PATHS }} - name: Package id: cmake-package diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 77287fc10a7..8bf6df8f0f2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -86,6 +86,9 @@ jobs: id: build-params uses: ./.github/actions/build-params + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + - name: Install specific QEMU from Choco uses: crazy-max/ghaction-chocolatey@v4 with: @@ -176,7 +179,7 @@ jobs: - name: Test working-directory: ${{ env.BUILD_DIR }} run: | - bin/multipass_tests + ctest -V - name: Package id: cmake-package diff --git a/BUILD.linux.md b/BUILD.linux.md index 7206e89632b..f0053b8b012 100644 --- a/BUILD.linux.md +++ b/BUILD.linux.md @@ -10,6 +10,13 @@ sudo apt install devscripts equivs mk-build-deps -s sudo -i ``` +### Install the Rust compiler + +Go [here](https://rust-lang.org/tools/install/) for the most recent method. The current method is: +``` +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable +``` + ## Building First, go into the repository root and get all the submodules: diff --git a/BUILD.macOS.md b/BUILD.macOS.md index 884ee5dc984..55b85502093 100644 --- a/BUILD.macOS.md +++ b/BUILD.macOS.md @@ -40,6 +40,13 @@ means to obtain cmake is with Homebrew . brew install cmake +### Rust compiler + +To build multipass you will need the rust compiler. It can be installed using brew: +``` +brew install rustup +``` + Building --------------------------------------- diff --git a/BUILD.windows.md b/BUILD.windows.md index 6221d22142f..06e45e1a459 100644 --- a/BUILD.windows.md +++ b/BUILD.windows.md @@ -15,7 +15,7 @@ After chocolatey is installed you can now install the rest of the dependencies f Powershell(Admin). To get the best results, in the following order: ```[pwsh] -choco install cmake ninja qemu-img git wget unzip -yfd +choco install cmake ninja qemu-img git wget unzip rustup.install -yfd ``` ```[pwsh] @@ -140,7 +140,7 @@ Finally, to build the project, run: cmake --build . --parallel ``` -This builds `multipass`, `multipassd`, and `multipass_tests`. +This builds `multipass`, `multipassd`, and `multipass_cpp_tests`. To create an installer, run `cmake --build . --target package`. ## Running `multipass` diff --git a/CMakeLists.txt b/CMakeLists.txt index a3bf438293d..bb60c2a4ca1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,25 +174,25 @@ if(MULTIPASS_ENABLE_FLUTTER_GUI) set(DART_PROTOC_PLUGIN ${CMAKE_SOURCE_DIR}/3rd-party/protobuf.dart/protoc_plugin/bin/protoc_plugin.exe) endif(MULTIPASS_ENABLE_FLUTTER_GUI) -string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_lower) +string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") add_definitions(-DMULTIPASS_COMPILER_CLANG) - if(cmake_build_type_lower MATCHES "asan") + if(CMAKE_BUILD_TYPE_LOWER MATCHES "asan") add_compile_options(-fno-omit-frame-pointer -fsanitize=address) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address") - elseif(cmake_build_type_lower MATCHES "ubsan") + elseif(CMAKE_BUILD_TYPE_LOWER MATCHES "ubsan") add_compile_options(-fno-omit-frame-pointer -fsanitize=undefined) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined") - elseif(cmake_build_type_lower MATCHES "tsan") + elseif(CMAKE_BUILD_TYPE_LOWER MATCHES "tsan") add_compile_options(-fno-omit-frame-pointer -fsanitize=thread) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=thread") endif() endif() # these we want to apply even to 3rd-party -if(cmake_build_type_lower MATCHES "coverage") +if(CMAKE_BUILD_TYPE_LOWER MATCHES "coverage") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(--coverage) if(COMMAND add_link_options) @@ -315,7 +315,7 @@ else() endif() endif() -if(cmake_build_type_lower MATCHES "coverage") +if(CMAKE_BUILD_TYPE_LOWER MATCHES "coverage") find_program(GCOV gcov) find_program(LCOV lcov) find_program(GENHTML genhtml) @@ -337,7 +337,7 @@ if(cmake_build_type_lower MATCHES "coverage") endif() add_custom_target(covreport - DEPENDS multipass_tests + DEPENDS multipass_cpp_tests rust_coverage_tarpaulin WORKING_DIRECTORY ${CMAKE_BUILD_DIR} COMMAND ${LCOV} --directory . --zerocounters COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target test @@ -351,7 +351,9 @@ if(cmake_build_type_lower MATCHES "coverage") ${CMAKE_BINARY_DIR}'/*' --output-file coverage.cleaned COMMAND ${CMAKE_COMMAND} -E remove coverage.info - COMMAND ${GENHTML} -o coverage coverage.cleaned + COMMAND ${CMAKE_COMMAND} -E remove coverage.info + COMMAND ${LCOV} -a coverage.cleaned -a lcov.info -o total_coverage.info + COMMAND ${GENHTML} -o coverage total_coverage.info ) endif() endif() @@ -371,6 +373,9 @@ if(MULTIPASS_ENABLE_TESTS) add_subdirectory(tests/unit) endif() +#Added later to be compatible with enable_testing() above +add_subdirectory(rxx) + include(packaging/cpack.cmake OPTIONAL) # Disable .clang-tidy in build folder diff --git a/include/multipass/name_generator.h b/include/multipass/name_generator.h index 7f607896e4c..48d8adf5232 100644 --- a/include/multipass/name_generator.h +++ b/include/multipass/name_generator.h @@ -37,5 +37,37 @@ class NameGenerator : private DisabledCopyMove NameGenerator() = default; }; -NameGenerator::UPtr make_default_name_generator(); +namespace petname +{ +enum NumWords +{ + One, + Two, + Three, +}; +template +concept IsValidSeparator = (S == '-' || S == '_'); + +namespace detail +{ +NameGenerator::UPtr make_petname_provider_impl(NumWords num_words, char separator); +} // namespace detail + +// Templated functions to avoid exposing the PetnameProvider type in the interface header with +// the default arguments. The templates ensure the argument checks are done at compile-time. +template + requires IsValidSeparator +NameGenerator::UPtr make_petname_provider() +{ + return detail::make_petname_provider_impl(NW, S); +}; + +template + requires IsValidSeparator +NameGenerator::UPtr make_petname_provider() +{ + return detail::make_petname_provider_impl(NumWords::Two, S); +} +} // namespace petname + } // namespace multipass diff --git a/packaging/cpack.cmake b/packaging/cpack.cmake index f34372ff8c5..d687438f9d7 100644 --- a/packaging/cpack.cmake +++ b/packaging/cpack.cmake @@ -74,7 +74,7 @@ if (MSVC) # and creats an install(PROGRAMS ...) rule using the destination and component IDs setup below. set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION bin) set(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT multipassd) - if(cmake_build_type_lower STREQUAL "debug") + if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") set(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) endif() diff --git a/packaging/gui-less/snap/snapcraft.yaml b/packaging/gui-less/snap/snapcraft.yaml index 0893497e88c..79f018205e2 100644 --- a/packaging/gui-less/snap/snapcraft.yaml +++ b/packaging/gui-less/snap/snapcraft.yaml @@ -195,6 +195,11 @@ parts: override-pull: | set -e + if ! snap list rustup >/dev/null 2>&1; then + snap install rustup --classic + rustup default stable + fi + # https://github.com/microsoft/vcpkg/issues/37279 # vcpkg-systemd: required for meson.build python3 -m pip install --break-system-packages jinja2 diff --git a/rxx/CMakeLists.txt b/rxx/CMakeLists.txt new file mode 100644 index 00000000000..77e0c0db446 --- /dev/null +++ b/rxx/CMakeLists.txt @@ -0,0 +1,150 @@ +# Copyright (C) Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#Set build variables +if (CMAKE_BUILD_TYPE_LOWER MATCHES "release") + set(RUST_BUILD "release") + set(RUST_BUILD_FLAG "--release") +else() + set(RUST_BUILD "debug") + set(RUST_BUILD_FLAG "") +endif() +if (WIN32) + set(LIB_PREFIX "") + set(LIB_SUFFIX "lib") +else() + set(LIB_PREFIX "lib") + set(LIB_SUFFIX "a") +endif() + +# Build output path +set(RUST_BUILD_DIR "${CMAKE_BINARY_DIR}/rxx") +set(RUST_BIN_DIR "${RUST_BUILD_DIR}/${RUST_BUILD}") + +#Find cargo, which will be a hard dependency +find_program(CARGO_EXECUTABLE cargo) +#Set the build directory via env variable +set(CARGO_CMD ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${RUST_BUILD_DIR}" ${CARGO_EXECUTABLE}) +#Crates to build. Both consumers and providers of code to the C++ side +#have to be built, since the CXX glue code has to be generated and compiled +#for said usage +set(CRATES + namegen +) + +if(NOT CRATES STREQUAL "") + # cxx.cc is compiled and linked into the static libraries by cargo automatically + # There is no documentation about this but you can check the symbols by doing + # nm -g mylib.a | grep string + # This means that no need to handle cxx.cc + add_library(cxx INTERFACE) + target_include_directories(cxx INTERFACE "${RUST_BUILD_DIR}/cxxbridge") +endif() + +set(ACTIVE_FEATURES "default") +set(CARGO_GENERATED_FILES "") +foreach(CRATE_NAME ${CRATES}) + set(CRATE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${CRATE_NAME}") + #Check that it is a crate (since target is not a crate) + if (IS_DIRECTORY "${CRATE_PATH}" AND EXISTS "${CRATE_PATH}/Cargo.toml") + message(STATUS "Configuring crate: ${CRATE_PATH}") + #Static library path for the rust source. + set(RUST_OUT "${RUST_BIN_DIR}/${LIB_PREFIX}${CRATE_NAME}.${LIB_SUFFIX}") + #Target to depend on cargo build. + add_library(${CRATE_NAME}_rs STATIC IMPORTED) + set_target_properties(${CRATE_NAME}_rs PROPERTIES IMPORTED_LOCATION ${RUST_OUT}) + set(${CRATE_NAME}_LINK_LIBS "") + if (WIN32) + list(APPEND ${CRATE_NAME}_LINK_LIBS "$") + endif() + set(BRIDGE_SOURCE "${RUST_BUILD_DIR}/cxxbridge/${CRATE_NAME}/src") + set(BRIDGE_CC "${BRIDGE_SOURCE}/lib.rs.cc") + + list(APPEND CARGO_GENERATED_FILES "${RUST_OUT}" "${BRIDGE_CC}") + set_source_files_properties(${BRIDGE_CC} PROPERTIES GENERATED TRUE) + set_source_files_properties(${RUST_OUT} PROPERTIES GENERATED TRUE) + + #Library creation to compile the CXX glue source + add_library(${CRATE_NAME} STATIC ${BRIDGE_CC}) + #Here we can place the crate-wise dependencies + add_subdirectory(${CRATE_NAME}) + #The CXX glue staticlib must link the rust staticlib + target_link_libraries(${CRATE_NAME} PUBLIC ${CRATE_NAME}_rs cxx) + target_link_libraries(${CRATE_NAME}_rs INTERFACE ${${CRATE_NAME}_LINK_LIBS}) + endif() +endforeach() + +list(JOIN ACTIVE_FEATURES "," FEATURES_CSV) +add_custom_command( + OUTPUT ${CARGO_GENERATED_FILES} + COMMAND ${CARGO_CMD} build + --manifest-path ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml + --workspace + ${RUST_BUILD_FLAG} + --lib + --features "${FEATURES_CSV}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Building Rust crates and generating C++ glue..." +) + +add_custom_target(cargo_build DEPENDS ${CARGO_GENERATED_FILES}) + +foreach(CRATE_NAME ${CRATES}) + if(TARGET ${CRATE_NAME}) + add_dependencies(${CRATE_NAME} cargo_build) + add_dependencies(${CRATE_NAME}_rs cargo_build) + endif() +endforeach() +#The macros crate cannot contain tests +set(TESTING_EXCLUDE_CRATES_ARG --exclude macros) +add_test( + NAME multipass_rust_tests + COMMAND ${CARGO_CMD} test + --manifest-path ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml + --no-fail-fast + --workspace + --lib + --tests + --features "${FEATURES_CSV}" + ${TESTING_EXCLUDE_CRATES_ARG} +) +set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${RUST_BIN_DIR}") + +if(CMAKE_BUILD_TYPE_LOWER MATCHES "coverage") + execute_process( + COMMAND ${CARGO_CMD} tarpaulin --version + RESULT_VARIABLE TARPAULIN_INSTALLED + OUTPUT_VARIABLE TARPAULIN_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if (NOT TARPAULIN_INSTALLED EQUAL 0) + execute_process( + COMMAND ${CARGO_CMD} install cargo-tarpaulin + ) + endif() + #Engine is for non-x86_64 processors. Can only use if return codes from test are always 0 + no fork or similar syscalls + add_custom_target(rust_coverage_tarpaulin + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CARGO_CMD} tarpaulin + --out Lcov + --output-dir ${CMAKE_BINARY_DIR} + --exclude-files '../*' + --engine llvm + --workspace + ${TESTING_EXCLUDE_CRATES_ARG} + COMMENT "Running Rust coverage with Tarpaulin" + ) +endif() diff --git a/rxx/Cargo.lock b/rxx/Cargo.lock new file mode 100644 index 00000000000..1ed313d36ce --- /dev/null +++ b/rxx/Cargo.lock @@ -0,0 +1,412 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "cc" +version = "1.2.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "cxx" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbda285ba6e5866529faf76352bdf73801d9b44a6308d7cd58ca2379f378e994" +dependencies = [ + "cc", + "cxx-build", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9efde466c5d532d57efd92f861da3bdb7f61e369128ce8b4c3fe0c9de4fa4d" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3efb93799095bccd4f763ca07997dc39a69e5e61ab52d2c407d4988d21ce144d" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3092010228026e143b32a4463ed9fa8f86dca266af4bf5f3b2a26e113dbe4e45" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d72ebfcd351ae404fb00ff378dfc9571827a00722c9e735c9181aec320ba0a" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "namegen" +version = "0.1.0" +dependencies = [ + "cxx", + "cxx-build", + "macros", + "rand", + "static_assertions", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/rxx/Cargo.toml b/rxx/Cargo.toml new file mode 100644 index 00000000000..57087e5103a --- /dev/null +++ b/rxx/Cargo.toml @@ -0,0 +1,45 @@ +# Copyright (C) Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +[workspace] +members = [ + "macros", + "namegen", +] +resolver = "2" + +[workspace.package] +version = "0.1.0" +edition = "2024" +rust-version = "1.91" + +[workspace.dependencies] +cxx = "^1.0.0" +cxx-build = "^1.0.0" +static_assertions = "^1.1.0" +syn = "^2.0.0" +quote = "^1.0.0" +proc-macro2 = "^1.0.0" +rand = "^0.9.0" +macros = { path = "macros" } +namegen = { path = "namegen" } + +# This is debug +[profile.dev] +codegen-units = 1 #Less threads to compete with C++ compilation +incremental = false #Removing incremental compilation allows lower codegen count + +[profile.release] +lto = true #Removes unused code sections +codegen-units = 1 #Less threads to compete with C++ compilation diff --git a/rxx/macros/Cargo.toml b/rxx/macros/Cargo.toml new file mode 100644 index 00000000000..fddf3cf1734 --- /dev/null +++ b/rxx/macros/Cargo.toml @@ -0,0 +1,27 @@ +# Copyright (C) Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +[package] +name = "macros" +version.workspace = true +edition.workspace = true +rust-version.workspace = true + +[lib] +proc-macro = true + +[dependencies] +quote.workspace = true +syn.workspace = true +proc-macro2.workspace = true diff --git a/rxx/macros/src/lib.rs b/rxx/macros/src/lib.rs new file mode 100644 index 00000000000..d9a8c5bd44b --- /dev/null +++ b/rxx/macros/src/lib.rs @@ -0,0 +1,40 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use syn::{LitStr, parse_macro_input}; + +#[proc_macro] +pub fn make_word_array(input: TokenStream) -> TokenStream { + // Parse filename as string literal + let filename = parse_macro_input!(input as LitStr).value(); + + // Read file contents + let content = std::fs::read_to_string(&filename).expect("Could not read file"); + + // Split & reformat as string literals for quote! + let words: Vec = content + .split_whitespace() + .map(|word| syn::LitStr::new(word, Span::call_site())) + .collect(); + + let output = quote! { + [ #( #words ),* ] + }; + output.into() +} diff --git a/rxx/namegen/CMakeLists.txt b/rxx/namegen/CMakeLists.txt new file mode 100644 index 00000000000..e1c15849fa7 --- /dev/null +++ b/rxx/namegen/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (C) Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Here the rust build depends on the C++ side of the rust_logger bridge because +# the rust_logger crate is compiled within this crate's static library. +# Because we did not build it, we specify the dependency like: +#list(APPEND ${crate_name}_link_libs "$") +#Add necessary sources for wrappers (it is advised that the source is named after +# the header in include/multipass/) +#target_sources(${crate_name} PRIVATE "src/name_generator_rs.cpp") +#Link necessary libraries for the exceptions header +#target_link_libraries(${crate_name} PRIVATE fmt::fmt-header-only Qt::Core) + +target_sources(namegen PRIVATE + "src/name_generator.cpp" + "src/petname_generator.cpp" + ) diff --git a/rxx/namegen/Cargo.toml b/rxx/namegen/Cargo.toml new file mode 100644 index 00000000000..6d45ebae236 --- /dev/null +++ b/rxx/namegen/Cargo.toml @@ -0,0 +1,34 @@ +# Copyright (C) Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +[package] +name = "namegen" +version.workspace = true +edition.workspace = true +rust-version.workspace = true + +[lib] +crate-type = ["staticlib","rlib"] + +[features] +default = [] + +[dependencies] +cxx.workspace = true +macros.workspace = true +static_assertions.workspace = true +rand.workspace = true + +[build-dependencies] +cxx-build.workspace = true diff --git a/src/petname/make_name_generator.cpp b/rxx/namegen/build.rs similarity index 68% rename from src/petname/make_name_generator.cpp rename to rxx/namegen/build.rs index 9981db98692..9e0989f4783 100644 --- a/src/petname/make_name_generator.cpp +++ b/rxx/namegen/build.rs @@ -13,16 +13,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * Authored by: Alberto Aguirre - * */ -#include "petname.h" -#include - -namespace mp = multipass; - -mp::NameGenerator::UPtr mp::make_default_name_generator() -{ - return std::make_unique(mp::Petname::NumWords::TWO, "-"); +fn main() { + // Generate the .h and .cc files (_do not_ compile them) + let _ = cxx_build::bridge("src/lib.rs"); // drops the builder, just codegen + println!("cargo:rerun-if-changed=src/lib.rs"); } diff --git a/src/petname/adjectives.txt b/rxx/namegen/src/adjectives.txt similarity index 100% rename from src/petname/adjectives.txt rename to rxx/namegen/src/adjectives.txt diff --git a/src/petname/adverbs.txt b/rxx/namegen/src/adverbs.txt similarity index 100% rename from src/petname/adverbs.txt rename to rxx/namegen/src/adverbs.txt diff --git a/rxx/namegen/src/lib.rs b/rxx/namegen/src/lib.rs new file mode 100644 index 00000000000..b772c9128d0 --- /dev/null +++ b/rxx/namegen/src/lib.rs @@ -0,0 +1,44 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +pub mod petname_error; +pub mod petname_generator; +use petname_generator::make_petname; + +#[cxx::bridge(namespace = "multipass::petname")] +pub mod ffi { + //Both the stated here and in extern C++ tells the CXX bridge generator + //to use the C++-side definition of NumWords + #[repr(i32)] + pub enum NumWords { + One, + Two, + Three, + } + + #[namespace = "rxx::petname"] + extern "Rust" { + //Result allows CXX to turn returned Rust Errors into cxx::RustError + //on the C++ side. + fn make_petname(num_words: NumWords, separator: c_char) -> Result; + } + #[namespace = "multipass::petname"] + extern "C++" { + include!("multipass/name_generator.h"); + type NumWords; + } +} diff --git a/rxx/namegen/src/name_generator.cpp b/rxx/namegen/src/name_generator.cpp new file mode 100644 index 00000000000..eb1687915c0 --- /dev/null +++ b/rxx/namegen/src/name_generator.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "petname_generator.h" + +#include + +#include + +namespace mp = multipass; +namespace mpp = multipass::petname; + +mp::NameGenerator::UPtr mpp::detail::make_petname_provider_impl(mpp::NumWords num_words, + char separator) +{ + return std::make_unique(num_words, separator); +} diff --git a/src/petname/names.txt b/rxx/namegen/src/names.txt similarity index 100% rename from src/petname/names.txt rename to rxx/namegen/src/names.txt diff --git a/rxx/namegen/src/petname_error.rs b/rxx/namegen/src/petname_error.rs new file mode 100644 index 00000000000..864fa251ab3 --- /dev/null +++ b/rxx/namegen/src/petname_error.rs @@ -0,0 +1,35 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +use std::fmt; + +#[derive(Debug, PartialEq)] +pub enum PetnameError { + InvalidWordNumber(i32), + InvalidSeparator(i8), +} + +impl fmt::Display for PetnameError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PetnameError::InvalidWordNumber(num) => write!(f, "Invalid word number: {}", num), + PetnameError::InvalidSeparator(sep) => { + write!(f, "Invalid separator, ASCII code: {}", sep) + } + } + } +} diff --git a/rxx/namegen/src/petname_generator.cpp b/rxx/namegen/src/petname_generator.cpp new file mode 100644 index 00000000000..66948afc262 --- /dev/null +++ b/rxx/namegen/src/petname_generator.cpp @@ -0,0 +1,16 @@ + + +#include "petname_generator.h" +#include + +namespace mp = multipass; + +mp::PetnameGenerator::PetnameGenerator(mp::petname::NumWords num_words, char separator) + : num_words{num_words}, separator{separator} +{ +} + +std::string mp::PetnameGenerator::make_name() +{ + return std::string(rxx::petname::make_petname(num_words, separator)); +} diff --git a/rxx/namegen/src/petname_generator.h b/rxx/namegen/src/petname_generator.h new file mode 100644 index 00000000000..9dc556f452d --- /dev/null +++ b/rxx/namegen/src/petname_generator.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include + +#include + +namespace multipass +{ + +class PetnameGenerator final : public NameGenerator +{ +public: + PetnameGenerator(petname::NumWords num_words, char separator); + + std::string make_name() override; + +private: + petname::NumWords num_words; + char separator; +}; +} // namespace multipass diff --git a/rxx/namegen/src/petname_generator.rs b/rxx/namegen/src/petname_generator.rs new file mode 100644 index 00000000000..d9238eea6d8 --- /dev/null +++ b/rxx/namegen/src/petname_generator.rs @@ -0,0 +1,63 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +use crate::ffi::NumWords; +use crate::petname_error::PetnameError; + +use macros::make_word_array; +use rand::{prelude::IndexedRandom, rngs::ThreadRng}; +use static_assertions as sa; + +use std::os::raw::c_char; +//Paths are relative to the workspace root +const ADJECTIVES: &[&str] = &make_word_array!("namegen/src/adjectives.txt"); +const ADVERBS: &[&str] = &make_word_array!("namegen/src/adverbs.txt"); +const NOUNS: &[&str] = &make_word_array!("namegen/src/names.txt"); + +sa::const_assert!(!NOUNS.is_empty()); +sa::const_assert!(!ADJECTIVES.is_empty()); +sa::const_assert!(!ADVERBS.is_empty()); + +pub fn make_petname(num_words: NumWords, separator_8: c_char) -> Result { + let separator_c = separator_8 as u8 as char; + if !matches!(separator_c, '-' | '_') { + return Err(PetnameError::InvalidSeparator(separator_8 as i8)); + } + + let sources: &[&[&str]] = match num_words { + NumWords::One => &[NOUNS], + NumWords::Two => &[ADJECTIVES, NOUNS], + NumWords::Three => &[ADVERBS, ADJECTIVES, NOUNS], + num => return Err(PetnameError::InvalidWordNumber(num.repr)), + }; + + let mut rng_engine = rand::rng(); + + let words: Vec<_> = sources + .iter() + .map(|&arr| choose_from_str_array(arr, &mut rng_engine)) + .collect(); + + Ok(words.join(&separator_c.to_string())) +} +fn choose_from_str_array(word_array: &[&'static str], rng: &mut ThreadRng) -> &'static str { + match word_array.choose(rng).copied() { + Some(str) => str, + //If one of the sources of words was empty, compile-time failure + _ => unreachable!(), + } +} diff --git a/rxx/namegen/tests/test_petname_error.rs b/rxx/namegen/tests/test_petname_error.rs new file mode 100644 index 00000000000..bd4d8cb4035 --- /dev/null +++ b/rxx/namegen/tests/test_petname_error.rs @@ -0,0 +1,12 @@ +use namegen::petname_error::PetnameError; +#[test] +fn test_error_display() { + assert_eq!( + PetnameError::InvalidWordNumber(5).to_string(), + "Invalid word number: 5" + ); + assert_eq!( + PetnameError::InvalidSeparator(64).to_string(), + "Invalid separator, ASCII code: 64" + ); +} diff --git a/rxx/namegen/tests/test_petname_generator.rs b/rxx/namegen/tests/test_petname_generator.rs new file mode 100644 index 00000000000..7adbfdbe873 --- /dev/null +++ b/rxx/namegen/tests/test_petname_generator.rs @@ -0,0 +1,40 @@ +use namegen::{ffi::NumWords, petname_error::PetnameError, petname_generator::make_petname}; +use std::collections::HashSet; +use std::ffi::c_char; + +#[test] +fn generates_requested_word_number_with_separator() { + let petname_1 = make_petname(NumWords::One, '-' as c_char).unwrap(); + let petname_2 = make_petname(NumWords::Two, '-' as c_char).unwrap(); + let petname_3 = make_petname(NumWords::Three, '-' as c_char).unwrap(); + assert_eq!(petname_1.split('-').count(), 1); + assert_eq!(petname_2.split('-').count(), 2); + assert_eq!(petname_3.split('-').count(), 3); +} + +#[test] +fn filters_out_bad_input() { + //First we test that failure is not due to CXX enum syntax + let result = make_petname(NumWords { repr: 0 }, '-' as c_char); + assert!(result.is_ok()); + let result = make_petname(NumWords { repr: 4 }, '-' as c_char); + assert!(matches!(result, Err(PetnameError::InvalidWordNumber(4)))); + + let result = make_petname(NumWords::One, '(' as c_char); + assert!(matches!(result, Err(PetnameError::InvalidSeparator(_)))); +} + +#[test] +fn can_generate_unique_names() { + let mut hashset: HashSet = HashSet::new(); + const TOTAL_NAMES: usize = 1000; + + for i in 0..TOTAL_NAMES { + let petname = make_petname(NumWords::Three, '-' as c_char).unwrap(); + assert!( + hashset.insert(petname), + "Generated duplicate petname at iteration {}", + i + ); + } +} diff --git a/snap/local/snapcraft-ci-override.yaml.in b/snap/local/snapcraft-ci-override.yaml.in index 53f6ff75176..6fcc517c29e 100644 --- a/snap/local/snapcraft-ci-override.yaml.in +++ b/snap/local/snapcraft-ci-override.yaml.in @@ -67,3 +67,5 @@ parts: - GITHUB_ACTIONS: '${GITHUB_ACTIONS}' - CMAKE_PRESET: '${CMAKE_PRESET}' - MP_ALLOW_OPTIONAL_FEATURES: '${MP_ALLOW_OPTIONAL_FEATURES}' + build-packages: + - jq diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index de4ae6ae56b..b33d3686146 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -211,6 +211,11 @@ parts: override-pull: | set -e + if ! snap list rustup >/dev/null 2>&1; then + snap install rustup --classic + rustup default stable + fi + # https://github.com/microsoft/vcpkg/issues/37279 # vcpkg-systemd: required for meson.build python3 -m pip install --break-system-packages jinja2 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 221d2f083fc..ab60dee9eca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,7 +25,6 @@ add_subdirectory(image_host) add_subdirectory(iso) add_subdirectory(logging) add_subdirectory(network) -add_subdirectory(petname) add_subdirectory(platform) add_subdirectory(process) add_subdirectory(rpc) diff --git a/src/client/gui/CMakeLists.txt b/src/client/gui/CMakeLists.txt index 4cb78238493..4440f599513 100644 --- a/src/client/gui/CMakeLists.txt +++ b/src/client/gui/CMakeLists.txt @@ -19,7 +19,7 @@ message(STATUS "Setting build number to: ${MULTIPASS_BUILD_NUMBER}") add_library(dart_ffi SHARED ffi/dart_ffi.cpp) target_link_libraries(dart_ffi - petname + namegen client_common client_platform ) diff --git a/src/client/gui/ffi/dart_ffi.cpp b/src/client/gui/ffi/dart_ffi.cpp index 7050de1ae0b..01ba280f6f3 100644 --- a/src/client/gui/ffi/dart_ffi.cpp +++ b/src/client/gui/ffi/dart_ffi.cpp @@ -32,7 +32,7 @@ char* generate_petname() static constexpr auto error = "failed generating petname"; try { - static mp::NameGenerator::UPtr generator = mp::make_default_name_generator(); + static mp::NameGenerator::UPtr generator = mp::petname::make_petname_provider(); const auto name = generator->make_name(); return strdup(name.c_str()); } @@ -76,9 +76,7 @@ struct KeyCertificatePair get_cert_pair() const auto provider = mpc::get_cert_provider(); const auto cert = provider->PEM_certificate(); const auto key = provider->PEM_signing_key(); - struct KeyCertificatePair pair - { - }; + struct KeyCertificatePair pair{}; pair.pem_cert = strdup(cert.c_str()); pair.pem_priv_key = strdup(key.c_str()); return pair; diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 14b26931226..24b0b143a63 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -34,7 +34,7 @@ target_link_libraries(daemon fmt::fmt-header-only image_host logger - petname + namegen platform rpc settings diff --git a/src/daemon/daemon_config.cpp b/src/daemon/daemon_config.cpp index 12d08bbf34c..fecff90fa0f 100644 --- a/src/daemon/daemon_config.cpp +++ b/src/daemon/daemon_config.cpp @@ -193,7 +193,7 @@ std::unique_ptr mp::DaemonConfigBuilder::build() days_to_expire); } if (name_generator == nullptr) - name_generator = mp::make_default_name_generator(); + name_generator = mp::petname::make_petname_provider(); if (server_address.empty()) server_address = platform::default_server_address(); if (ssh_key_provider == nullptr) diff --git a/src/petname/CMakeLists.txt b/src/petname/CMakeLists.txt deleted file mode 100644 index eb59e3e9d63..00000000000 --- a/src/petname/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) Canonical, Ltd. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Authored by: Alberto Aguirre - -set(PETNAME_GENERATED_SOURCE_DIR ${MULTIPASS_GENERATED_SOURCE_DIR}/multipass/petname) -set(PETNAME_GENERATED_HEADER ${PETNAME_GENERATED_SOURCE_DIR}/names.h) -file(MAKE_DIRECTORY ${PETNAME_GENERATED_SOURCE_DIR}) - -add_executable(text_to_string_array - text_to_string_array.cpp) - -add_custom_command( - OUTPUT "${PETNAME_GENERATED_HEADER}" - COMMAND $ - ARGS ${CMAKE_CURRENT_SOURCE_DIR}/adjectives.txt ${CMAKE_CURRENT_SOURCE_DIR}/adverbs.txt - ${CMAKE_CURRENT_SOURCE_DIR}/names.txt ${PETNAME_GENERATED_HEADER} - DEPENDS text_to_string_array - COMMENT "Converting petnames to c++ header" - VERBATIM) - -set_source_files_properties(${PETNAME_GENERATED_HEADER} PROPERTIES GENERATED TRUE) - -add_library(petname STATIC - ${PETNAME_GENERATED_HEADER} - petname.cpp - make_name_generator.cpp) diff --git a/src/petname/petname.cpp b/src/petname/petname.cpp deleted file mode 100644 index 1079cd14ecb..00000000000 --- a/src/petname/petname.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) Canonical, Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authored by: Alberto Aguirre - * - */ - -#include "petname.h" -#include "multipass/petname/names.h" - -namespace mp = multipass; -namespace -{ -constexpr auto num_names = std::extent::value; -constexpr auto num_adverbs = std::extent::value; -constexpr auto num_adjectives = std::extent::value; - -// Arbitrary but arrays should have at least 100 entries each -static_assert(num_names >= 100, ""); -static_assert(num_adverbs >= 100, ""); -static_assert(num_adjectives >= 100, ""); - -std::mt19937 make_engine() -{ - std::random_device device; - return std::mt19937(device()); -} -} // namespace - -mp::Petname::Petname(std::string separator) : Petname(NumWords::TWO, separator) -{ -} - -mp::Petname::Petname(NumWords num_words) : Petname(num_words, "-") -{ -} - -mp::Petname::Petname(NumWords num_words, std::string separator) - : separator{separator}, - num_words{num_words}, - engine{make_engine()}, - name_dist{1, num_names - 1}, - adjective_dist{0, num_adjectives - 1}, - adverb_dist{0, num_adverbs - 1} -{ -} - -std::string mp::Petname::make_name() -{ - std::string name = multipass::petname::names[name_dist(engine)]; - std::string adjective = multipass::petname::adjectives[adjective_dist(engine)]; - std::string adverb = multipass::petname::adverbs[adverb_dist(engine)]; - - switch (num_words) - { - case NumWords::ONE: - return name; - case NumWords::TWO: - return adjective + separator + name; - case NumWords::THREE: - return adverb + separator + adjective + separator + name; - default: - throw std::invalid_argument("Invalid number of words chosen"); - } -} diff --git a/src/petname/petname.h b/src/petname/petname.h deleted file mode 100644 index 1f44708c9fe..00000000000 --- a/src/petname/petname.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) Canonical, Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authored by: Alberto Aguirre - * - */ - -#pragma once - -#include - -#include -#include - -namespace multipass -{ -class Petname final : public NameGenerator -{ -public: - enum class NumWords - { - ONE, - TWO, - THREE - }; - - /// Constructs an instance that will generate names using - /// the requested separator and the requested number of words - Petname(NumWords num_words, std::string separator); - /// Constructs an instance that will generate names using - /// a default separator of "-" and the requested number of words - explicit Petname(NumWords num_words); - /// Constructs an instance that will generate names using - /// the requested separator and two words - explicit Petname(std::string separator); - - std::string make_name() override; - -private: - std::string separator; - NumWords num_words; - std::mt19937 engine; - std::uniform_int_distribution name_dist; - std::uniform_int_distribution adjective_dist; - std::uniform_int_distribution adverb_dist; -}; -} // namespace multipass diff --git a/src/petname/text_to_string_array.cpp b/src/petname/text_to_string_array.cpp deleted file mode 100644 index dd296f3b6e2..00000000000 --- a/src/petname/text_to_string_array.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) Canonical, Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authored by: Alberto Aguirre - * - */ - -#include -#include -#include -#include - -namespace -{ -void usage(char* argv[]) -{ - std::cout << "Usage:\n "; - std::cout << argv[0] << " \n"; -} - -std::vector words_in(const std::string& filename) -{ - std::ifstream input_stream(filename); - std::string word; - std::vector words; - while (std::getline(input_stream, word)) - { - words.push_back(word); - } - return words; -} - -class Words -{ -public: - Words(const std::string& filename, std::string var_name) - : var_name{std::move(var_name)}, words{words_in(filename)} - { - } - - void print_to(std::ostream& out) - { - out << "const char* " << var_name << "[] =\n{\n"; - for (auto const& w : words) - { - out << " \"" << w << "\",\n"; - } - out << "};\n\n"; - } - -private: - std::string var_name; - std::vector words; -}; -} // namespace - -int main(int argc, char* argv[]) -try -{ - if (argc != 5) - { - usage(argv); - return EXIT_FAILURE; - } - - Words adjectives{argv[1], "adjectives"}; - Words adverbs{argv[2], "adverbs"}; - Words names{argv[3], "names"}; - - std::ofstream out(argv[4]); - - out << "//Auto Generated, any edits will be lost\n\n"; - out << "namespace multipass\n{\n"; - out << "namespace petname\n{\n"; - - adjectives.print_to(out); - adverbs.print_to(out); - names.print_to(out); - - out << "}\n}"; - - return EXIT_SUCCESS; -} -catch (const std::exception& e) -{ - std::cerr << "Error: " << e.what() << "\n"; -} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 187a330faa9..434c5bae9d5 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -20,7 +20,7 @@ include(c_mock_defines.cmake) find_package(GTest CONFIG REQUIRED) find_package(premock CONFIG REQUIRED) -add_executable(multipass_tests +add_executable(multipass_cpp_tests common.cpp daemon_test_fixture.cpp file_operations.cpp @@ -89,11 +89,11 @@ add_executable(multipass_tests test_output_formatter.cpp test_permission_utils.cpp test_persistent_settings_handler.cpp - test_petname.cpp test_private_pass_provider.cpp test_qemuimg_process_spec.cpp test_recursive_dir_iter.cpp test_remote_settings_handler.cpp + test_rust_integration.cpp test_setting_specs.cpp test_settings.cpp test_sftp_client.cpp @@ -126,12 +126,12 @@ add_executable(multipass_tests ) if(AVAILABILITY_ZONES_ENABLED) - target_sources(multipass_tests PRIVATE + target_sources(multipass_cpp_tests PRIVATE test_base_availability_zone.cpp test_base_availability_zone_manager.cpp) endif() -target_include_directories(multipass_tests +target_include_directories(multipass_cpp_tests PRIVATE ${CMAKE_SOURCE_DIR} PRIVATE ${CMAKE_SOURCE_DIR}/src PRIVATE ${CMAKE_SOURCE_DIR}/src/platform/backends @@ -150,7 +150,7 @@ target_compile_definitions(ssh_client_test PRIVATE target_compile_definitions(utils_test PRIVATE -DEVP_PBE_scrypt=ut_premock_EVP_PBE_scrypt) -target_link_libraries(multipass_tests +target_link_libraries(multipass_cpp_tests cert client daemon @@ -160,7 +160,7 @@ target_link_libraries(multipass_tests ip_address iso network - petname + namegen settings simplestreams sftp_test @@ -175,13 +175,13 @@ target_link_libraries(multipass_tests yaml-cpp::yaml-cpp ) -add_test(NAME multipass_tests - COMMAND multipass_tests +add_test(NAME multipass_cpp_tests + COMMAND multipass_cpp_tests ) foreach(BACKEND IN LISTS MULTIPASS_BACKENDS) string(TOUPPER ${BACKEND}_ENABLED DEF) - target_compile_definitions(multipass_tests PRIVATE ${DEF}) + target_compile_definitions(multipass_cpp_tests PRIVATE ${DEF}) endforeach() string(TOLOWER ${CMAKE_HOST_SYSTEM_PROCESSOR} MANIFEST_ARCH) @@ -191,7 +191,7 @@ elseif (${MANIFEST_ARCH} STREQUAL "ppc64le") string(TOLOWER "ppc64el" MANIFEST_ARCH) endif() -target_compile_definitions(multipass_tests PRIVATE -DMANIFEST_ARCH="${MANIFEST_ARCH}") +target_compile_definitions(multipass_cpp_tests PRIVATE -DMANIFEST_ARCH="${MANIFEST_ARCH}") file(GLOB_RECURSE TEST_DATA_FILES CONFIGURE_DEPENDS @@ -233,14 +233,14 @@ foreach(f IN LISTS PLAIN_TEST_DATA_FILES) endforeach() add_custom_target(copy_test_data ALL DEPENDS ${COPIED_PLAIN_FILES}) -add_dependencies(multipass_tests copy_test_data) +add_dependencies(multipass_cpp_tests copy_test_data) set(TEST_DATA_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_data") configure_file( test_data_path.h.in ${MULTIPASS_GENERATED_SOURCE_DIR}/multipass/test_data_path.h @ONLY) -target_include_directories(multipass_tests PRIVATE ${MULTIPASS_GENERATED_SOURCE_DIR}/multipass/gen) +target_include_directories(multipass_cpp_tests PRIVATE ${MULTIPASS_GENERATED_SOURCE_DIR}/multipass/gen) # Mock binaries for testing BasicProcess (cross-platform compatible) add_executable(mock_process @@ -252,17 +252,17 @@ set_target_properties(mock_process RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/mocks" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/mocks") -add_dependencies(multipass_tests mock_process) +add_dependencies(multipass_cpp_tests mock_process) if(MSVC) add_definitions(-DGTEST_LANG_CXX11=1) - target_compile_options(multipass_tests PRIVATE /bigobj) + target_compile_options(multipass_cpp_tests PRIVATE /bigobj) elseif(APPLE) set(CMAKE_CXX_FLAGS "-Wno-inconsistent-missing-override") add_definitions(-DHOST_ARCH="${HOST_ARCH}") endif() -target_include_directories(multipass_tests +target_include_directories(multipass_cpp_tests BEFORE PRIVATE ${CMAKE_SOURCE_DIR}/src/platform/backends/shared/${MULTIPASS_PLATFORM} ) diff --git a/tests/unit/applevz/CMakeLists.txt b/tests/unit/applevz/CMakeLists.txt index 681f31daad6..afa7d58bd00 100644 --- a/tests/unit/applevz/CMakeLists.txt +++ b/tests/unit/applevz/CMakeLists.txt @@ -13,7 +13,7 @@ # along with this program. If not, see . # -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_applevz_virtual_machine.cpp ) diff --git a/tests/unit/daemon_test_fixture.cpp b/tests/unit/daemon_test_fixture.cpp index f393d63c3d0..50ed809b93e 100644 --- a/tests/unit/daemon_test_fixture.cpp +++ b/tests/unit/daemon_test_fixture.cpp @@ -397,7 +397,7 @@ void mpt::DaemonTestFixture::send_commands(std::vector> TestClient client{client_config}; for (const auto& command : commands) { - QStringList args = QStringList() << "multipass_test"; + QStringList args = QStringList() << "multipass_cpp_test"; for (const auto& arg : command) { diff --git a/tests/unit/hyperv/CMakeLists.txt b/tests/unit/hyperv/CMakeLists.txt index f325f56e9d7..1d7585dbfd8 100644 --- a/tests/unit/hyperv/CMakeLists.txt +++ b/tests/unit/hyperv/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_hyperv_backend.cpp ) diff --git a/tests/unit/linux/CMakeLists.txt b/tests/unit/linux/CMakeLists.txt index a922defdc24..d8b20dd1c3e 100644 --- a/tests/unit/linux/CMakeLists.txt +++ b/tests/unit/linux/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_apparmored_process.cpp ${CMAKE_CURRENT_LIST_DIR}/test_backend_utils.cpp @@ -8,19 +8,19 @@ target_sources(multipass_tests ) if(TARGET logger_linux_journald) - target_sources(multipass_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_journald_logger.cpp) - target_link_libraries(multipass_tests logger_linux_journald) + target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_journald_logger.cpp) + target_link_libraries(multipass_cpp_tests logger_linux_journald) endif() if(TARGET logger_linux_syslog) - target_sources(multipass_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_syslog_logger.cpp) - target_link_libraries(multipass_tests logger_linux_syslog) + target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_syslog_logger.cpp) + target_link_libraries(multipass_cpp_tests logger_linux_syslog) endif() target_compile_definitions(shared_linux_test PRIVATE -Daa_is_enabled=ut_premock_aa_is_enabled) -target_link_libraries(multipass_tests +target_link_libraries(multipass_cpp_tests shared_linux_test) add_executable(apparmor_parser @@ -32,4 +32,4 @@ set_target_properties(apparmor_parser RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/mocks" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/mocks") -add_dependencies(multipass_tests apparmor_parser) +add_dependencies(multipass_cpp_tests apparmor_parser) diff --git a/tests/unit/macos/CMakeLists.txt b/tests/unit/macos/CMakeLists.txt index 861b210ee53..d443fb2b8df 100644 --- a/tests/unit/macos/CMakeLists.txt +++ b/tests/unit/macos/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_platform_osx.cpp ${CMAKE_CURRENT_LIST_DIR}/test_backend_utils.cpp diff --git a/tests/unit/main.cpp b/tests/unit/main.cpp index 2282f59be31..fc26fe9c5fd 100644 --- a/tests/unit/main.cpp +++ b/tests/unit/main.cpp @@ -31,7 +31,7 @@ int main(int argc, char* argv[]) GOOGLE_PROTOBUF_VERIFY_VERSION; QCoreApplication app(argc, argv); - QCoreApplication::setApplicationName("multipass_tests"); + QCoreApplication::setApplicationName("multipass_cpp_tests"); ::testing::InitGoogleTest(&argc, argv); mp::test::MockStandardPaths::mockit(); diff --git a/tests/unit/qemu/CMakeLists.txt b/tests/unit/qemu/CMakeLists.txt index efcb3a7b546..5aaddadad33 100644 --- a/tests/unit/qemu/CMakeLists.txt +++ b/tests/unit/qemu/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_qemu_backend.cpp ${CMAKE_CURRENT_LIST_DIR}/test_qemu_img_utils.cpp @@ -17,7 +17,7 @@ set_target_properties(qemu-img RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/mocks" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/mocks") -add_dependencies(multipass_tests +add_dependencies(multipass_cpp_tests qemu-img ) diff --git a/tests/unit/qemu/linux/CMakeLists.txt b/tests/unit/qemu/linux/CMakeLists.txt index 06681ae0d89..e412b9e303a 100644 --- a/tests/unit/qemu/linux/CMakeLists.txt +++ b/tests/unit/qemu/linux/CMakeLists.txt @@ -13,7 +13,7 @@ # along with this program. If not, see . # -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_dnsmasq_server.cpp ${CMAKE_CURRENT_LIST_DIR}/test_dnsmasq_process_spec.cpp @@ -21,7 +21,7 @@ target_sources(multipass_tests ${CMAKE_CURRENT_LIST_DIR}/test_qemu_platform_linux.cpp ) -target_include_directories(multipass_tests PRIVATE ${CMAKE_SOURCE_DIR}/src/platform/backends/qemu) +target_include_directories(multipass_cpp_tests PRIVATE ${CMAKE_SOURCE_DIR}/src/platform/backends/qemu) add_executable(dnsmasq mock_dnsmasq.cpp) @@ -44,7 +44,7 @@ set_target_properties(dhcp_release RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/mocks" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/mocks") -add_dependencies(multipass_tests +add_dependencies(multipass_cpp_tests dnsmasq dhcp_release ) diff --git a/tests/unit/qemu/macos/CMakeLists.txt b/tests/unit/qemu/macos/CMakeLists.txt index 484feba0a93..2d568f0fee3 100644 --- a/tests/unit/qemu/macos/CMakeLists.txt +++ b/tests/unit/qemu/macos/CMakeLists.txt @@ -13,9 +13,9 @@ # along with this program. If not, see . # -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_qemu_platform_macos.cpp ) -target_include_directories(multipass_tests PRIVATE ${CMAKE_SOURCE_DIR}/src/platform/backends/qemu) +target_include_directories(multipass_cpp_tests PRIVATE ${CMAKE_SOURCE_DIR}/src/platform/backends/qemu) diff --git a/tests/unit/test_argparser.cpp b/tests/unit/test_argparser.cpp index 47ccbce8880..b88698b3f98 100644 --- a/tests/unit/test_argparser.cpp +++ b/tests/unit/test_argparser.cpp @@ -45,7 +45,7 @@ TEST_P(TestVerbosity, testVariousVs) std::ostringstream oss; const auto cmds = std::vector{}; const auto v = GetParam(); - auto args = QStringList{"multipass_tests"}; + auto args = QStringList{"multipass_cpp_tests"}; if (v) { diff --git a/tests/unit/test_cli_client.cpp b/tests/unit/test_cli_client.cpp index a481ee98f31..760933e4a9a 100644 --- a/tests/unit/test_cli_client.cpp +++ b/tests/unit/test_cli_client.cpp @@ -234,7 +234,7 @@ struct Client : public Test { mp::ClientConfig client_config{server_address, get_client_cert_provider(), &term}; mp::Client client{client_config}; - QStringList args = QStringList() << "multipass_test"; + QStringList args = QStringList() << "multipass_cpp_test"; for (const auto& arg : command) { diff --git a/tests/unit/test_petname.cpp b/tests/unit/test_petname.cpp deleted file mode 100644 index 13dbc058177..00000000000 --- a/tests/unit/test_petname.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) Canonical, Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authored by: Alberto Aguirre - * - */ - -#include "common.h" - -#include - -#include -#include -#include -#include - -namespace mp = multipass; - -using namespace testing; - -namespace -{ -std::vector split(const std::string& string, const std::string& delimiter) -{ - std::regex regex(delimiter); - return {std::sregex_token_iterator{string.begin(), string.end(), regex, -1}, - std::sregex_token_iterator{}}; -} -} // namespace -TEST(Petname, generatesTheRequestedNumWords) -{ - std::string separator{"-"}; - mp::Petname gen1{mp::Petname::NumWords::ONE, separator}; - mp::Petname gen2{mp::Petname::NumWords::TWO, separator}; - mp::Petname gen3{mp::Petname::NumWords::THREE, separator}; - - auto one_word_name = gen1.make_name(); - auto tokens = split(one_word_name, separator); - EXPECT_THAT(tokens.size(), Eq(1u)); - - auto two_word_name = gen2.make_name(); - tokens = split(two_word_name, separator); - EXPECT_THAT(tokens.size(), Eq(2u)); - - auto three_word_name = gen3.make_name(); - tokens = split(three_word_name, separator); - EXPECT_THAT(tokens.size(), Eq(3u)); -} - -TEST(Petname, usesDefaultSeparator) -{ - std::string expected_separator{"-"}; - mp::Petname name_generator{mp::Petname::NumWords::THREE}; - auto name = name_generator.make_name(); - auto tokens = split(name, expected_separator); - EXPECT_THAT(tokens.size(), Eq(3u)); -} - -TEST(Petname, generatesTwoTokensByDefault) -{ - std::string separator{"-"}; - mp::Petname name_generator{separator}; - auto name = name_generator.make_name(); - auto tokens = split(name, separator); - EXPECT_THAT(tokens.size(), Eq(2u)); - - // Each token should be unique - std::unordered_set set(tokens.begin(), tokens.end()); - EXPECT_THAT(set.size(), Eq(tokens.size())); -} - -TEST(Petname, canGenerateAtLeastHundredUniqueNames) -{ - std::string separator{"-"}; - mp::Petname name_generator{mp::Petname::NumWords::THREE, separator}; - std::unordered_set name_set; - const std::size_t expected_num_unique_names{100}; - - // TODO: fixme, randomness is involved in name generation hence there's a non-zero probability - // we will fail to generate the number of expected unique names. - for (std::size_t i = 0; i < 10 * expected_num_unique_names; i++) - { - name_set.insert(name_generator.make_name()); - } - - EXPECT_THAT(name_set.size(), Ge(expected_num_unique_names)); -} diff --git a/tests/unit/test_rust_integration.cpp b/tests/unit/test_rust_integration.cpp new file mode 100644 index 00000000000..ff0fe90449c --- /dev/null +++ b/tests/unit/test_rust_integration.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "common.h" + +#include + +#include +#include +#include +#include + +namespace mp = multipass; + +using namespace testing; + +namespace +{ +std::vector split(const std::string& string, char delimiter) +{ + std::regex regex(std::string(1, delimiter)); + return {std::sregex_token_iterator{string.begin(), string.end(), regex, -1}, + std::sregex_token_iterator{}}; +} +} // namespace + +// Tests are intended to test the Rust module integration, not the module itself + +TEST(PetnameIntegration, usesDefaultSeparator) +{ + char expected_separator{'-'}; + mp::NameGenerator::UPtr name_generator{multipass::petname::make_petname_provider()}; + auto name = name_generator->make_name(); + auto tokens = split(name, expected_separator); + EXPECT_THAT(tokens.size(), Eq(2u)); +} + +TEST(PetnameIntegration, generatesTwoTokensByDefault) +{ + constexpr char separator{'-'}; + mp::NameGenerator::UPtr name_generator{multipass::petname::make_petname_provider()}; + auto name = name_generator->make_name(); + auto tokens = split(name, separator); + EXPECT_THAT(tokens.size(), Eq(2u)); + + // Each token should be unique + std::unordered_set set(tokens.begin(), tokens.end()); + EXPECT_THAT(set.size(), Eq(tokens.size())); +} diff --git a/tests/unit/unix/CMakeLists.txt b/tests/unit/unix/CMakeLists.txt index bcc55cc3146..dd564147742 100644 --- a/tests/unit/unix/CMakeLists.txt +++ b/tests/unit/unix/CMakeLists.txt @@ -13,7 +13,7 @@ # along with this program. If not, see . # -target_sources(multipass_tests PRIVATE +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/mock_libc_functions.cpp ${CMAKE_CURRENT_LIST_DIR}/test_daemon_rpc.cpp ${CMAKE_CURRENT_LIST_DIR}/test_platform_unix.cpp @@ -30,7 +30,7 @@ target_compile_definitions(platform_test PRIVATE -Dgetgrnam=ut_premock_getgrnam ) -target_link_libraries(multipass_tests +target_link_libraries(multipass_cpp_tests console_test platform_test ) diff --git a/tests/unit/windows/CMakeLists.txt b/tests/unit/windows/CMakeLists.txt index 415de089518..e2cdb4053d1 100644 --- a/tests/unit/windows/CMakeLists.txt +++ b/tests/unit/windows/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(multipass_tests +target_sources(multipass_cpp_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/powershell_test_helper.cpp ${CMAKE_CURRENT_LIST_DIR}/test_platform_win.cpp