Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++ Test #440

Merged
merged 27 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions ci/run_ctests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,16 @@ set -euo pipefail
# Support customizing the ctests' install location
cd "${INSTALL_PREFIX:-${CONDA_PREFIX:-/usr}}/bin/tests/libkvikio/"

# Run BASIC_IO_TEST
./BASIC_IO_TEST
# Run basic tests
rapids-logger "Run BASIC_IO_EXAMPLE"
./BASIC_IO_EXAMPLE
rapids-logger "Run BASIC_NO_CUDA_EXAMPLE"
./BASIC_NO_CUDA_EXAMPLE

# Run gtests
rapids-logger "Run gtests"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a weak preference to avoid rapids-logger commands in the run_*.sh scripts because I think those are intended for reuse outside of the CI workflows (in DLFW, perhaps?). In other use cases, the gha-tools utilities may not be present. Either use echo or replace the rapids-logger statements with code comments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trxcllnt may have comments about appropriate use cases here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed the rapids-logger use

./cpp_tests
# TODO: how to use ctest instead of executing the test directly?
# The following line fails with a "ctest doesn't exist" in CI.
# Do we need to install ctest in the CI images?
# ctest --no-tests=error --output-on-failure "$@"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to install ctest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to install cmake. Add to the test_cpp group.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed

5 changes: 3 additions & 2 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ rapids_cmake_build_type(Release)

# build options
option(KvikIO_BUILD_EXAMPLES "Configure CMake to build examples" ON)
option(KvikIO_BUILD_TESTS "Configure CMake to build tests" ON)

rapids_cmake_support_conda_env(conda_env MODIFY_PREFIX_PATH)

