diff --git a/CMakeLists.txt b/CMakeLists.txt index 29fbb13..0d861af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,11 @@ option(ENABLE_COVERAGE "Enabling coverage" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") if(ENABLE_COVERAGE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -O0 -g") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -O0 -g") + add_library(coverage_flags INTERFACE) + + target_compile_options(coverage_flags INTERFACE --coverage -fprofile-arcs -ftest-coverage -O0) + + target_link_options(coverage_flags INTERFACE --coverage) endif() set(CMAKE_C_STANDARD 11) diff --git a/include/mqss/client.h b/include/mqss/client.h index b6890bf..92cb013 100644 --- a/include/mqss/client.h +++ b/include/mqss/client.h @@ -49,11 +49,18 @@ class MQSSClient { std::unique_ptr waitForJobResult(const JobRequest& job, size_t poll_seconds); + std::unique_ptr mClient; + public: MQSSClient(const std::string& token = "", const std::string& url_or_queue = MQP_DEFAULT_URL, bool is_hpc = false); +#ifdef ENABLE_UNIT_TEST + explicit MQSSClient(std::unique_ptr client) + : mClient(std::move(client)) {} +#endif + // Resources std::vector getAllResources() const; std::optional getResourceInfo(const std::string& resource) const; @@ -64,11 +71,7 @@ class MQSSClient { std::string getJobStatus(const JobRequest& job); std::unique_ptr getJobResult(const JobRequest& job, bool wait = false, size_t timeout = 100); - int getNumberPendingJobs(const std::string& resource) const; - -private: - std::unique_ptr mClient; }; } // namespace mqss::client diff --git a/src/client.cpp b/src/client.cpp index fc13c03..36eb940 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -27,7 +27,7 @@ #include using namespace mqss::client; - +// GCOVR_EXCL_START MQSSClient::MQSSClient(const std::string& token, const std::string& url_or_queue, bool is_hpc) { if (is_hpc) { @@ -36,6 +36,7 @@ MQSSClient::MQSSClient(const std::string& token, mClient = std::make_unique(token, url_or_queue); } } +// GCOVR_EXCL_STOP std::vector MQSSClient::getAllResources() const { std::vector Resources; diff --git a/src/job.cpp b/src/job.cpp index 80b0e1e..d5b38b0 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -54,6 +54,7 @@ nlohmann::json HamiltonianJobRequest::toJson() const { {"coefficients_str", mCoefficientsStr}}; } +// GCOVR_EXCL_START JobResult::JobResult(std::vector> results, std::string timestampCompleted, std::string timestampSubmitted, @@ -63,6 +64,8 @@ JobResult::JobResult(std::vector> results, mTimestampSubmitted(std::move(timestampSubmitted)), mTimestampScheduled(std::move(timestampScheduled)) {} +// GCOVR_EXCL_STOP + JobResult::JobResult(const nlohmann::json& parsed) { std::vector> results; diff --git a/src/resource.cpp b/src/resource.cpp index 117ecdf..735b6fe 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -63,8 +63,7 @@ std::vector extractGates(const nlohmann::json& response, const std::string& text = it->get_ref(); if (text == "None") return result; - std::regex gateRegex(R"(\(\s*'([^']+)'\s*,\s*\{([^}]*)\}\s*\))"); - + std::regex gateRegex(R"(\(\s*['"]([^'"]+)['"]\s*,\s*(\{[^{}]*\})\s*\))"); auto gateBegin = std::sregex_iterator(text.begin(), text.end(), gateRegex); auto gateEnd = std::sregex_iterator(); std::string GateName; @@ -126,7 +125,7 @@ Resource::Resource(const nlohmann::json& json) { std::vector> couplingMap = extractCouplingMap(json, "connectivity"); - std::vector nativeGateset = extractGates(json, "Gates"); + std::vector nativeGateset = extractGates(json, "instructions"); mName = std::move(name); mQubitCount = qubitCount; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9103115..ae13839 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,4 +16,24 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception enable_testing() -add_subdirectory(unit_tests) +include(GoogleTest) + +function(add_test test_name) + add_executable(${test_name} ${test_name}.cpp) + + target_link_libraries(${test_name} PRIVATE gmock gtest gtest_main mqss_client) + + if(ENABLE_COVERAGE) + target_link_libraries(${test_name} PRIVATE coverage_flags) + endif() + + gtest_discover_tests(${test_name}) +endfunction() + +if(BUILD_TESTS OR BUILD_UNIT_TESTS) + add_subdirectory(unit/c++) +endif() + +if(BUILD_TESTS OR BUILD_INTEGRATION_TESTS) + add_subdirectory(integration/c++) +endif() diff --git a/tests/integration/c++/CMakeLists.txt b/tests/integration/c++/CMakeLists.txt new file mode 100644 index 0000000..9136099 --- /dev/null +++ b/tests/integration/c++/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2024 - 2026 MQSS Project +# All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +add_test(job_integration_test) +add_test(resource_integration_test) diff --git a/tests/unit_tests/job_unit_test.cpp b/tests/integration/c++/job_integration_test.cpp similarity index 100% rename from tests/unit_tests/job_unit_test.cpp rename to tests/integration/c++/job_integration_test.cpp diff --git a/tests/unit_tests/resource_unit_test.cpp b/tests/integration/c++/resource_integration_test.cpp similarity index 100% rename from tests/unit_tests/resource_unit_test.cpp rename to tests/integration/c++/resource_integration_test.cpp diff --git a/tests/unit_tests/python/test_job.py b/tests/integration/python/job_integration_test.py similarity index 100% rename from tests/unit_tests/python/test_job.py rename to tests/integration/python/job_integration_test.py diff --git a/tests/unit_tests/python/test_resource.py b/tests/integration/python/resource_integration_test.py similarity index 100% rename from tests/unit_tests/python/test_resource.py rename to tests/integration/python/resource_integration_test.py diff --git a/tests/unit/c++/CMakeLists.txt b/tests/unit/c++/CMakeLists.txt new file mode 100644 index 0000000..7825802 --- /dev/null +++ b/tests/unit/c++/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2024 - 2026 MQSS Project +# All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +add_test(job_unit_test) +add_test(resource_unit_test) diff --git a/tests/unit/c++/job_unit_test.cpp b/tests/unit/c++/job_unit_test.cpp new file mode 100644 index 0000000..7929804 --- /dev/null +++ b/tests/unit/c++/job_unit_test.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "mock_server.h" +#include "mqss/client.h" + +#include +#include +#include +#include +#include + +namespace { + +constexpr char TEST_CIRCUIT[] = R"( +OPENQASM 2.0; +include "qelib1.inc"; +qreg q[2]; +creg c[2]; +h q[0]; +cx q[0], q[1]; +measure q -> c; +)"; + +constexpr char kSubmitSuccess[] = R"({ + "uuid":"12345" +})"; + +constexpr char kStatusCompleted[] = R"({ + "status":"COMPLETED" +})"; + +constexpr char kStatusRunning[] = R"({ + "status":"RUNNING" +})"; + +constexpr char kStatusFailed[] = R"({ + "status":"FAILED" +})"; + +constexpr char kResultArrayResponse[] = + R"({"result":"[{\"10\":100},{\"10\":200}]","timestamp_completed":"2026-05-26 11:26:07.615381","timestamp_scheduled":"2026-05-26 08:07:40.026325","timestamp_submitted":"2026-05-25 08:52:14.503964"})"; + +constexpr char kResultResponse[] = + R"({"result":"{\"10\":200}","timestamp_completed":"2026-05-26 11:26:07.615381","timestamp_scheduled":"2026-05-26 08:07:40.026325","timestamp_submitted":"2026-05-25 08:52:14.503964"})"; + +constexpr char kPendingJobsResponse[] = R"({ + "num_pending_jobs":42 +})"; + +} // namespace + +class MQSSClientTest : public ::testing::Test { +protected: + void SetUp() override { + mock_ = std::make_unique(); + mock_ptr_ = mock_.get(); + } + + MQSSClient createClient() { return MQSSClient(std::move(mock_)); } + + CircuitJobRequest createCircuitJob() { + return CircuitJobRequest(TEST_CIRCUIT, "qasm", "mock-resource", 100, 0, 0); + } + + HamiltonianJobRequest createHamiltonianJob() { + return HamiltonianJobRequest("mock-resource", "Z0 Z1\nX0 X1", "0.5\n0.3"); + } + + MockMQSSBaseClient* mock_ptr_{nullptr}; + std::unique_ptr mock_; +}; + +TEST_F(MQSSClientTest, SubmitJobSuccess) { + auto job = createCircuitJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return(kSubmitSuccess)); + + auto client = createClient(); + + auto uuid = client.submitJob(job); + + ASSERT_TRUE(uuid.has_value()); + EXPECT_EQ(*uuid, "12345"); + EXPECT_EQ(job.getUuid(), "12345"); +} + +TEST_F(MQSSClientTest, SubmitJobEmptyResponse) { + auto job = createCircuitJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return("")); + + auto client = createClient(); + + EXPECT_FALSE(client.submitJob(job).has_value()); +} + +TEST_F(MQSSClientTest, SubmitJobInvalidJson) { + auto job = createCircuitJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return("not-json")); + + auto client = createClient(); + + EXPECT_FALSE(client.submitJob(job).has_value()); +} + +TEST_F(MQSSClientTest, SubmitJobMissingUuid) { + auto job = createCircuitJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return(R"({"status":"ok"})")); + + auto client = createClient(); + + EXPECT_FALSE(client.submitJob(job).has_value()); +} + +TEST_F(MQSSClientTest, CancelJob) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, del(job.getPath() + "/12345")).Times(1); + + auto client = createClient(); + + client.cancelJob(job); +} + +TEST_F(MQSSClientTest, GetJobStatusCompleted) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(job.getPath() + "/12345/status")) + .WillOnce(Return(kStatusCompleted)); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), "COMPLETED"); +} + +TEST_F(MQSSClientTest, GetJobStatusFailed) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(job.getPath() + "/12345/status")) + .WillOnce(Return(kStatusFailed)); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), "FAILED"); +} + +TEST_F(MQSSClientTest, GetJobStatusEmptyResponse) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), ""); +} + +TEST_F(MQSSClientTest, GetJobStatusInvalidJson) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("bad-json")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), ""); +} + +TEST_F(MQSSClientTest, GetJobStatusMissingField) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return(R"({})")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), ""); +} + +TEST_F(MQSSClientTest, GetJobResultSuccess) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(job.getPath() + "/12345/status")) + .WillOnce(Return(kStatusCompleted)); + + EXPECT_CALL(*mock_ptr_, get(job.getPath() + "/12345/result")) + .WillOnce(Return(kResultResponse)); + + auto client = createClient(); + + auto result = client.getJobResult(job, true); + + ASSERT_NE(result, nullptr); +} + +TEST_F(MQSSClientTest, GetJobResultArraySuccess) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(job.getPath() + "/12345/status")) + .WillOnce(Return(kStatusCompleted)); + + EXPECT_CALL(*mock_ptr_, get(job.getPath() + "/12345/result")) + .WillOnce(Return(kResultArrayResponse)); + + auto client = createClient(); + + auto result = client.getJobResult(job, true); + + ASSERT_NE(result, nullptr); +} + +TEST_F(MQSSClientTest, GetJobResultEmptyResponse) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobResult(job, false), nullptr); +} + +TEST_F(MQSSClientTest, GetJobResultInvalidJson) { + auto job = createCircuitJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("invalid-json")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobResult(job, false), nullptr); +} + +TEST_F(MQSSClientTest, SubmitHamiltonianJobSuccess) { + auto job = createHamiltonianJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return(kSubmitSuccess)); + + auto client = createClient(); + + auto uuid = client.submitJob(job); + + ASSERT_TRUE(uuid.has_value()); + EXPECT_EQ(*uuid, "12345"); + EXPECT_EQ(job.getUuid(), "12345"); +} + +TEST_F(MQSSClientTest, SubmitHamiltonianJobEmptyResponse) { + auto job = createHamiltonianJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return("")); + + auto client = createClient(); + + EXPECT_FALSE(client.submitJob(job).has_value()); +} + +TEST_F(MQSSClientTest, SubmitHamiltonianJobInvalidJson) { + auto job = createHamiltonianJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return("not-json")); + + auto client = createClient(); + + EXPECT_FALSE(client.submitJob(job).has_value()); +} + +TEST_F(MQSSClientTest, SubmitHamiltonianJobMissingUuid) { + auto job = createHamiltonianJob(); + + EXPECT_CALL(*mock_ptr_, post(job.getPath(), job.toJson())) + .WillOnce(Return(R"({"status":"ok"})")); + + auto client = createClient(); + + EXPECT_FALSE(client.submitJob(job).has_value()); +} + +TEST_F(MQSSClientTest, CancelHamiltonianJob) { + auto job = createHamiltonianJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, del(job.getPath() + "/12345")).Times(1); + + auto client = createClient(); + + client.cancelJob(job); +} + +TEST_F(MQSSClientTest, GetHamiltonianJobStatusCompleted) { + auto job = createHamiltonianJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(job.getPath() + "/12345/status")) + .WillOnce(Return(kStatusCompleted)); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), "COMPLETED"); +} + +TEST_F(MQSSClientTest, GetHamiltonianJobStatusEmptyResponse) { + auto job = createHamiltonianJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), ""); +} + +TEST_F(MQSSClientTest, GetHamiltonianJobStatusInvalidJson) { + auto job = createHamiltonianJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("bad-json")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), ""); +} + +TEST_F(MQSSClientTest, GetHamiltonianJobStatusMissingField) { + auto job = createHamiltonianJob(); + job.setUuid("12345"); + + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return(R"({})")); + + auto client = createClient(); + + EXPECT_EQ(client.getJobStatus(job), ""); +} + +TEST_F(MQSSClientTest, GetNumberPendingJobsSuccess) { + EXPECT_CALL(*mock_ptr_, get("resources/mock-resource/num_pending_jobs")) + .WillOnce(Return(kPendingJobsResponse)); + + auto client = createClient(); + + EXPECT_EQ(client.getNumberPendingJobs("mock-resource"), 42); +} + +TEST_F(MQSSClientTest, GetNumberPendingJobsEmptyResponse) { + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("")); + + auto client = createClient(); + + EXPECT_EQ(client.getNumberPendingJobs("mock-resource"), -1); +} + +TEST_F(MQSSClientTest, GetNumberPendingJobsInvalidJson) { + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return("invalid-json")); + + auto client = createClient(); + + EXPECT_EQ(client.getNumberPendingJobs("mock-resource"), -1); +} + +TEST_F(MQSSClientTest, GetNumberPendingJobsMissingField) { + EXPECT_CALL(*mock_ptr_, get(_)).WillOnce(Return(R"({})")); + + auto client = createClient(); + + EXPECT_EQ(client.getNumberPendingJobs("mock-resource"), -1); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit/c++/mock_server.h b/tests/unit/c++/mock_server.h new file mode 100644 index 0000000..9704206 --- /dev/null +++ b/tests/unit/c++/mock_server.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "mqss/client.h" + +#include +#include + +using namespace mqss::client; +using ::testing::_; +using ::testing::HasSubstr; +using ::testing::Return; + +class MockMQSSBaseClient : public MQSSBaseClient { +public: + MOCK_METHOD(std::string, get, (const std::string& path), (override)); + + MOCK_METHOD(std::string, post, + (const std::string& path, const nlohmann::json& data), + (override)); + + MOCK_METHOD(void, del, (const std::string& path), (override)); +}; diff --git a/tests/unit/c++/resource_unit_test.cpp b/tests/unit/c++/resource_unit_test.cpp new file mode 100644 index 0000000..36773ca --- /dev/null +++ b/tests/unit/c++/resource_unit_test.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "mock_server.h" +#include "mqss/client.h" + +#include +#include +#include +#include +#include + +namespace { + +constexpr char kResourceResponse[] = R"({ + "connectivity":"[[0, 2], [2, 0], [1, 2], [2, 1], [2, 3], [3, 2], [2, 4], [4, 2]]", + "instructions":"[(\"r\",{(0,):None, (1,):None, (2,):None, (3,):None,(4,):None})]", + "name":"Fake-Resource2", + "online":false, + "qubits":5 +})"; + +constexpr char kAllResourcesResponse[] = R"({ + "Fake-Resource1":{ + "connectivity":"None", + "instructions":"None", + "name":"Fake-Resource1", + "online":false, + "qubits":20 + }, + "Fake-Resource2":{ + "connectivity":"[[0, 2], [2, 0], [1, 2], [2, 1], [2, 3], [3, 2], [2, 4], [4, 2]]", + "instructions":"[(\"r\",{(0,):None})]", + "name":"Fake-Resource2", + "online":false, + "qubits":5 + } +})"; + +constexpr char kNotFoundResponse[] = R"({"RESOURCE NOT FOUND"})"; + +constexpr char kErrorResponse[] = R"({"ERROR" : "Not Found"})"; + +constexpr char kNotAcceptedResponse[] = R"()"; + +} // namespace + +class MQSSClientTest : public ::testing::Test { +protected: + void SetUp() override { + mock_ = std::make_unique(); + mock_ptr_ = mock_.get(); + } + + MQSSClient createClient() { return MQSSClient(std::move(mock_)); } + + MockMQSSBaseClient* mock_ptr_{nullptr}; + std::unique_ptr mock_; +}; + +TEST_F(MQSSClientTest, GetAllResources) { + EXPECT_CALL(*mock_ptr_, get("resources")) + .WillOnce(Return(kAllResourcesResponse)); + + auto client = createClient(); + + auto resources = client.getAllResources(); + + ASSERT_EQ(resources.size(), 2u); + EXPECT_EQ(resources[0].getName(), "Fake-Resource1"); + EXPECT_EQ(resources[1].getName(), "Fake-Resource2"); +} + +TEST_F(MQSSClientTest, GetResourceInfoReturnsResource) { + EXPECT_CALL(*mock_ptr_, get("resources/Fake-Resource2")) + .WillOnce(Return(kResourceResponse)); + + auto client = createClient(); + + auto resource = client.getResourceInfo("Fake-Resource2"); + + ASSERT_TRUE(resource.has_value()); + EXPECT_EQ(resource->getName(), "Fake-Resource2"); +} + +TEST_F(MQSSClientTest, GetResourceInfoReturnsNulloptForUnknownResource) { + EXPECT_CALL(*mock_ptr_, get("resources/mock-resource")) + .WillOnce(Return(kNotFoundResponse)); + + auto client = createClient(); + + auto resource = client.getResourceInfo("mock-resource"); + + EXPECT_FALSE(resource.has_value()); +} + +TEST_F(MQSSClientTest, GetResourceInfoReturnsNotAccepted) { + EXPECT_CALL(*mock_ptr_, get("resources/mock-resource")) + .WillOnce(Return(kNotAcceptedResponse)); + + auto client = createClient(); + + auto resource = client.getResourceInfo("mock-resource"); + + EXPECT_FALSE(resource.has_value()); +} + +TEST_F(MQSSClientTest, GetResourceInfoReturnsError) { + EXPECT_CALL(*mock_ptr_, get("resources/mock-resource")) + .WillOnce(Return(kErrorResponse)); + + auto client = createClient(); + + auto resource = client.getResourceInfo("mock-resource"); + + EXPECT_FALSE(resource.has_value()); +} + +TEST_F(MQSSClientTest, ResourceFieldsAreParsedCorrectly) { + constexpr char kResourceName[] = "Fake-Resource2"; + + EXPECT_CALL(*mock_ptr_, get("resources/Fake-Resource2")) + .WillOnce(Return(kResourceResponse)); + + auto client = createClient(); + + auto resource = client.getResourceInfo(kResourceName); + + ASSERT_TRUE(resource.has_value()); + + EXPECT_EQ(resource->getName(), kResourceName); + EXPECT_EQ(resource->getQubitCount(), 5); + EXPECT_FALSE(resource->isOnline()); + + EXPECT_GT(resource->getCouplingMap().size(), 0); + EXPECT_GT(resource->getNativeGateset().size(), 0); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt deleted file mode 100644 index 3c118e6..0000000 --- a/tests/unit_tests/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2024 - 2026 MQSS Project -# All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. -# -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -include(GoogleTest) - -add_executable(resource_unit_test resource_unit_test.cpp) - -target_link_libraries(resource_unit_test PRIVATE gtest gtest_main mqss_client) -gtest_discover_tests(resource_unit_test) - -add_executable(job_unit_test job_unit_test.cpp) - -target_link_libraries(job_unit_test PRIVATE gtest gtest_main mqss_client) -gtest_discover_tests(job_unit_test) - -if(ENABLE_COVERAGE) - target_compile_options(resource_unit_test INTERFACE --coverage -fprofile-arcs -ftest-coverage -O0) - target_link_libraries(resource_unit_test INTERFACE gcov --coverage) - - target_compile_options(job_unit_test INTERFACE --coverage -fprofile-arcs -ftest-coverage -O0) - target_link_libraries(job_unit_test INTERFACE gcov --coverage) -endif()