parent
6a4a092c61
commit
692cab5651
@ -1,28 +1,28 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DINSTALL_GTEST=OFF -DBUILD_TESTING=OFF -DBUILD_CURL_TRANSPORT=ON",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": "",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "x86-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "msvc_x86" ],
|
||||
"variables": []
|
||||
}
|
||||
]
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DINSTALL_GTEST=OFF -DBUILD_TESTING=ON -DBUILD_CURL_TRANSPORT=ON -DBUILD_STORAGE_SAMPLES=ON",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": "",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "x86-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "msvc_x86" ],
|
||||
"variables": []
|
||||
}
|
||||
]
|
||||
}
|
||||
235
sdk/core/azure-core/inc/nullable.hpp
Normal file
235
sdk/core/azure-core/inc/nullable.hpp
Normal file
@ -0,0 +1,235 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib> // for abort
|
||||
#include <new> // for placement new
|
||||
#include <type_traits>
|
||||
#include <utility> // for swap and move
|
||||
|
||||
namespace Azure { namespace Core {
|
||||
namespace Details {
|
||||
struct NontrivialEmptyType
|
||||
{
|
||||
constexpr NontrivialEmptyType() noexcept {}
|
||||
};
|
||||
} // namespace Details
|
||||
|
||||
template <class T> class Nullable {
|
||||
union
|
||||
{
|
||||
Details::NontrivialEmptyType
|
||||
m_disengaged; // due to constexpr rules for the default constructor
|
||||
T m_value;
|
||||
};
|
||||
|
||||
bool m_hasValue;
|
||||
|
||||
public:
|
||||
constexpr Nullable() : m_disengaged{}, m_hasValue(false) {}
|
||||
constexpr Nullable(const T& initialValue) : m_value(initialValue), m_hasValue(true) {}
|
||||
|
||||
Nullable(const Nullable& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
|
||||
: m_hasValue(other.m_hasValue)
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
::new (static_cast<void*>(&m_value)) T(other.m_value);
|
||||
}
|
||||
}
|
||||
|
||||
Nullable(Nullable&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
: m_hasValue(other.m_hasValue)
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
::new (static_cast<void*>(&m_value)) T(std::move(other.m_value));
|
||||
}
|
||||
}
|
||||
|
||||
~Nullable()
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
m_value.~T();
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() noexcept /* enforces termination */
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
m_value.~T();
|
||||
m_hasValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
// this assumes that swap can't throw if T is nothrow move constructible because
|
||||
// is_nothrow_swappable is added in C++17
|
||||
void Swap(Nullable& other) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
if (other.m_hasValue)
|
||||
{
|
||||
using std::swap;
|
||||
swap(m_value, other.m_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
::new (static_cast<void*>(&other.m_value)) T(std::move(m_value)); // throws
|
||||
other.m_hasValue = true;
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
else if (other.m_hasValue)
|
||||
{
|
||||
::new (static_cast<void*>(&m_value)) T(std::move(other.m_value)); // throws
|
||||
m_hasValue = true;
|
||||
other.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Intentionally lowercase to follow the Swappable requirements
|
||||
// https://en.cppreference.com/w/cpp/named_req/Swappable
|
||||
//
|
||||
friend void swap(Nullable& lhs, Nullable& rhs) noexcept(
|
||||
std::is_nothrow_move_constructible<T>::value)
|
||||
{
|
||||
lhs.Swap(rhs);
|
||||
}
|
||||
|
||||
Nullable& operator=(const Nullable& other)
|
||||
{
|
||||
// this copy and swap may be inefficient for some Ts but
|
||||
// it's a lot less code than the standard implementation :)
|
||||
Nullable{other}.Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Nullable& operator=(Nullable&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
{
|
||||
// this move and swap may be inefficient for some Ts but
|
||||
// it's a lot less code than the standard implementation :)
|
||||
Nullable{std::move(other)}.Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
class U = T,
|
||||
typename std::enable_if<
|
||||
!std::is_same<
|
||||
Nullable,
|
||||
typename std::remove_cv<typename std::remove_reference<U>::type>::type>::
|
||||
value // Avoid repeated assignment
|
||||
&& !(
|
||||
std::is_scalar<U>::value
|
||||
&& std::is_same<T, typename std::decay<U>::type>::value) // Avoid repeated
|
||||
// assignment of
|
||||
// equivallent scaler
|
||||
// types
|
||||
&& std::is_constructible<T, U>::value // Ensure the type is constructible
|
||||
&& std::is_assignable<T&, U>::value, // Ensure the type is assignable
|
||||
int>::type
|
||||
= 0>
|
||||
Nullable& operator=(U&& other) noexcept(
|
||||
std::is_nothrow_constructible<T, U>::value&& std::is_nothrow_assignable<T&, U>::value)
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
m_value = std::forward<U>(other);
|
||||
}
|
||||
else
|
||||
{
|
||||
::new (static_cast<void*>(&m_value)) T(std::forward<U>(other));
|
||||
m_hasValue = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class... U>
|
||||
T& Emplace(U&&... Args) noexcept(std::is_nothrow_constructible<T, U...>::value)
|
||||
{
|
||||
Reset();
|
||||
::new (static_cast<void*>(&m_value)) T(std::forward<U>(Args)...);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool HasValue() const noexcept { return m_hasValue; }
|
||||
|
||||
const T& GetValue() const& noexcept
|
||||
{
|
||||
if (!m_hasValue)
|
||||
{
|
||||
// throwing here prohibited by our guidelines
|
||||
// https://azure.github.io/azure-sdk/cpp_design.html#pre-conditions
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return m_value;
|
||||
}
|
||||
|
||||
T& GetValue() & noexcept
|
||||
{
|
||||
if (!m_hasValue)
|
||||
{
|
||||
// throwing here prohibited by our guidelines
|
||||
// https://azure.github.io/azure-sdk/cpp_design.html#pre-conditions
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return m_value;
|
||||
}
|
||||
|
||||
T&& GetValue() && noexcept
|
||||
{
|
||||
if (!m_hasValue)
|
||||
{
|
||||
// throwing here prohibited by our guidelines
|
||||
// https://azure.github.io/azure-sdk/cpp_design.html#pre-conditions
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return std::move(m_value);
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept { return HasValue(); }
|
||||
|
||||
template <
|
||||
class U = T,
|
||||
typename std::enable_if<
|
||||
std::is_convertible_v<
|
||||
const T&,
|
||||
typename std::remove_cv<T>::type> && std::is_convertible_v<U, T>,
|
||||
int>::type
|
||||
= 0>
|
||||
constexpr typename std::remove_cv<T>::type ValueOr(U&& other) const&
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
return static_cast<typename std::remove_cv<T>::type>(std::forward<U>(other));
|
||||
}
|
||||
|
||||
template <
|
||||
class U = T,
|
||||
typename std::enable_if<
|
||||
std::is_convertible_v<
|
||||
T,
|
||||
typename std::remove_cv<T>::type> && std::is_convertible_v<U, T>,
|
||||
int>::type
|
||||
= 0>
|
||||
constexpr typename std::remove_cv<T>::type ValueOr(U&& other) &&
|
||||
{
|
||||
if (m_hasValue)
|
||||
{
|
||||
return std::move(m_value);
|
||||
}
|
||||
|
||||
return static_cast<typename std::remove_cv<T>::type>(std::forward<U>(other));
|
||||
}
|
||||
};
|
||||
}} // namespace Azure::Core
|
||||
@ -11,8 +11,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
add_executable (
|
||||
${TARGET_NAME}
|
||||
main.cpp
|
||||
)
|
||||
main.cpp
|
||||
nullable.cpp
|
||||
http.cpp)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE azure-core)
|
||||
add_gtest(${TARGET_NAME})
|
||||
|
||||
326
sdk/core/azure-core/test/http.cpp
Normal file
326
sdk/core/azure-core/test/http.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <http/http.hpp>
|
||||
#include <internal/credentials_internal.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace Azure::Core;
|
||||
|
||||
TEST(Http_Request, getters)
|
||||
{
|
||||
Http::HttpMethod httpMethod = Http::HttpMethod::Get;
|
||||
std::string url = "http://test.url.com";
|
||||
Http::Request req(httpMethod, url);
|
||||
|
||||
// EXPECT_PRED works better than just EQ because it will print values in log
|
||||
EXPECT_PRED2(
|
||||
[](Http::HttpMethod a, Http::HttpMethod b) { return a == b; }, req.GetMethod(), httpMethod);
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, req.GetEncodedUrl(), url);
|
||||
|
||||
EXPECT_NO_THROW(req.AddHeader("name", "value"));
|
||||
EXPECT_NO_THROW(req.AddHeader("name2", "value2"));
|
||||
|
||||
auto headers = req.GetHeaders();
|
||||
|
||||
EXPECT_TRUE(headers.count("name"));
|
||||
EXPECT_TRUE(headers.count("name2"));
|
||||
EXPECT_FALSE(headers.count("newHeader"));
|
||||
|
||||
auto value = headers.find("name");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value->second, "value");
|
||||
auto value2 = headers.find("name2");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value2->second, "value2");
|
||||
|
||||
// now add to retry headers
|
||||
req.StartRetry();
|
||||
// same headers first, then one new
|
||||
EXPECT_NO_THROW(req.AddHeader("name", "retryValue"));
|
||||
EXPECT_NO_THROW(req.AddHeader("name2", "retryValue2"));
|
||||
EXPECT_NO_THROW(req.AddHeader("newHeader", "new"));
|
||||
|
||||
headers = req.GetHeaders();
|
||||
|
||||
EXPECT_TRUE(headers.count("name"));
|
||||
EXPECT_TRUE(headers.count("name2"));
|
||||
EXPECT_TRUE(headers.count("newHeader"));
|
||||
|
||||
value = headers.find("name");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value->second, "retryValue");
|
||||
value2 = headers.find("name2");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value2->second, "retryValue2");
|
||||
auto value3 = headers.find("newHeader");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value3->second, "new");
|
||||
}
|
||||
|
||||
TEST(Http_Request, query_parameter)
|
||||
{
|
||||
Http::HttpMethod httpMethod = Http::HttpMethod::Put;
|
||||
std::string url = "http://test.com";
|
||||
Http::Request req(httpMethod, url);
|
||||
|
||||
EXPECT_NO_THROW(req.AddQueryParameter("query", "value"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "?query=value");
|
||||
|
||||
std::string url_with_query = "http://test.com?query=1";
|
||||
Http::Request req_with_query(httpMethod, url_with_query);
|
||||
|
||||
// ignore if adding same query parameter key that is already in url
|
||||
EXPECT_NO_THROW(req_with_query.AddQueryParameter("query", "value"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req_with_query.GetEncodedUrl(),
|
||||
url_with_query);
|
||||
|
||||
// retry query params testing
|
||||
req.StartRetry();
|
||||
// same query parameter should override previous
|
||||
EXPECT_NO_THROW(req.AddQueryParameter("query", "retryValue"));
|
||||
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "?query=retryValue");
|
||||
}
|
||||
|
||||
TEST(Http_Request, add_path)
|
||||
{
|
||||
Http::HttpMethod httpMethod = Http::HttpMethod::Post;
|
||||
std::string url = "http://test.com";
|
||||
Http::Request req(httpMethod, url);
|
||||
|
||||
EXPECT_NO_THROW(req.AppendPath("path"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; }, req.GetEncodedUrl(), url + "/path");
|
||||
|
||||
EXPECT_NO_THROW(req.AddQueryParameter("query", "value"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "/path?query=value");
|
||||
|
||||
EXPECT_NO_THROW(req.AppendPath("path2"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "/path/path2?query=value");
|
||||
|
||||
EXPECT_NO_THROW(req.AppendPath("path3"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "/path/path2/path3?query=value");
|
||||
}
|
||||
|
||||
class Azure::Core::Credentials::Details::CredentialTest : public ClientSecretCredential {
|
||||
public:
|
||||
CredentialTest(
|
||||
std::string const& tenantId,
|
||||
std::string const& clientId,
|
||||
std::string const& clientSecret)
|
||||
: ClientSecretCredential(tenantId, clientId, clientSecret)
|
||||
{
|
||||
}
|
||||
|
||||
std::string NewTokenString;
|
||||
std::chrono::system_clock::time_point NewExpiration;
|
||||
bool IsExpired;
|
||||
|
||||
std::string GetTenantId() const
|
||||
{
|
||||
return this->ClientSecretCredential::m_clientSecret->m_tenantId;
|
||||
}
|
||||
|
||||
std::string GetClientId() const
|
||||
{
|
||||
return this->ClientSecretCredential::m_clientSecret->m_clientId;
|
||||
}
|
||||
|
||||
std::string GetClientSecret() const
|
||||
{
|
||||
return this->ClientSecretCredential::m_clientSecret->m_clientSecret;
|
||||
}
|
||||
|
||||
std::string GetScopes() const { return this->ClientSecretCredential::m_clientSecret->m_scopes; }
|
||||
|
||||
bool IsTokenPtrNull() const { return !this->TokenCredential::m_token; }
|
||||
|
||||
std::string GetTokenString() const { return this->TokenCredential::m_token->m_tokenString; }
|
||||
|
||||
std::chrono::system_clock::time_point GetExpiration() const
|
||||
{
|
||||
return this->TokenCredential::m_token->m_expiresAt;
|
||||
}
|
||||
|
||||
private:
|
||||
void RefreshToken(
|
||||
std::string& newTokenString,
|
||||
std::chrono::system_clock::time_point& newExpiration) override
|
||||
{
|
||||
newTokenString = this->NewTokenString;
|
||||
newExpiration = this->NewExpiration;
|
||||
}
|
||||
|
||||
bool IsTokenExpired(std::chrono::system_clock::time_point const&) const override
|
||||
{
|
||||
return this->IsExpired;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Credential, ClientSecretCredential)
|
||||
{
|
||||
// Client Secret credential properties
|
||||
std::string const tenantId = "tenantId";
|
||||
std::string const clientId = "clientId";
|
||||
std::string const clientSecret = "clientSecret";
|
||||
|
||||
Credentials::Details::CredentialTest clientSecretCredential(tenantId, clientId, clientSecret);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
|
||||
|
||||
// Token credential
|
||||
{
|
||||
auto const emptyString = std::string();
|
||||
auto const defaultTime = std::chrono::system_clock::time_point();
|
||||
{
|
||||
// Default values
|
||||
{
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
|
||||
}
|
||||
|
||||
{
|
||||
// Set scopes
|
||||
std::string const scopes = "scope";
|
||||
{
|
||||
Credentials::Credential::Internal::SetScopes(clientSecretCredential, scopes);
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
|
||||
}
|
||||
|
||||
// Get token
|
||||
{
|
||||
std::string const olderToken = "olderToken";
|
||||
std::string const newToken = "newToken";
|
||||
auto const olderTime = defaultTime + std::chrono::minutes(10);
|
||||
auto const newTime = olderTime + std::chrono::minutes(10);
|
||||
|
||||
{
|
||||
clientSecretCredential.IsExpired = true;
|
||||
clientSecretCredential.NewTokenString = olderToken;
|
||||
clientSecretCredential.NewExpiration = olderTime;
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), false);
|
||||
EXPECT_EQ(tokenReceived, olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), olderTime);
|
||||
}
|
||||
|
||||
// Attemp to get the token when it is not expired yet
|
||||
{
|
||||
clientSecretCredential.IsExpired = false;
|
||||
clientSecretCredential.NewTokenString = newToken;
|
||||
clientSecretCredential.NewExpiration = newTime;
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), olderTime);
|
||||
}
|
||||
|
||||
// Attempt to get token after it expired
|
||||
{
|
||||
clientSecretCredential.IsExpired = true;
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
|
||||
|
||||
clientSecretCredential.IsExpired = false;
|
||||
}
|
||||
|
||||
// Setting the very same scopes set earlier does not reset token
|
||||
{
|
||||
std::string const scopesCopy
|
||||
= scopes.substr(0, scopes.length() / 2) + scopes.substr(scopes.length() / 2);
|
||||
|
||||
{
|
||||
auto const scopesPtr = scopes.c_str();
|
||||
auto const scopesCopyPtr = scopesCopy.c_str();
|
||||
EXPECT_NE(scopesPtr, scopesCopyPtr);
|
||||
EXPECT_EQ(scopes, scopesCopy);
|
||||
}
|
||||
|
||||
Credentials::Credential::Internal::SetScopes(clientSecretCredential, scopesCopy);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
|
||||
}
|
||||
|
||||
// Updating scopes does reset the token
|
||||
{
|
||||
clientSecretCredential.IsExpired = false;
|
||||
|
||||
std::string const anotherScopes = "anotherScopes";
|
||||
std::string const anotherToken = "anotherToken";
|
||||
auto const anotherTime = newTime + std::chrono::minutes(10);
|
||||
|
||||
clientSecretCredential.NewTokenString = anotherToken;
|
||||
clientSecretCredential.NewExpiration = anotherTime;
|
||||
|
||||
auto tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
|
||||
|
||||
Credentials::Credential::Internal::SetScopes(
|
||||
clientSecretCredential, std::string(anotherScopes));
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), anotherScopes);
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
|
||||
|
||||
tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), false);
|
||||
EXPECT_EQ(tokenReceived, anotherToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), anotherToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), anotherScopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), anotherTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,325 +2,9 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <http/http.hpp>
|
||||
#include <internal/credentials_internal.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace Azure::Core;
|
||||
|
||||
TEST(Http_Request, getters)
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Http::HttpMethod httpMethod = Http::HttpMethod::Get;
|
||||
std::string url = "http://test.url.com";
|
||||
Http::Request req(httpMethod, url);
|
||||
|
||||
// EXPECT_PRED works better than just EQ because it will print values in log
|
||||
EXPECT_PRED2(
|
||||
[](Http::HttpMethod a, Http::HttpMethod b) { return a == b; }, req.GetMethod(), httpMethod);
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, req.GetEncodedUrl(), url);
|
||||
|
||||
EXPECT_NO_THROW(req.AddHeader("name", "value"));
|
||||
EXPECT_NO_THROW(req.AddHeader("name2", "value2"));
|
||||
|
||||
auto headers = req.GetHeaders();
|
||||
|
||||
EXPECT_TRUE(headers.count("name"));
|
||||
EXPECT_TRUE(headers.count("name2"));
|
||||
EXPECT_FALSE(headers.count("newHeader"));
|
||||
|
||||
auto value = headers.find("name");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value->second, "value");
|
||||
auto value2 = headers.find("name2");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value2->second, "value2");
|
||||
|
||||
// now add to retry headers
|
||||
req.StartRetry();
|
||||
// same headers first, then one new
|
||||
EXPECT_NO_THROW(req.AddHeader("name", "retryValue"));
|
||||
EXPECT_NO_THROW(req.AddHeader("name2", "retryValue2"));
|
||||
EXPECT_NO_THROW(req.AddHeader("newHeader", "new"));
|
||||
|
||||
headers = req.GetHeaders();
|
||||
|
||||
EXPECT_TRUE(headers.count("name"));
|
||||
EXPECT_TRUE(headers.count("name2"));
|
||||
EXPECT_TRUE(headers.count("newHeader"));
|
||||
|
||||
value = headers.find("name");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value->second, "retryValue");
|
||||
value2 = headers.find("name2");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value2->second, "retryValue2");
|
||||
auto value3 = headers.find("newHeader");
|
||||
EXPECT_PRED2([](std::string a, std::string b) { return a == b; }, value3->second, "new");
|
||||
}
|
||||
|
||||
TEST(Http_Request, query_parameter)
|
||||
{
|
||||
Http::HttpMethod httpMethod = Http::HttpMethod::Put;
|
||||
std::string url = "http://test.com";
|
||||
Http::Request req(httpMethod, url);
|
||||
|
||||
EXPECT_NO_THROW(req.AddQueryParameter("query", "value"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "?query=value");
|
||||
|
||||
std::string url_with_query = "http://test.com?query=1";
|
||||
Http::Request req_with_query(httpMethod, url_with_query);
|
||||
|
||||
// ignore if adding same query parameter key that is already in url
|
||||
EXPECT_NO_THROW(req_with_query.AddQueryParameter("query", "value"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req_with_query.GetEncodedUrl(),
|
||||
url_with_query);
|
||||
|
||||
// retry query params testing
|
||||
req.StartRetry();
|
||||
// same query parameter should override previous
|
||||
EXPECT_NO_THROW(req.AddQueryParameter("query", "retryValue"));
|
||||
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "?query=retryValue");
|
||||
}
|
||||
|
||||
TEST(Http_Request, add_path)
|
||||
{
|
||||
Http::HttpMethod httpMethod = Http::HttpMethod::Post;
|
||||
std::string url = "http://test.com";
|
||||
Http::Request req(httpMethod, url);
|
||||
|
||||
EXPECT_NO_THROW(req.AppendPath("path"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; }, req.GetEncodedUrl(), url + "/path");
|
||||
|
||||
EXPECT_NO_THROW(req.AddQueryParameter("query", "value"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "/path?query=value");
|
||||
|
||||
EXPECT_NO_THROW(req.AppendPath("path2"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "/path/path2?query=value");
|
||||
|
||||
EXPECT_NO_THROW(req.AppendPath("path3"));
|
||||
EXPECT_PRED2(
|
||||
[](std::string a, std::string b) { return a == b; },
|
||||
req.GetEncodedUrl(),
|
||||
url + "/path/path2/path3?query=value");
|
||||
}
|
||||
|
||||
class Azure::Core::Credentials::Details::CredentialTest : public ClientSecretCredential {
|
||||
public:
|
||||
CredentialTest(
|
||||
std::string const& tenantId,
|
||||
std::string const& clientId,
|
||||
std::string const& clientSecret)
|
||||
: ClientSecretCredential(tenantId, clientId, clientSecret)
|
||||
{
|
||||
}
|
||||
|
||||
std::string NewTokenString;
|
||||
std::chrono::system_clock::time_point NewExpiration;
|
||||
bool IsExpired;
|
||||
|
||||
std::string GetTenantId() const
|
||||
{
|
||||
return this->ClientSecretCredential::m_clientSecret->m_tenantId;
|
||||
}
|
||||
|
||||
std::string GetClientId() const
|
||||
{
|
||||
return this->ClientSecretCredential::m_clientSecret->m_clientId;
|
||||
}
|
||||
|
||||
std::string GetClientSecret() const
|
||||
{
|
||||
return this->ClientSecretCredential::m_clientSecret->m_clientSecret;
|
||||
}
|
||||
|
||||
std::string GetScopes() const { return this->ClientSecretCredential::m_clientSecret->m_scopes; }
|
||||
|
||||
bool IsTokenPtrNull() const { return !this->TokenCredential::m_token; }
|
||||
|
||||
std::string GetTokenString() const { return this->TokenCredential::m_token->m_tokenString; }
|
||||
|
||||
std::chrono::system_clock::time_point GetExpiration() const
|
||||
{
|
||||
return this->TokenCredential::m_token->m_expiresAt;
|
||||
}
|
||||
|
||||
private:
|
||||
void RefreshToken(
|
||||
std::string& newTokenString,
|
||||
std::chrono::system_clock::time_point& newExpiration) override
|
||||
{
|
||||
newTokenString = this->NewTokenString;
|
||||
newExpiration = this->NewExpiration;
|
||||
}
|
||||
|
||||
bool IsTokenExpired(std::chrono::system_clock::time_point const&) const override
|
||||
{
|
||||
return this->IsExpired;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Credential, ClientSecretCredential)
|
||||
{
|
||||
// Client Secret credential properties
|
||||
std::string const tenantId = "tenantId";
|
||||
std::string const clientId = "clientId";
|
||||
std::string const clientSecret = "clientSecret";
|
||||
|
||||
Credentials::Details::CredentialTest clientSecretCredential(tenantId, clientId, clientSecret);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
|
||||
|
||||
// Token credential
|
||||
{
|
||||
auto const emptyString = std::string();
|
||||
auto const defaultTime = std::chrono::system_clock::time_point();
|
||||
{
|
||||
// Default values
|
||||
{
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
|
||||
}
|
||||
|
||||
{
|
||||
// Set scopes
|
||||
std::string const scopes = "scope";
|
||||
{
|
||||
Credentials::Credential::Internal::SetScopes(clientSecretCredential, scopes);
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
|
||||
}
|
||||
|
||||
// Get token
|
||||
{
|
||||
std::string const olderToken = "olderToken";
|
||||
std::string const newToken = "newToken";
|
||||
auto const olderTime = defaultTime + std::chrono::minutes(10);
|
||||
auto const newTime = olderTime + std::chrono::minutes(10);
|
||||
|
||||
{
|
||||
clientSecretCredential.IsExpired = true;
|
||||
clientSecretCredential.NewTokenString = olderToken;
|
||||
clientSecretCredential.NewExpiration = olderTime;
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), false);
|
||||
EXPECT_EQ(tokenReceived, olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), olderTime);
|
||||
}
|
||||
|
||||
// Attemp to get the token when it is not expired yet
|
||||
{
|
||||
clientSecretCredential.IsExpired = false;
|
||||
clientSecretCredential.NewTokenString = newToken;
|
||||
clientSecretCredential.NewExpiration = newTime;
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), olderToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), olderTime);
|
||||
}
|
||||
|
||||
// Attempt to get token after it expired
|
||||
{
|
||||
clientSecretCredential.IsExpired = true;
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
|
||||
|
||||
clientSecretCredential.IsExpired = false;
|
||||
}
|
||||
|
||||
// Setting the very same scopes set earlier does not reset token
|
||||
{
|
||||
std::string const scopesCopy
|
||||
= scopes.substr(0, scopes.length() / 2) + scopes.substr(scopes.length() / 2);
|
||||
|
||||
{
|
||||
auto const scopesPtr = scopes.c_str();
|
||||
auto const scopesCopyPtr = scopesCopy.c_str();
|
||||
EXPECT_NE(scopesPtr, scopesCopyPtr);
|
||||
EXPECT_EQ(scopes, scopesCopy);
|
||||
}
|
||||
|
||||
Credentials::Credential::Internal::SetScopes(clientSecretCredential, scopesCopy);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
|
||||
|
||||
auto const tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
|
||||
}
|
||||
|
||||
// Updating scopes does reset the token
|
||||
{
|
||||
clientSecretCredential.IsExpired = false;
|
||||
|
||||
std::string const anotherScopes = "anotherScopes";
|
||||
std::string const anotherToken = "anotherToken";
|
||||
auto const anotherTime = newTime + std::chrono::minutes(10);
|
||||
|
||||
clientSecretCredential.NewTokenString = anotherToken;
|
||||
clientSecretCredential.NewExpiration = anotherTime;
|
||||
|
||||
auto tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(tokenReceived, newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
|
||||
|
||||
Credentials::Credential::Internal::SetScopes(
|
||||
clientSecretCredential, std::string(anotherScopes));
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
|
||||
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), anotherScopes);
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
|
||||
|
||||
tokenReceived
|
||||
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
|
||||
|
||||
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), false);
|
||||
EXPECT_EQ(tokenReceived, anotherToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetTokenString(), anotherToken);
|
||||
EXPECT_EQ(clientSecretCredential.GetScopes(), anotherScopes);
|
||||
EXPECT_EQ(clientSecretCredential.GetExpiration(), anotherTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
176
sdk/core/azure-core/test/nullable.cpp
Normal file
176
sdk/core/azure-core/test/nullable.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <nullable.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace Azure::Core;
|
||||
|
||||
TEST(Nullable, Basic)
|
||||
{
|
||||
Nullable<std::string> testString{"hello world"};
|
||||
EXPECT_TRUE(testString.HasValue());
|
||||
EXPECT_TRUE(testString.GetValue() == "hello world");
|
||||
|
||||
Nullable<int> testInt{54321};
|
||||
EXPECT_TRUE(testInt.HasValue());
|
||||
EXPECT_TRUE(testInt.GetValue() == 54321);
|
||||
|
||||
Nullable<double> testDouble{10.0};
|
||||
EXPECT_TRUE(testDouble.HasValue());
|
||||
EXPECT_TRUE(testDouble.GetValue() == 10.0);
|
||||
}
|
||||
|
||||
TEST(Nullable, Empty)
|
||||
{
|
||||
Nullable<std::string> testString{};
|
||||
EXPECT_FALSE(testString.HasValue());
|
||||
EXPECT_TRUE(!testString);
|
||||
|
||||
Nullable<std::string> testString2;
|
||||
EXPECT_FALSE(testString2.HasValue());
|
||||
EXPECT_TRUE(!testString2);
|
||||
|
||||
Nullable<int> testInt{};
|
||||
EXPECT_FALSE(testInt.HasValue());
|
||||
EXPECT_TRUE(!testInt);
|
||||
|
||||
Nullable<int> testInt2;
|
||||
EXPECT_FALSE(testInt2.HasValue());
|
||||
EXPECT_TRUE(!testInt2);
|
||||
|
||||
Nullable<double> testDouble{};
|
||||
EXPECT_FALSE(testDouble.HasValue());
|
||||
EXPECT_TRUE(!testDouble);
|
||||
|
||||
Nullable<double> testDouble2;
|
||||
EXPECT_FALSE(testDouble2.HasValue());
|
||||
EXPECT_TRUE(!testDouble2);
|
||||
}
|
||||
|
||||
TEST(Nullable, Assignment)
|
||||
{
|
||||
Nullable<std::string> instance{"hello world"};
|
||||
auto instance2 = instance;
|
||||
EXPECT_TRUE(instance2.HasValue());
|
||||
EXPECT_TRUE(instance2.GetValue() == "hello world");
|
||||
|
||||
auto instance3 = std::move(instance);
|
||||
EXPECT_TRUE(instance3.HasValue());
|
||||
EXPECT_TRUE(instance3.GetValue() == "hello world");
|
||||
|
||||
EXPECT_TRUE(instance.HasValue());
|
||||
|
||||
// This is not a guarantee that the string will be empty
|
||||
// It is an implementation detail that the contents are moved
|
||||
// Should a future compiler change this assumption this test will need updates
|
||||
EXPECT_TRUE(instance.GetValue() == "");
|
||||
EXPECT_TRUE(instance.HasValue());
|
||||
}
|
||||
|
||||
TEST(Nullable, ValueAssignment)
|
||||
{
|
||||
Nullable<int> intVal;
|
||||
EXPECT_FALSE(intVal.HasValue());
|
||||
intVal = 7;
|
||||
EXPECT_TRUE(intVal.HasValue());
|
||||
EXPECT_TRUE(intVal.GetValue() == 7);
|
||||
|
||||
Nullable<double> doubleVal;
|
||||
EXPECT_FALSE(doubleVal.HasValue());
|
||||
doubleVal = 10.12345;
|
||||
EXPECT_TRUE(doubleVal.HasValue());
|
||||
EXPECT_TRUE(doubleVal.GetValue() == 10.12345);
|
||||
|
||||
Nullable<std::string> strVal;
|
||||
EXPECT_FALSE(strVal.HasValue());
|
||||
strVal = std::string("Hello World");
|
||||
EXPECT_TRUE(strVal.HasValue());
|
||||
EXPECT_TRUE(strVal.GetValue() == "Hello World");
|
||||
|
||||
strVal = "New String";
|
||||
EXPECT_TRUE(strVal.GetValue() == "New String");
|
||||
|
||||
strVal.Reset();
|
||||
EXPECT_FALSE(strVal.HasValue());
|
||||
}
|
||||
|
||||
TEST(Nullable, Swap)
|
||||
{
|
||||
Nullable<int> val1;
|
||||
Nullable<int> val2;
|
||||
Nullable<int> val3(12345);
|
||||
Nullable<int> val4(678910);
|
||||
|
||||
val1.Swap(val2);
|
||||
EXPECT_FALSE(val1);
|
||||
EXPECT_FALSE(val2);
|
||||
|
||||
val3.Swap(val4);
|
||||
EXPECT_TRUE(val3);
|
||||
EXPECT_TRUE(val4);
|
||||
EXPECT_TRUE(val3.GetValue() == 678910);
|
||||
EXPECT_TRUE(val4.GetValue() == 12345);
|
||||
|
||||
val1.Swap(val3);
|
||||
EXPECT_TRUE(val1);
|
||||
EXPECT_FALSE(val3);
|
||||
EXPECT_TRUE(val1.GetValue() == 678910);
|
||||
EXPECT_FALSE(val3.HasValue());
|
||||
}
|
||||
|
||||
TEST(Nullable, CopyConstruction)
|
||||
{
|
||||
//Empty
|
||||
Nullable<int> val1;
|
||||
Nullable<int> val2(val1);
|
||||
EXPECT_FALSE(val1);
|
||||
EXPECT_FALSE(val2);
|
||||
|
||||
//Non-Empty
|
||||
Nullable<int> val3(12345);
|
||||
Nullable<int> val4(val3);
|
||||
EXPECT_TRUE(val3);
|
||||
EXPECT_TRUE(val4);
|
||||
EXPECT_TRUE(val3.GetValue() == 12345);
|
||||
EXPECT_TRUE(val4.GetValue() == 12345);
|
||||
|
||||
//Literal
|
||||
Nullable<int> val5 = 54321;
|
||||
EXPECT_TRUE(val5);
|
||||
EXPECT_TRUE(val5.GetValue() == 54321);
|
||||
|
||||
//Value
|
||||
const int i = 1;
|
||||
Nullable<int> val6(i);
|
||||
EXPECT_TRUE(val6);
|
||||
EXPECT_TRUE(val6.GetValue() == 1);
|
||||
|
||||
}
|
||||
|
||||
TEST(Nullable, Disengage)
|
||||
{
|
||||
Nullable<int> val1(12345);
|
||||
val1.Reset();
|
||||
EXPECT_FALSE(val1);
|
||||
}
|
||||
|
||||
TEST(Nullable, ValueOr)
|
||||
{
|
||||
Nullable<int> val1(12345);
|
||||
Nullable<int> val2;
|
||||
|
||||
EXPECT_TRUE(val1);
|
||||
EXPECT_TRUE(val1.ValueOr(678910) == 12345);
|
||||
// Ensure the value was unmodified in ValueOr
|
||||
EXPECT_TRUE(val1.GetValue() == 12345);
|
||||
|
||||
EXPECT_FALSE(val2);
|
||||
EXPECT_TRUE(val2.ValueOr(678910) == 678910);
|
||||
// Ensure val2 is still disengaged after call to ValueOr
|
||||
EXPECT_FALSE(val2);
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user