Expand Down Expand Up @@ -138,9 +139,9 @@ if(KvikIO_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

# optionally build tests
if(KvikIO_BUILD_TESTS AND CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(cmake/thirdparty/get_gtest.cmake)
include(${rapids-cmake-dir}/cpm/gtest.cmake)
rapids_cpm_gtest(BUILD_STATIC)
madsbk marked this conversation as resolved.
Show resolved Hide resolved
include(CTest) # calls enable_testing()
add_subdirectory(tests)
endif()
Expand Down
30 changes: 18 additions & 12 deletions cpp/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@

set(TEST_INSTALL_PATH bin/tests/libkvikio)

# Example: basic_io

if(CUDAToolkit_FOUND)
add_executable(BASIC_IO_TEST basic_io.cpp)
set_target_properties(BASIC_IO_TEST PROPERTIES INSTALL_RPATH "\$ORIGIN/../../lib")
target_include_directories(BASIC_IO_TEST PRIVATE ../include ${cuFile_INCLUDE_DIRS})
target_link_libraries(BASIC_IO_TEST PRIVATE kvikio CUDA::cudart)
add_executable(BASIC_IO_EXAMPLE basic_io.cpp)
set_target_properties(BASIC_IO_EXAMPLE PROPERTIES INSTALL_RPATH "\$ORIGIN/../../lib")
target_include_directories(BASIC_IO_EXAMPLE PRIVATE ../include ${cuFile_INCLUDE_DIRS})
target_link_libraries(BASIC_IO_EXAMPLE PRIVATE kvikio CUDA::cudart)

if(CMAKE_COMPILER_IS_GNUCXX)
set(KVIKIO_CXX_FLAGS "-Wall;-Werror;-Wno-unknown-pragmas")
target_compile_options(BASIC_IO_TEST PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${KVIKIO_CXX_FLAGS}>")
target_compile_options(
BASIC_IO_EXAMPLE PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${KVIKIO_CXX_FLAGS}>"
)
endif()

install(
TARGETS BASIC_IO_TEST
TARGETS BASIC_IO_EXAMPLE
COMPONENT testing
DESTINATION ${TEST_INSTALL_PATH}
EXCLUDE_FROM_ALL
Expand All @@ -35,20 +39,22 @@ else()
message(STATUS "Cannot build the basic_io example when CUDA is not found")
endif()

add_executable(BASIC_NO_CUDA_TEST basic_no_cuda.cpp)
set_target_properties(BASIC_NO_CUDA_TEST PROPERTIES INSTALL_RPATH "\$ORIGIN/../../lib")
target_include_directories(BASIC_NO_CUDA_TEST PRIVATE ../include)
target_link_libraries(BASIC_NO_CUDA_TEST PRIVATE kvikio)
# Example: basic_no_cuda

add_executable(BASIC_NO_CUDA_EXAMPLE basic_no_cuda.cpp)
set_target_properties(BASIC_NO_CUDA_EXAMPLE PROPERTIES INSTALL_RPATH "\$ORIGIN/../../lib")
target_include_directories(BASIC_NO_CUDA_EXAMPLE PRIVATE ../include)
target_link_libraries(BASIC_NO_CUDA_EXAMPLE PRIVATE kvikio)

if(CMAKE_COMPILER_IS_GNUCXX)
set(KVIKIO_CXX_FLAGS "-Wall;-Werror;-Wno-unknown-pragmas")
target_compile_options(
BASIC_NO_CUDA_TEST PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${KVIKIO_CXX_FLAGS}>"
BASIC_NO_CUDA_EXAMPLE PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${KVIKIO_CXX_FLAGS}>"
)
endif()

install(
TARGETS BASIC_NO_CUDA_TEST
TARGETS BASIC_NO_CUDA_EXAMPLE
COMPONENT testing
DESTINATION ${TEST_INSTALL_PATH}
EXCLUDE_FROM_ALL
Expand Down
41 changes: 41 additions & 0 deletions cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# =============================================================================
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
# =============================================================================

# ##################################################################################################
# enable testing -----------------------------------------------------------------------------------
# ##################################################################################################
enable_testing()

include(rapids-test)

file(GLOB SOURCES "*.cpp")
add_executable(cpp_tests ${SOURCES})
set_target_properties(
cpp_tests
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$<BUILD_INTERFACE:${KvikIO_BINARY_DIR}/gtests>"
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
# For std:: support of __int128_t. Can be removed once using cuda::std
CXX_EXTENSIONS ON
CUDA_STANDARD 17
CUDA_STANDARD_REQUIRED ON
)
target_link_libraries(cpp_tests PRIVATE kvikio::kvikio GTest::gmock GTest::gtest)
rapids_test_add(
NAME cpp_tests
COMMAND cpp_tests
INSTALL_COMPONENT_SET testing
)

rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing DESTINATION bin/tests/libkvikio)
23 changes: 23 additions & 0 deletions cpp/tests/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <gtest/gtest.h>

int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
43 changes: 43 additions & 0 deletions cpp/tests/test_basic_io.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <kvikio/file_handle.hpp>

#include "utils.hpp"

using namespace kvikio::test;

TEST(BasicIO, write_read)
{
TempDir tmp_dir{false};
auto filepath = tmp_dir.path() / "test";

auto dev_a = DevBuffer::arange(100);
auto dev_b = DevBuffer::zero_like(dev_a);

{
kvikio::FileHandle f(filepath, "w");
auto nbytes = f.write(dev_a.ptr, dev_a.nbytes, 0, 0);
EXPECT_EQ(nbytes, dev_a.nbytes);
}

{
kvikio::FileHandle f(filepath, "r");
auto nbytes = f.read(dev_b.ptr, dev_b.nbytes, 0, 0);
EXPECT_EQ(nbytes, dev_b.nbytes);
expect_equal(dev_a, dev_b);
}
}
172 changes: 172 additions & 0 deletions cpp/tests/utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <cstdint>
#include <filesystem>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

#include <cuda_runtime_api.h>
#include <gtest/gtest.h>

#include <kvikio/error.hpp>

namespace kvikio::test {

/** \
* @brief Error checking macro for CUDA runtime API functions. \
* \
* Invokes a CUDA runtime API function call. If the call does not return \
* `cudaSuccess`, invokes cudaGetLastError() to clear the error and throws an \
* exception detailing the CUDA error that occurred \
* \
* Defaults to throwing std::runtime_error, but a custom exception may also be \
* specified. \
* \
* Example: \
* ```c++ \
* \
* // Throws std::runtime_error if `cudaMalloc` fails \
* KVIKIO_CHECK_CUDA(cudaMalloc(&p, 100)); \
* \
* // Throws std::runtime_error if `cudaMalloc` fails \
* KVIKIO_CHECK_CUDA(cudaMalloc(&p, 100), std::runtime_error); \
* ``` \
* \
*/ \
#define KVIKIO_CHECK_CUDA(...) \
GET_KVIKIO_CHECK_CUDA_MACRO(__VA_ARGS__, KVIKIO_CHECK_CUDA_2, KVIKIO_CHECK_CUDA_1) \
(__VA_ARGS__)
#define GET_KVIKIO_CHECK_CUDA_MACRO(_1, _2, NAME, ...) NAME
#define KVIKIO_CHECK_CUDA_2(_call, _exception_type) \
do { \
cudaError_t const error = (_call); \
if (cudaSuccess != error) { \
cudaGetLastError(); \
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
throw _exception_type{std::string{"CUDA error at: "} + __FILE__ + ":" + \
KVIKIO_STRINGIFY(__LINE__) + ": " + cudaGetErrorName(error) + " " + \
cudaGetErrorString(error)}; \
} \
} while (0)
#define KVIKIO_CHECK_CUDA_1(_call) KVIKIO_CHECK_CUDA_2(_call, std::runtime_error)

/**
* @brief Help class to create a temporary directory.
*/
class TempDir {
public:
TempDir(const bool cleanup = true) : _cleanup{cleanup}
{
std::string tpl{std::filesystem::temp_directory_path() / "legate-dataframe.XXXXXX"};
madsbk marked this conversation as resolved.
Show resolved Hide resolved
if (mkdtemp(tpl.data()) == nullptr) {
throw std::runtime_error("TempDir: cannot make tmpdir: " + tpl);
}
_dir_path = tpl;
}
~TempDir() noexcept
{
if (_cleanup) {
try {
std::filesystem::remove_all(_dir_path);
} catch (...) {
std::cout << "error while trying to remove " << _dir_path.string() << std::endl;
}
}
}

TempDir(const TempDir&) = delete;
TempDir& operator=(TempDir const&) = delete;
TempDir(const TempDir&&) = delete;
TempDir&& operator=(TempDir const&&) = delete;

const std::filesystem::path& path() { return _dir_path; }

operator std::string() { return path(); }

private:
const bool _cleanup;
std::filesystem::path _dir_path{};
};

/**
* @brief Help class for creating and comparing buffers.
*/
class DevBuffer {
public:
const std::size_t nelem;
const std::size_t nbytes;
void* ptr{nullptr};

DevBuffer(std::size_t nelem) : nelem{nelem}, nbytes{nelem * sizeof(std::int64_t)}
{
KVIKIO_CHECK_CUDA(cudaMalloc(&ptr, nbytes));
}
DevBuffer(const std::vector<std::int64_t>& host_buffer) : DevBuffer{host_buffer.size()}
{
KVIKIO_CHECK_CUDA(cudaMemcpy(ptr, host_buffer.data(), nbytes, cudaMemcpyHostToDevice));
}

~DevBuffer() noexcept { cudaFree(ptr); }

[[nodiscard]] static DevBuffer arange(std::size_t nelem, std::int64_t start = 0)
{
std::vector<std::int64_t> host_buffer(nelem);
std::iota(host_buffer.begin(), host_buffer.end(), start);
return DevBuffer{host_buffer};
}

[[nodiscard]] static DevBuffer zero_like(const DevBuffer& prototype)
{
DevBuffer ret{prototype.nelem};
KVIKIO_CHECK_CUDA(cudaMemset(ret.ptr, 0, ret.nbytes));
return ret;
}

[[nodiscard]] std::vector<std::int64_t> to_vector() const
{
std::vector<std::int64_t> ret(nelem);
KVIKIO_CHECK_CUDA(cudaMemcpy(ret.data(), this->ptr, nbytes, cudaMemcpyDeviceToHost));
return ret;
}

void pprint() const
{
std::cout << "DevBuffer(";
for (auto item : to_vector()) {
std::cout << (int64_t)item << ", ";
madsbk marked this conversation as resolved.
Show resolved Hide resolved
}
std::cout << ")" << std::endl;
}
};

/**
* @brief Check that two buffers are equal
*/
inline void expect_equal(const DevBuffer& a, const DevBuffer& b)
{
EXPECT_EQ(a.nbytes, b.nbytes);
auto a_vec = a.to_vector();
auto b_vec = b.to_vector();
for (std::size_t i = 0; i < a.nelem; ++i) {
EXPECT_EQ(a_vec[i], b_vec[i]) << "Mismatch at index #" << i;
madsbk marked this conversation as resolved.
Show resolved Hide resolved
}
}

} // namespace kvikio::test