[Core] - Support azure-core version without OpenSSL dependency (#2839)
* perf test uuid * use win impl for uuid on linux * use c99 base64 encode-decode
This commit is contained in:
parent
fe42c0f831
commit
3be793d558
@ -16,6 +16,9 @@
|
||||
|
||||
### Other Changes
|
||||
|
||||
- Updated `base64` implementation to remove external dependency.
|
||||
- Updated `Uuid` implementation for Linux to remove external dependency.
|
||||
|
||||
## 1.2.1 (2021-09-02)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
@ -148,6 +148,7 @@ target_link_libraries(azure-core INTERFACE Threads::Threads)
|
||||
if(WIN32)
|
||||
target_link_libraries(azure-core PRIVATE bcrypt crypt32)
|
||||
else()
|
||||
# Required for Hashing ( md5 and sha ).
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(azure-core PRIVATE OpenSSL::SSL)
|
||||
endif()
|
||||
|
||||
@ -4,93 +4,474 @@
|
||||
#include "azure/core/base64.hpp"
|
||||
#include "azure/core/platform.hpp"
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
#include <windows.h>
|
||||
#elif defined(AZ_PLATFORM_POSIX)
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/buffer.h>
|
||||
#include <openssl/evp.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
static char const Base64EncodeArray[65]
|
||||
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
static char const EncodingPad = '=';
|
||||
static int8_t const Base64DecodeArray[256] = {
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
62,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
63, // 62 is placed at index 43 (for +), 63 at index 47 (for /)
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
55,
|
||||
56,
|
||||
57,
|
||||
58,
|
||||
59,
|
||||
60,
|
||||
61,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1, // 52-61 are placed at index 48-57 (for 0-9), 64 at index 61 (for =)
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1, // 0-25 are placed at index 65-90 (for A-Z)
|
||||
-1,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
45,
|
||||
46,
|
||||
47,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1, // 26-51 are placed at index 97-122 (for a-z)
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1, // Bytes over 122 ('z') are invalid and cannot be decoded. Hence, padding the map with -1,
|
||||
// which indicates invalid input
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
};
|
||||
|
||||
static int32_t Base64Encode(const uint8_t* threeBytes)
|
||||
{
|
||||
int32_t i = (threeBytes[0] << 16) | (threeBytes[1] << 8) | threeBytes[2];
|
||||
|
||||
int32_t i0 = Base64EncodeArray[i >> 18];
|
||||
int32_t i1 = Base64EncodeArray[(i >> 12) & 0x3F];
|
||||
int32_t i2 = Base64EncodeArray[(i >> 6) & 0x3F];
|
||||
int32_t i3 = Base64EncodeArray[i & 0x3F];
|
||||
|
||||
return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24);
|
||||
}
|
||||
|
||||
static int32_t Base64EncodeAndPadOne(const uint8_t* twoBytes)
|
||||
{
|
||||
int32_t i = twoBytes[0] << 16 | (twoBytes[1] << 8);
|
||||
|
||||
int32_t i0 = Base64EncodeArray[i >> 18];
|
||||
int32_t i1 = Base64EncodeArray[(i >> 12) & 0x3F];
|
||||
int32_t i2 = Base64EncodeArray[(i >> 6) & 0x3F];
|
||||
|
||||
return i0 | (i1 << 8) | (i2 << 16) | (EncodingPad << 24);
|
||||
}
|
||||
|
||||
static int32_t Base64EncodeAndPadTwo(const uint8_t* oneByte)
|
||||
{
|
||||
int32_t i = oneByte[0] << 8;
|
||||
|
||||
int32_t i0 = Base64EncodeArray[i >> 10];
|
||||
int32_t i1 = Base64EncodeArray[(i >> 4) & 0x3F];
|
||||
|
||||
return i0 | (i1 << 8) | (EncodingPad << 16) | (EncodingPad << 24);
|
||||
}
|
||||
|
||||
static void Base64WriteIntAsFourBytes(char* destination, int32_t value)
|
||||
{
|
||||
destination[3] = static_cast<uint8_t>((value >> 24) & 0xFF);
|
||||
destination[2] = static_cast<uint8_t>((value >> 16) & 0xFF);
|
||||
destination[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
|
||||
destination[0] = static_cast<uint8_t>(value & 0xFF);
|
||||
}
|
||||
|
||||
std::string Base64Encode(const std::vector<uint8_t>& data)
|
||||
{
|
||||
size_t sourceIndex = 0;
|
||||
auto inputSize = data.size();
|
||||
auto maxEncodedSize = ((inputSize + 2) / 3) * 4;
|
||||
// Use a string with size to the max possible result
|
||||
std::string encodedResult(maxEncodedSize, '0');
|
||||
// Removing const from the string to update the placeholder string
|
||||
auto destination = const_cast<char*>(encodedResult.data());
|
||||
|
||||
while (sourceIndex + 3 <= inputSize)
|
||||
{
|
||||
int32_t result = Base64Encode(&data[sourceIndex]);
|
||||
Base64WriteIntAsFourBytes(destination, result);
|
||||
destination += 4;
|
||||
sourceIndex += 3;
|
||||
}
|
||||
|
||||
if (sourceIndex + 1 == inputSize)
|
||||
{
|
||||
int32_t result = Base64EncodeAndPadTwo(&data[sourceIndex]);
|
||||
Base64WriteIntAsFourBytes(destination, result);
|
||||
destination += 4;
|
||||
sourceIndex += 1;
|
||||
}
|
||||
else if (sourceIndex + 2 == inputSize)
|
||||
{
|
||||
int32_t result = Base64EncodeAndPadOne(&data[sourceIndex]);
|
||||
Base64WriteIntAsFourBytes(destination, result);
|
||||
destination += 4;
|
||||
sourceIndex += 2;
|
||||
}
|
||||
|
||||
return encodedResult;
|
||||
}
|
||||
|
||||
static int32_t Base64Decode(const char* encodedBytes)
|
||||
{
|
||||
int32_t i0 = encodedBytes[0];
|
||||
int32_t i1 = encodedBytes[1];
|
||||
int32_t i2 = encodedBytes[2];
|
||||
int32_t i3 = encodedBytes[3];
|
||||
|
||||
i0 = Base64DecodeArray[i0];
|
||||
i1 = Base64DecodeArray[i1];
|
||||
i2 = Base64DecodeArray[i2];
|
||||
i3 = Base64DecodeArray[i3];
|
||||
|
||||
i0 <<= 18;
|
||||
i1 <<= 12;
|
||||
i2 <<= 6;
|
||||
|
||||
i0 |= i3;
|
||||
i1 |= i2;
|
||||
|
||||
i0 |= i1;
|
||||
return i0;
|
||||
}
|
||||
|
||||
static void Base64WriteThreeLowOrderBytes(std::vector<uint8_t>::iterator destination, int64_t value)
|
||||
{
|
||||
destination[0] = static_cast<uint8_t>(value >> 16);
|
||||
destination[1] = static_cast<uint8_t>(value >> 8);
|
||||
destination[2] = static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Base64Decode(const std::string& text)
|
||||
{
|
||||
auto inputSize = text.size();
|
||||
if (inputSize < 4)
|
||||
{
|
||||
return std::vector<uint8_t>(0);
|
||||
}
|
||||
|
||||
size_t sourceIndex = 0;
|
||||
auto inputPtr = text.data();
|
||||
auto decodedSize = (inputSize / 4) * 3;
|
||||
|
||||
if (inputPtr[inputSize - 2] == EncodingPad)
|
||||
{
|
||||
decodedSize -= 2;
|
||||
}
|
||||
else if (inputPtr[inputSize - 1] == EncodingPad)
|
||||
{
|
||||
decodedSize -= 1;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> destination(decodedSize);
|
||||
auto destinationPtr = destination.begin();
|
||||
|
||||
while (sourceIndex + 4 < inputSize)
|
||||
{
|
||||
int64_t result = Base64Decode(inputPtr + sourceIndex);
|
||||
Base64WriteThreeLowOrderBytes(destinationPtr, result);
|
||||
destinationPtr += 3;
|
||||
sourceIndex += 4;
|
||||
}
|
||||
|
||||
// We are guaranteed to have an input with at least 4 bytes at this point, with a size that is a
|
||||
// multiple of 4.
|
||||
int64_t i0 = inputPtr[inputSize - 4];
|
||||
int64_t i1 = inputPtr[inputSize - 3];
|
||||
int64_t i2 = inputPtr[inputSize - 2];
|
||||
int64_t i3 = inputPtr[inputSize - 1];
|
||||
|
||||
i0 = Base64DecodeArray[i0];
|
||||
i1 = Base64DecodeArray[i1];
|
||||
|
||||
i0 <<= 18;
|
||||
i1 <<= 12;
|
||||
|
||||
i0 |= i1;
|
||||
|
||||
if (i3 != EncodingPad)
|
||||
{
|
||||
i2 = Base64DecodeArray[i2];
|
||||
i3 = Base64DecodeArray[i3];
|
||||
|
||||
i2 <<= 6;
|
||||
|
||||
i0 |= i3;
|
||||
i0 |= i2;
|
||||
|
||||
Base64WriteThreeLowOrderBytes(destinationPtr, i0);
|
||||
destinationPtr += 3;
|
||||
}
|
||||
else if (i2 != EncodingPad)
|
||||
{
|
||||
i2 = Base64DecodeArray[i2];
|
||||
|
||||
i2 <<= 6;
|
||||
|
||||
i0 |= i2;
|
||||
|
||||
destinationPtr[1] = static_cast<uint8_t>(i0 >> 8);
|
||||
destinationPtr[0] = static_cast<uint8_t>(i0 >> 16);
|
||||
destinationPtr += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
destinationPtr[0] = static_cast<uint8_t>(i0 >> 16);
|
||||
destinationPtr += 1;
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Azure { namespace Core {
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
|
||||
std::string Convert::Base64Encode(const std::vector<uint8_t>& data)
|
||||
{
|
||||
std::string encoded;
|
||||
// According to RFC 4648, the encoded length should be ceiling(n / 3) * 4
|
||||
DWORD encodedLength = static_cast<DWORD>((data.size() + 2) / 3 * 4);
|
||||
encoded.resize(encodedLength);
|
||||
|
||||
CryptBinaryToStringA(
|
||||
reinterpret_cast<const BYTE*>(data.data()),
|
||||
static_cast<DWORD>(data.size()),
|
||||
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
|
||||
static_cast<LPSTR>(&encoded[0]),
|
||||
&encodedLength);
|
||||
|
||||
return encoded;
|
||||
return ::Base64Encode(data);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Convert::Base64Decode(const std::string& text)
|
||||
{
|
||||
std::vector<uint8_t> decoded;
|
||||
// According to RFC 4648, the encoded length should be ceiling(n / 3) * 4, so we can infer an
|
||||
// upper bound here
|
||||
DWORD decodedLength = DWORD(text.length() / 4 * 3);
|
||||
decoded.resize(decodedLength);
|
||||
|
||||
CryptStringToBinaryA(
|
||||
text.data(),
|
||||
static_cast<DWORD>(text.length()),
|
||||
CRYPT_STRING_BASE64 | CRYPT_STRING_STRICT,
|
||||
reinterpret_cast<BYTE*>(decoded.data()),
|
||||
&decodedLength,
|
||||
nullptr,
|
||||
nullptr);
|
||||
decoded.resize(decodedLength);
|
||||
return decoded;
|
||||
return ::Base64Decode(text);
|
||||
}
|
||||
|
||||
#elif defined(AZ_PLATFORM_POSIX)
|
||||
|
||||
std::string Convert::Base64Encode(const std::vector<uint8_t>& data)
|
||||
{
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
bio = BIO_push(BIO_new(BIO_f_base64()), bio);
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO_write(bio, data.data(), static_cast<int>(data.size()));
|
||||
(void)BIO_flush(bio);
|
||||
BUF_MEM* bufferPtr;
|
||||
BIO_get_mem_ptr(bio, &bufferPtr);
|
||||
std::string toReturn(bufferPtr->data, bufferPtr->length);
|
||||
BIO_free_all(bio);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Convert::Base64Decode(const std::string& text)
|
||||
{
|
||||
std::vector<uint8_t> decoded;
|
||||
// According to RFC 4648, the encoded length should be ceiling(n / 3) * 4, so we can infer an
|
||||
// upper bound here
|
||||
size_t maxDecodedLength = text.length() / 4 * 3;
|
||||
decoded.resize(maxDecodedLength);
|
||||
|
||||
BIO* bio = BIO_new_mem_buf(text.data(), -1);
|
||||
bio = BIO_push(BIO_new(BIO_f_base64()), bio);
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||
int decodedLength = BIO_read(bio, &decoded[0], static_cast<int>(text.length()));
|
||||
BIO_free_all(bio);
|
||||
|
||||
decoded.resize(decodedLength);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}} // namespace Azure::Core
|
||||
|
||||
@ -3,13 +3,19 @@
|
||||
|
||||
#include "azure/core/uuid.hpp"
|
||||
|
||||
#if defined(AZ_PLATFORM_POSIX)
|
||||
#include <openssl/rand.h> //for RAND_bytes
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <random>
|
||||
|
||||
#if defined(AZ_PLATFORM_POSIX)
|
||||
#include <thread>
|
||||
namespace {
|
||||
// 64-bit Mersenne Twister by Matsumoto and Nishimura, 2000
|
||||
// Used to generate the random numbers for the Uuid.
|
||||
// The seed is generated with std::random_device.
|
||||
static thread_local std::mt19937_64 randomGenerator(std::random_device{}());
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
namespace Azure { namespace Core {
|
||||
std::string Uuid::ToString()
|
||||
{
|
||||
@ -47,22 +53,19 @@ namespace Azure { namespace Core {
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
std::random_device rd;
|
||||
#else
|
||||
std::uniform_int_distribution<uint32_t> distribution;
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < UuidSize; i += 4)
|
||||
{
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
const uint32_t x = rd();
|
||||
#else
|
||||
const uint32_t x = distribution(randomGenerator);
|
||||
#endif
|
||||
std::memcpy(uuid + i, &x, 4);
|
||||
}
|
||||
#elif defined(AZ_PLATFORM_POSIX)
|
||||
// This static cast is safe since we know Uuid size, which is a const, will always fit an int.
|
||||
int ret = RAND_bytes(uuid, static_cast<int>(UuidSize));
|
||||
if (ret <= 0)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
|
||||
// SetVariant to ReservedRFC4122
|
||||
uuid[8] = (uuid[8] | ReservedRFC4122) & 0x7F;
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Test the Uuid component performance.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core.hpp>
|
||||
#include <azure/perf.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
/**
|
||||
* @brief Measure the Uuid object performance.
|
||||
*/
|
||||
class UuidTest : public Azure::Perf::PerfTest {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Uuid test.
|
||||
*
|
||||
* @param options The test options.
|
||||
*/
|
||||
UuidTest(Azure::Perf::TestOptions options) : PerfTest(options) {}
|
||||
|
||||
/**
|
||||
* @brief Use Uuid to assign and read.
|
||||
*
|
||||
*/
|
||||
void Run(Azure::Core::Context const&) override
|
||||
{
|
||||
auto const total = m_options.GetMandatoryOption<int>("count");
|
||||
for (auto count = 0; count < total; count++)
|
||||
{
|
||||
Azure::Core::Uuid::CreateUuid();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Define the test options for the test.
|
||||
*
|
||||
* @return The list of test options.
|
||||
*/
|
||||
std::vector<Azure::Perf::TestOption> GetTestOptions() override
|
||||
{
|
||||
return {{"count", {"-c"}, "The number of uuid objects to be created.", 1, true}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the static Test Metadata for the test.
|
||||
*
|
||||
* @return Azure::Perf::TestMetadata describing the test.
|
||||
*/
|
||||
static Azure::Perf::TestMetadata GetTestMetadata()
|
||||
{
|
||||
return {
|
||||
"UuidTest",
|
||||
"Measures the overhead of using Uuid objects",
|
||||
[](Azure::Perf::TestOptions options) {
|
||||
return std::make_unique<Azure::Core::Test::UuidTest>(options);
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
}}} // namespace Azure::Core::Test
|
||||
@ -4,6 +4,7 @@
|
||||
#include <azure/perf.hpp>
|
||||
|
||||
#include "azure/core/test/nullable_test.hpp"
|
||||
#include "azure/core/test/uuid_test.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -11,7 +12,9 @@ int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
// Create the test list
|
||||
std::vector<Azure::Perf::TestMetadata> tests{Azure::Core::Test::NullableTest::GetTestMetadata()};
|
||||
std::vector<Azure::Perf::TestMetadata> tests{
|
||||
Azure::Core::Test::NullableTest::GetTestMetadata(),
|
||||
Azure::Core::Test::UuidTest::GetTestMetadata()};
|
||||
|
||||
Azure::Perf::Program::Run(Azure::Core::Context::ApplicationContext, tests, argc, argv);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user