Squashed 'third_party/allwpilib_2019/' content from commit bd05dfa1c
Change-Id: I2b1c2250cdb9b055133780c33593292098c375b7
git-subtree-dir: third_party/allwpilib_2019
git-subtree-split: bd05dfa1c7cca74c4fac451e7b9d6a37e7b53447
diff --git a/wpiutil/src/test/native/cpp/Base64Test.cpp b/wpiutil/src/test/native/cpp/Base64Test.cpp
new file mode 100644
index 0000000..99aecb9
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/Base64Test.cpp
@@ -0,0 +1,105 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "gtest/gtest.h"
+#include "wpi/Base64.h"
+#include "wpi/SmallString.h"
+
+namespace wpi {
+
+struct Base64TestParam {
+ int plain_len;
+ const char* plain;
+ const char* encoded;
+};
+
+std::ostream& operator<<(std::ostream& os, const Base64TestParam& param) {
+ os << "Base64TestParam(Len: " << param.plain_len << ", "
+ << "Plain: \"" << param.plain << "\", "
+ << "Encoded: \"" << param.encoded << "\")";
+ return os;
+}
+
+class Base64Test : public ::testing::TestWithParam<Base64TestParam> {
+ protected:
+ StringRef GetPlain() {
+ if (GetParam().plain_len < 0)
+ return StringRef(GetParam().plain);
+ else
+ return StringRef(GetParam().plain, GetParam().plain_len);
+ }
+};
+
+TEST_P(Base64Test, EncodeStdString) {
+ std::string s;
+ Base64Encode(GetPlain(), &s);
+ ASSERT_EQ(GetParam().encoded, s);
+
+ // text already in s
+ Base64Encode(GetPlain(), &s);
+ ASSERT_EQ(GetParam().encoded, s);
+}
+
+TEST_P(Base64Test, EncodeSmallString) {
+ SmallString<128> buf;
+ ASSERT_EQ(GetParam().encoded, Base64Encode(GetPlain(), buf));
+ // reuse buf
+ ASSERT_EQ(GetParam().encoded, Base64Encode(GetPlain(), buf));
+}
+
+TEST_P(Base64Test, DecodeStdString) {
+ std::string s;
+ StringRef encoded = GetParam().encoded;
+ EXPECT_EQ(encoded.size(), Base64Decode(encoded, &s));
+ ASSERT_EQ(GetPlain(), s);
+
+ // text already in s
+ Base64Decode(encoded, &s);
+ ASSERT_EQ(GetPlain(), s);
+}
+
+TEST_P(Base64Test, DecodeSmallString) {
+ SmallString<128> buf;
+ StringRef encoded = GetParam().encoded;
+ size_t len;
+ StringRef plain = Base64Decode(encoded, &len, buf);
+ EXPECT_EQ(encoded.size(), len);
+ ASSERT_EQ(GetPlain(), plain);
+
+ // reuse buf
+ plain = Base64Decode(encoded, &len, buf);
+ ASSERT_EQ(GetPlain(), plain);
+}
+
+static Base64TestParam sample[] = {
+ {-1, "Send reinforcements", "U2VuZCByZWluZm9yY2VtZW50cw=="},
+ {-1, "Now is the time for all good coders\n to learn C++",
+ "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKIHRvIGxlYXJuIEMrKw=="},
+ {-1,
+ "This is line one\nThis is line two\nThis is line three\nAnd so on...\n",
+ "VGhpcyBpcyBsaW5lIG9uZQpUaGlzIGlzIGxpbmUgdHdvClRoaXMgaXMgbGluZSB0aHJlZQpBb"
+ "mQgc28gb24uLi4K"},
+};
+
+INSTANTIATE_TEST_CASE_P(Base64Sample, Base64Test,
+ ::testing::ValuesIn(sample), );
+
+static Base64TestParam standard[] = {
+ {0, "", ""},
+ {1, "\0", "AA=="},
+ {2, "\0\0", "AAA="},
+ {3, "\0\0\0", "AAAA"},
+ {1, "\377", "/w=="},
+ {2, "\377\377", "//8="},
+ {3, "\377\377\377", "////"},
+ {2, "\xff\xef", "/+8="},
+};
+
+INSTANTIATE_TEST_CASE_P(Base64Standard, Base64Test,
+ ::testing::ValuesIn(standard), );
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/HttpParserTest.cpp b/wpiutil/src/test/native/cpp/HttpParserTest.cpp
new file mode 100644
index 0000000..8de2bc6
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/HttpParserTest.cpp
@@ -0,0 +1,209 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/HttpParser.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+TEST(HttpParserTest, UrlMethodHeadersComplete) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.url.connect([&](StringRef path) {
+ ASSERT_EQ(path, "/foo/bar");
+ ASSERT_EQ(p.GetUrl(), "/foo/bar");
+ ++callbacks;
+ });
+ p.Execute("GET /foo");
+ p.Execute("/bar");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute(" HTTP/1.1\r\n\r\n");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_EQ(p.GetUrl(), "/foo/bar");
+ ASSERT_EQ(p.GetMethod(), HTTP_GET);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, UrlMethodHeader) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.url.connect([&](StringRef path) {
+ ASSERT_EQ(path, "/foo/bar");
+ ASSERT_EQ(p.GetUrl(), "/foo/bar");
+ ++callbacks;
+ });
+ p.Execute("GET /foo");
+ p.Execute("/bar");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute(" HTTP/1.1\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("F");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_EQ(p.GetUrl(), "/foo/bar");
+ ASSERT_EQ(p.GetMethod(), HTTP_GET);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, StatusHeadersComplete) {
+ HttpParser p{HttpParser::kResponse};
+ int callbacks = 0;
+ p.status.connect([&](StringRef status) {
+ ASSERT_EQ(status, "OK");
+ ASSERT_EQ(p.GetStatusCode(), 200u);
+ ++callbacks;
+ });
+ p.Execute("HTTP/1.1 200");
+ p.Execute(" OK");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n\r\n");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_EQ(p.GetStatusCode(), 200u);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, StatusHeader) {
+ HttpParser p{HttpParser::kResponse};
+ int callbacks = 0;
+ p.status.connect([&](StringRef status) {
+ ASSERT_EQ(status, "OK");
+ ASSERT_EQ(p.GetStatusCode(), 200u);
+ ++callbacks;
+ });
+ p.Execute("HTTP/1.1 200");
+ p.Execute(" OK\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("F");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_EQ(p.GetStatusCode(), 200u);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeaderFieldComplete) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.header.connect([&](StringRef name, StringRef value) {
+ ASSERT_EQ(name, "Foo");
+ ASSERT_EQ(value, "Bar");
+ ++callbacks;
+ });
+ p.Execute("GET / HTTP/1.1\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("Fo");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("o: ");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("Bar");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeaderFieldNext) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.header.connect([&](StringRef name, StringRef value) {
+ ASSERT_EQ(name, "Foo");
+ ASSERT_EQ(value, "Bar");
+ ++callbacks;
+ });
+ p.Execute("GET / HTTP/1.1\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("Fo");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("o: ");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("Bar");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("F");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersComplete) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.headersComplete.connect([&](bool keepAlive) {
+ ASSERT_EQ(keepAlive, false);
+ ++callbacks;
+ });
+ p.Execute("GET / HTTP/1.0\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersCompleteHTTP11) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.headersComplete.connect([&](bool keepAlive) {
+ ASSERT_EQ(keepAlive, true);
+ ++callbacks;
+ });
+ p.Execute("GET / HTTP/1.1\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersCompleteKeepAlive) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.headersComplete.connect([&](bool keepAlive) {
+ ASSERT_EQ(keepAlive, true);
+ ++callbacks;
+ });
+ p.Execute("GET / HTTP/1.0\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("Connection: Keep-Alive\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersCompleteUpgrade) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.headersComplete.connect([&](bool) {
+ ASSERT_TRUE(p.IsUpgrade());
+ ++callbacks;
+ });
+ p.Execute("GET / HTTP/1.0\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("Connection: Upgrade\r\n");
+ p.Execute("Upgrade: websocket\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 1);
+ ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, Reset) {
+ HttpParser p{HttpParser::kRequest};
+ int callbacks = 0;
+ p.headersComplete.connect([&](bool) { ++callbacks; });
+ p.Execute("GET / HTTP/1.1\r\n");
+ ASSERT_EQ(callbacks, 0);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 1);
+ p.Reset(HttpParser::kRequest);
+ p.Execute("GET / HTTP/1.1\r\n");
+ ASSERT_EQ(callbacks, 1);
+ p.Execute("\r\n");
+ ASSERT_EQ(callbacks, 2);
+ ASSERT_FALSE(p.HasError());
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/HttpUtilTest.cpp b/wpiutil/src/test/native/cpp/HttpUtilTest.cpp
new file mode 100644
index 0000000..a83214d
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/HttpUtilTest.cpp
@@ -0,0 +1,105 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/HttpUtil.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+TEST(HttpMultipartScannerTest, ExecuteExact) {
+ HttpMultipartScanner scanner("foo");
+ EXPECT_TRUE(scanner.Execute("abcdefg---\r\n--foo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+ EXPECT_TRUE(scanner.GetSkipped().empty());
+}
+
+TEST(HttpMultipartScannerTest, ExecutePartial) {
+ HttpMultipartScanner scanner("foo");
+ EXPECT_TRUE(scanner.Execute("abcdefg--").empty());
+ EXPECT_FALSE(scanner.IsDone());
+ EXPECT_TRUE(scanner.Execute("-\r\n").empty());
+ EXPECT_FALSE(scanner.IsDone());
+ EXPECT_TRUE(scanner.Execute("--foo\r").empty());
+ EXPECT_FALSE(scanner.IsDone());
+ EXPECT_TRUE(scanner.Execute("\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, ExecuteTrailing) {
+ HttpMultipartScanner scanner("foo");
+ EXPECT_EQ(scanner.Execute("abcdefg---\r\n--foo\r\nxyz"), "xyz");
+}
+
+TEST(HttpMultipartScannerTest, ExecutePadding) {
+ HttpMultipartScanner scanner("foo");
+ EXPECT_EQ(scanner.Execute("abcdefg---\r\n--foo \r\nxyz"), "xyz");
+ EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SaveSkipped) {
+ HttpMultipartScanner scanner("foo", true);
+ scanner.Execute("abcdefg---\r\n--foo\r\n");
+ EXPECT_EQ(scanner.GetSkipped(), "abcdefg---\r\n--foo\r\n");
+}
+
+TEST(HttpMultipartScannerTest, Reset) {
+ HttpMultipartScanner scanner("foo", true);
+
+ scanner.Execute("abcdefg---\r\n--foo\r\n");
+ EXPECT_TRUE(scanner.IsDone());
+ EXPECT_EQ(scanner.GetSkipped(), "abcdefg---\r\n--foo\r\n");
+
+ scanner.Reset(true);
+ EXPECT_FALSE(scanner.IsDone());
+ scanner.SetBoundary("bar");
+
+ scanner.Execute("--foo\r\n--bar\r\n");
+ EXPECT_TRUE(scanner.IsDone());
+ EXPECT_EQ(scanner.GetSkipped(), "--foo\r\n--bar\r\n");
+}
+
+TEST(HttpMultipartScannerTest, WithoutDashes) {
+ HttpMultipartScanner scanner("foo", true);
+
+ EXPECT_TRUE(scanner.Execute("--\r\nfoo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqDashesDashes) {
+ HttpMultipartScanner scanner("foo", true);
+ EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+ EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqDashesNoDashes) {
+ HttpMultipartScanner scanner("foo", true);
+ EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+ EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+ EXPECT_FALSE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqNoDashesDashes) {
+ HttpMultipartScanner scanner("foo", true);
+ EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+ EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+ EXPECT_FALSE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqNoDashesNoDashes) {
+ HttpMultipartScanner scanner("foo", true);
+ EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+ EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+ EXPECT_TRUE(scanner.IsDone());
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/UidVectorTest.cpp b/wpiutil/src/test/native/cpp/UidVectorTest.cpp
new file mode 100644
index 0000000..e11c7b2
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/UidVectorTest.cpp
@@ -0,0 +1,48 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/UidVector.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+TEST(UidVectorTest, Empty) {
+ UidVector<int, 4> v;
+ ASSERT_TRUE(v.empty());
+
+ v.emplace_back(1);
+ ASSERT_FALSE(v.empty());
+}
+
+TEST(UidVectorTest, Erase) {
+ UidVector<int, 4> v;
+ size_t uid = v.emplace_back(1);
+ v.erase(uid);
+ ASSERT_TRUE(v.empty());
+}
+
+TEST(UidVectorTest, Clear) {
+ UidVector<int, 4> v;
+ v.emplace_back(1);
+ v.emplace_back(2);
+ v.clear();
+ ASSERT_TRUE(v.empty());
+}
+
+TEST(UidVectorTest, Iterate) {
+ UidVector<int, 4> v;
+ v.emplace_back(2);
+ v.emplace_back(1);
+ std::vector<int> out;
+ for (auto&& val : v) out.push_back(val);
+ ASSERT_EQ(out.size(), 2u);
+ EXPECT_EQ(out[0], 2);
+ EXPECT_EQ(out[1], 1);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
new file mode 100644
index 0000000..692e2a7
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
@@ -0,0 +1,299 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/WebSocket.h" // NOLINT(build/include_order)
+
+#include "WebSocketTest.h"
+#include "wpi/Base64.h"
+#include "wpi/HttpParser.h"
+#include "wpi/SmallString.h"
+#include "wpi/raw_uv_ostream.h"
+#include "wpi/sha1.h"
+
+namespace wpi {
+
+class WebSocketClientTest : public WebSocketTest {
+ public:
+ WebSocketClientTest() {
+ // Bare bones server
+ req.header.connect([this](StringRef name, StringRef value) {
+ // save key (required for valid response)
+ if (name.equals_lower("sec-websocket-key")) clientKey = value;
+ });
+ req.headersComplete.connect([this](bool) {
+ // send response
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os{bufs, 4096};
+ os << "HTTP/1.1 101 Switching Protocols\r\n";
+ os << "Upgrade: websocket\r\n";
+ os << "Connection: Upgrade\r\n";
+
+ // accept hash
+ SHA1 hash;
+ hash.Update(clientKey);
+ hash.Update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ if (mockBadAccept) hash.Update("1");
+ SmallString<64> hashBuf;
+ SmallString<64> acceptBuf;
+ os << "Sec-WebSocket-Accept: "
+ << Base64Encode(hash.RawFinal(hashBuf), acceptBuf) << "\r\n";
+
+ if (!mockProtocol.empty())
+ os << "Sec-WebSocket-Protocol: " << mockProtocol << "\r\n";
+
+ os << "\r\n";
+
+ conn->Write(bufs, [](auto bufs, uv::Error) {
+ for (auto& buf : bufs) buf.Deallocate();
+ });
+
+ serverHeadersDone = true;
+ if (connected) connected();
+ });
+
+ serverPipe->Listen([this] {
+ conn = serverPipe->Accept();
+ conn->StartRead();
+ conn->data.connect([this](uv::Buffer& buf, size_t size) {
+ StringRef data{buf.base, size};
+ if (!serverHeadersDone) {
+ data = req.Execute(data);
+ if (req.HasError()) Finish();
+ ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
+ if (data.empty()) return;
+ }
+ wireData.insert(wireData.end(), data.bytes_begin(), data.bytes_end());
+ });
+ conn->end.connect([this] { Finish(); });
+ });
+ }
+
+ bool mockBadAccept = false;
+ std::vector<uint8_t> wireData;
+ std::shared_ptr<uv::Pipe> conn;
+ HttpParser req{HttpParser::kRequest};
+ SmallString<64> clientKey;
+ std::string mockProtocol;
+ bool serverHeadersDone = false;
+ std::function<void()> connected;
+};
+
+TEST_F(WebSocketClientTest, Open) {
+ int gotOpen = 0;
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ Finish();
+ if (code != 1005 && code != 1006)
+ FAIL() << "Code: " << code << " Reason: " << reason;
+ });
+ ws->open.connect([&](StringRef protocol) {
+ ++gotOpen;
+ Finish();
+ ASSERT_TRUE(protocol.empty());
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketClientTest, BadAccept) {
+ int gotClosed = 0;
+
+ mockBadAccept = true;
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ ws->closed.connect([&](uint16_t code, StringRef msg) {
+ Finish();
+ ++gotClosed;
+ ASSERT_EQ(code, 1002) << "Message: " << msg;
+ });
+ ws->open.connect([&](StringRef protocol) {
+ Finish();
+ FAIL() << "Got open";
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketClientTest, ProtocolGood) {
+ int gotOpen = 0;
+
+ mockProtocol = "myProtocol";
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(
+ *clientPipe, "/test", pipeName,
+ ArrayRef<StringRef>{"myProtocol", "myProtocol2"});
+ ws->closed.connect([&](uint16_t code, StringRef msg) {
+ Finish();
+ if (code != 1005 && code != 1006)
+ FAIL() << "Code: " << code << "Message: " << msg;
+ });
+ ws->open.connect([&](StringRef protocol) {
+ ++gotOpen;
+ Finish();
+ ASSERT_EQ(protocol, "myProtocol");
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketClientTest, ProtocolRespNotReq) {
+ int gotClosed = 0;
+
+ mockProtocol = "myProtocol";
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ ws->closed.connect([&](uint16_t code, StringRef msg) {
+ Finish();
+ ++gotClosed;
+ ASSERT_EQ(code, 1003) << "Message: " << msg;
+ });
+ ws->open.connect([&](StringRef protocol) {
+ Finish();
+ FAIL() << "Got open";
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketClientTest, ProtocolReqNotResp) {
+ int gotClosed = 0;
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
+ StringRef{"myProtocol"});
+ ws->closed.connect([&](uint16_t code, StringRef msg) {
+ Finish();
+ ++gotClosed;
+ ASSERT_EQ(code, 1002) << "Message: " << msg;
+ });
+ ws->open.connect([&](StringRef protocol) {
+ Finish();
+ FAIL() << "Got open";
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// Send and receive data. Most of these cases are tested in
+// WebSocketServerTest, so only spot check differences like masking.
+//
+
+class WebSocketClientDataTest : public WebSocketClientTest,
+ public ::testing::WithParamInterface<size_t> {
+ public:
+ WebSocketClientDataTest() {
+ clientPipe->Connect(pipeName, [&] {
+ ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ if (setupWebSocket) setupWebSocket();
+ });
+ }
+
+ std::function<void()> setupWebSocket;
+ std::shared_ptr<WebSocket> ws;
+};
+
+INSTANTIATE_TEST_CASE_P(WebSocketClientDataTests, WebSocketClientDataTest,
+ ::testing::Values(0, 1, 125, 126, 65535, 65536), );
+
+TEST_P(WebSocketClientDataTest, SendBinary) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) {
+ ws->SendBinary(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_FALSE(bufs.empty());
+ ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+ });
+ });
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x02, true, true, data);
+ AdjustMasking(wireData);
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketClientDataTest, ReceiveBinary) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_TRUE(fin);
+ std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+ ASSERT_EQ(data, recvData);
+ });
+ };
+ auto message = BuildMessage(0x02, true, false, data);
+ connected = [&] {
+ conn->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ };
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// The client must close the connection if a masked frame is received.
+//
+
+TEST_P(WebSocketClientDataTest, ReceiveMasked) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), ' ');
+ setupWebSocket = [&] {
+ ws->text.connect([&](StringRef, bool) {
+ ws->Terminate();
+ FAIL() << "Should not have gotten masked message";
+ });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1002) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x01, true, true, data);
+ connected = [&] {
+ conn->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ };
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp b/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
new file mode 100644
index 0000000..f51e8fc
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
@@ -0,0 +1,148 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/WebSocketServer.h" // NOLINT(build/include_order)
+
+#include "WebSocketTest.h"
+#include "wpi/HttpParser.h"
+#include "wpi/SmallString.h"
+
+namespace wpi {
+
+class WebSocketIntegrationTest : public WebSocketTest {};
+
+TEST_F(WebSocketIntegrationTest, Open) {
+ int gotServerOpen = 0;
+ int gotClientOpen = 0;
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ auto server = WebSocketServer::Create(*conn);
+ server->connected.connect([&](StringRef url, WebSocket&) {
+ ++gotServerOpen;
+ ASSERT_EQ(url, "/test");
+ });
+ });
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ Finish();
+ if (code != 1005 && code != 1006)
+ FAIL() << "Code: " << code << " Reason: " << reason;
+ });
+ ws->open.connect([&, s = ws.get() ](StringRef) {
+ ++gotClientOpen;
+ s->Close();
+ });
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotServerOpen, 1);
+ ASSERT_EQ(gotClientOpen, 1);
+}
+
+TEST_F(WebSocketIntegrationTest, Protocol) {
+ int gotServerOpen = 0;
+ int gotClientOpen = 0;
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ auto server = WebSocketServer::Create(*conn, {"proto1", "proto2"});
+ server->connected.connect([&](StringRef, WebSocket& ws) {
+ ++gotServerOpen;
+ ASSERT_EQ(ws.GetProtocol(), "proto1");
+ });
+ });
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws =
+ WebSocket::CreateClient(*clientPipe, "/test", pipeName, {"proto1"});
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ Finish();
+ if (code != 1005 && code != 1006)
+ FAIL() << "Code: " << code << " Reason: " << reason;
+ });
+ ws->open.connect([&, s = ws.get() ](StringRef protocol) {
+ ++gotClientOpen;
+ s->Close();
+ ASSERT_EQ(protocol, "proto1");
+ });
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotServerOpen, 1);
+ ASSERT_EQ(gotClientOpen, 1);
+}
+
+TEST_F(WebSocketIntegrationTest, ServerSendBinary) {
+ int gotData = 0;
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ auto server = WebSocketServer::Create(*conn);
+ server->connected.connect([&](StringRef, WebSocket& ws) {
+ ws.SendBinary(uv::Buffer{"\x03\x04", 2}, [&](auto, uv::Error) {});
+ ws.Close();
+ });
+ });
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ Finish();
+ if (code != 1005 && code != 1006)
+ FAIL() << "Code: " << code << " Reason: " << reason;
+ });
+ ws->binary.connect([&](ArrayRef<uint8_t> data, bool) {
+ ++gotData;
+ std::vector<uint8_t> recvData{data.begin(), data.end()};
+ std::vector<uint8_t> expectData{0x03, 0x04};
+ ASSERT_EQ(recvData, expectData);
+ });
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotData, 1);
+}
+
+TEST_F(WebSocketIntegrationTest, ClientSendText) {
+ int gotData = 0;
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ auto server = WebSocketServer::Create(*conn);
+ server->connected.connect([&](StringRef, WebSocket& ws) {
+ ws.text.connect([&](StringRef data, bool) {
+ ++gotData;
+ ASSERT_EQ(data, "hello");
+ });
+ });
+ });
+
+ clientPipe->Connect(pipeName, [&] {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ Finish();
+ if (code != 1005 && code != 1006)
+ FAIL() << "Code: " << code << " Reason: " << reason;
+ });
+ ws->open.connect([&, s = ws.get() ](StringRef) {
+ s->SendText(uv::Buffer{"hello"}, [&](auto, uv::Error) {});
+ s->Close();
+ });
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotData, 1);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
new file mode 100644
index 0000000..7d723e3
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
@@ -0,0 +1,736 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/WebSocket.h" // NOLINT(build/include_order)
+
+#include "WebSocketTest.h"
+#include "wpi/Base64.h"
+#include "wpi/HttpParser.h"
+#include "wpi/SmallString.h"
+#include "wpi/raw_uv_ostream.h"
+#include "wpi/sha1.h"
+
+namespace wpi {
+
+class WebSocketServerTest : public WebSocketTest {
+ public:
+ WebSocketServerTest() {
+ resp.headersComplete.connect([this](bool) { headersDone = true; });
+
+ serverPipe->Listen([this]() {
+ auto conn = serverPipe->Accept();
+ ws = WebSocket::CreateServer(*conn, "foo", "13");
+ if (setupWebSocket) setupWebSocket();
+ });
+ clientPipe->Connect(pipeName, [this]() {
+ clientPipe->StartRead();
+ clientPipe->data.connect([this](uv::Buffer& buf, size_t size) {
+ StringRef data{buf.base, size};
+ if (!headersDone) {
+ data = resp.Execute(data);
+ if (resp.HasError()) Finish();
+ ASSERT_EQ(resp.GetError(), HPE_OK)
+ << http_errno_name(resp.GetError());
+ if (data.empty()) return;
+ }
+ wireData.insert(wireData.end(), data.bytes_begin(), data.bytes_end());
+ if (handleData) handleData(data);
+ });
+ clientPipe->end.connect([this]() { Finish(); });
+ });
+ }
+
+ std::function<void()> setupWebSocket;
+ std::function<void(StringRef)> handleData;
+ std::vector<uint8_t> wireData;
+ std::shared_ptr<WebSocket> ws;
+ HttpParser resp{HttpParser::kResponse};
+ bool headersDone = false;
+};
+
+//
+// Terminate closes the endpoint but doesn't send a close frame.
+//
+
+TEST_F(WebSocketServerTest, Terminate) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) { ws->Terminate(); });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1006) << "reason: " << reason;
+ });
+ };
+
+ loop->Run();
+
+ ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, TerminateCode) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) { ws->Terminate(1000); });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1000) << "reason: " << reason;
+ });
+ };
+
+ loop->Run();
+
+ ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, TerminateReason) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) { ws->Terminate(1000, "reason"); });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1000);
+ ASSERT_EQ(reason, "reason");
+ });
+ };
+
+ loop->Run();
+
+ ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
+ ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// Close() sends a close frame.
+//
+
+TEST_F(WebSocketServerTest, CloseBasic) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) { ws->Close(); });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1005) << "reason: " << reason;
+ });
+ };
+ // need to respond with close for server to finish shutdown
+ auto message = BuildMessage(0x08, true, true, {});
+ handleData = [&](StringRef) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x08, true, false, {});
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, CloseCode) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) { ws->Close(1000); });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1000) << "reason: " << reason;
+ });
+ };
+ // need to respond with close for server to finish shutdown
+ auto message = BuildMessage(0x08, true, true, {0x03u, 0xe8u});
+ handleData = [&](StringRef) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x08, true, false, {0x03u, 0xe8u});
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, CloseReason) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) { ws->Close(1000, "hangup"); });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1000);
+ ASSERT_EQ(reason, "hangup");
+ });
+ };
+ // need to respond with close for server to finish shutdown
+ auto message = BuildMessage(0x08, true, true,
+ {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+ handleData = [&](StringRef) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x08, true, false,
+ {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// Receiving a close frame results in closure and echoing the close frame.
+//
+
+TEST_F(WebSocketServerTest, ReceiveCloseBasic) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1005) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x08, true, true, {});
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ // the endpoint should echo the message
+ auto expectData = BuildMessage(0x08, true, false, {});
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, ReceiveCloseCode) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1000) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x08, true, true, {0x03u, 0xe8u});
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ // the endpoint should echo the message
+ auto expectData = BuildMessage(0x08, true, false, {0x03u, 0xe8u});
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, ReceiveCloseReason) {
+ int gotClosed = 0;
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotClosed;
+ ASSERT_EQ(code, 1000);
+ ASSERT_EQ(reason, "hangup");
+ });
+ };
+ auto message = BuildMessage(0x08, true, true,
+ {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ // the endpoint should echo the message
+ auto expectData = BuildMessage(0x08, true, false,
+ {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// If an unknown opcode is received, the receiving endpoint MUST _Fail the
+// WebSocket Connection_.
+//
+
+class WebSocketServerBadOpcodeTest
+ : public WebSocketServerTest,
+ public ::testing::WithParamInterface<uint8_t> {};
+
+INSTANTIATE_TEST_CASE_P(WebSocketServerBadOpcodeTests,
+ WebSocketServerBadOpcodeTest,
+ ::testing::Values(3, 4, 5, 6, 7, 0xb, 0xc, 0xd, 0xe,
+ 0xf), );
+
+TEST_P(WebSocketServerBadOpcodeTest, Receive) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(4, 0x03);
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1002) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(GetParam(), true, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// Control frames themselves MUST NOT be fragmented.
+//
+
+class WebSocketServerControlFrameTest
+ : public WebSocketServerTest,
+ public ::testing::WithParamInterface<uint8_t> {};
+
+INSTANTIATE_TEST_CASE_P(WebSocketServerControlFrameTests,
+ WebSocketServerControlFrameTest,
+ ::testing::Values(0x8, 0x9, 0xa), );
+
+TEST_P(WebSocketServerControlFrameTest, ReceiveFragment) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(4, 0x03);
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1002) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(GetParam(), false, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// A fragmented message consists of a single frame with the FIN bit
+// clear and an opcode other than 0, followed by zero or more frames
+// with the FIN bit clear and the opcode set to 0, and terminated by
+// a single frame with the FIN bit set and an opcode of 0.
+//
+
+// No previous message
+TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFrame) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(4, 0x03);
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1002) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x00, false, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+// No previous message with FIN=1.
+TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFragment) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(4, 0x03);
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1002) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x01, true, true, {}); // FIN=1
+ auto message2 = BuildMessage(0x00, false, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write({uv::Buffer(message), uv::Buffer(message2)},
+ [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+// Incomplete fragment
+TEST_F(WebSocketServerTest, ReceiveFragmentInvalidIncomplete) {
+ int gotCallback = 0;
+ setupWebSocket = [&] {
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1002) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x01, false, true, {});
+ auto message2 = BuildMessage(0x00, false, true, {});
+ auto message3 = BuildMessage(0x01, true, true, {});
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(
+ {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
+ [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+// Normally fragments are combined into a single callback
+TEST_F(WebSocketServerTest, ReceiveFragment) {
+ int gotCallback = 0;
+
+ std::vector<uint8_t> data(4, 0x03);
+ std::vector<uint8_t> data2(4, 0x04);
+ std::vector<uint8_t> data3(4, 0x05);
+ std::vector<uint8_t> combData{data};
+ combData.insert(combData.end(), data2.begin(), data2.end());
+ combData.insert(combData.end(), data3.begin(), data3.end());
+
+ setupWebSocket = [&] {
+ ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_TRUE(fin);
+ std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+ ASSERT_EQ(combData, recvData);
+ });
+ };
+
+ auto message = BuildMessage(0x02, false, true, data);
+ auto message2 = BuildMessage(0x00, false, true, data2);
+ auto message3 = BuildMessage(0x00, true, true, data3);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(
+ {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
+ [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+// But can be configured for multiple callbacks
+TEST_F(WebSocketServerTest, ReceiveFragmentSeparate) {
+ int gotCallback = 0;
+
+ std::vector<uint8_t> data(4, 0x03);
+ std::vector<uint8_t> data2(4, 0x04);
+ std::vector<uint8_t> data3(4, 0x05);
+ std::vector<uint8_t> combData{data};
+ combData.insert(combData.end(), data2.begin(), data2.end());
+ combData.insert(combData.end(), data3.begin(), data3.end());
+
+ setupWebSocket = [&] {
+ ws->SetCombineFragments(false);
+ ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+ switch (++gotCallback) {
+ case 1:
+ ASSERT_FALSE(fin);
+ ASSERT_EQ(data, recvData);
+ break;
+ case 2:
+ ASSERT_FALSE(fin);
+ ASSERT_EQ(data2, recvData);
+ break;
+ case 3:
+ ws->Terminate();
+ ASSERT_TRUE(fin);
+ ASSERT_EQ(data3, recvData);
+ break;
+ default:
+ FAIL() << "too many callbacks";
+ break;
+ }
+ });
+ };
+
+ auto message = BuildMessage(0x02, false, true, data);
+ auto message2 = BuildMessage(0x00, false, true, data2);
+ auto message3 = BuildMessage(0x00, true, true, data3);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(
+ {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
+ [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 3);
+}
+
+//
+// Maximum message size is limited.
+//
+
+// Single message
+TEST_F(WebSocketServerTest, ReceiveTooLarge) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(2048, 0x03u);
+ setupWebSocket = [&] {
+ ws->SetMaxMessageSize(1024);
+ ws->binary.connect([&](auto, bool) {
+ ws->Terminate();
+ FAIL() << "Should not have gotten unmasked message";
+ });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1009) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x01, true, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+// Applied across fragments if combining
+TEST_F(WebSocketServerTest, ReceiveTooLargeFragmented) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(768, 0x03u);
+ setupWebSocket = [&] {
+ ws->SetMaxMessageSize(1024);
+ ws->binary.connect([&](auto, bool) {
+ ws->Terminate();
+ FAIL() << "Should not have gotten unmasked message";
+ });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1009) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x01, false, true, data);
+ auto message2 = BuildMessage(0x00, true, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write({uv::Buffer(message), uv::Buffer(message2)},
+ [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// Send and receive data.
+//
+
+class WebSocketServerDataTest : public WebSocketServerTest,
+ public ::testing::WithParamInterface<size_t> {};
+
+INSTANTIATE_TEST_CASE_P(WebSocketServerDataTests, WebSocketServerDataTest,
+ ::testing::Values(0, 1, 125, 126, 65535, 65536), );
+
+TEST_P(WebSocketServerDataTest, SendText) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), ' ');
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) {
+ ws->SendText(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_FALSE(bufs.empty());
+ ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+ });
+ });
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x01, true, false, data);
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, SendBinary) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) {
+ ws->SendBinary(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_FALSE(bufs.empty());
+ ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+ });
+ });
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x02, true, false, data);
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, SendPing) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) {
+ ws->SendPing(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_FALSE(bufs.empty());
+ ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+ });
+ });
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x09, true, false, data);
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, SendPong) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->open.connect([&](StringRef) {
+ ws->SendPong(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_FALSE(bufs.empty());
+ ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+ });
+ });
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x0a, true, false, data);
+ ASSERT_EQ(wireData, expectData);
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceiveText) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), ' ');
+ setupWebSocket = [&] {
+ ws->text.connect([&](StringRef inData, bool fin) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_TRUE(fin);
+ std::vector<uint8_t> recvData;
+ recvData.insert(recvData.end(), inData.bytes_begin(), inData.bytes_end());
+ ASSERT_EQ(data, recvData);
+ });
+ };
+ auto message = BuildMessage(0x01, true, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceiveBinary) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ ++gotCallback;
+ ws->Terminate();
+ ASSERT_TRUE(fin);
+ std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+ ASSERT_EQ(data, recvData);
+ });
+ };
+ auto message = BuildMessage(0x02, true, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceivePing) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->ping.connect([&](ArrayRef<uint8_t> inData) {
+ ++gotCallback;
+ ws->Terminate();
+ std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+ ASSERT_EQ(data, recvData);
+ });
+ };
+ auto message = BuildMessage(0x09, true, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceivePong) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), 0x03u);
+ setupWebSocket = [&] {
+ ws->pong.connect([&](ArrayRef<uint8_t> inData) {
+ ++gotCallback;
+ ws->Terminate();
+ std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+ ASSERT_EQ(data, recvData);
+ });
+ };
+ auto message = BuildMessage(0x0a, true, true, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// The server must close the connection if an unmasked frame is received.
+//
+
+TEST_P(WebSocketServerDataTest, ReceiveUnmasked) {
+ int gotCallback = 0;
+ std::vector<uint8_t> data(GetParam(), ' ');
+ setupWebSocket = [&] {
+ ws->text.connect([&](StringRef, bool) {
+ ws->Terminate();
+ FAIL() << "Should not have gotten unmasked message";
+ });
+ ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ++gotCallback;
+ ASSERT_EQ(code, 1002) << "reason: " << reason;
+ });
+ };
+ auto message = BuildMessage(0x01, true, false, data);
+ resp.headersComplete.connect([&](bool) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(gotCallback, 1);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/WebSocketTest.cpp b/wpiutil/src/test/native/cpp/WebSocketTest.cpp
new file mode 100644
index 0000000..c27bac0
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketTest.cpp
@@ -0,0 +1,346 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/WebSocket.h" // NOLINT(build/include_order)
+
+#include "WebSocketTest.h"
+
+#include "wpi/HttpParser.h"
+
+namespace wpi {
+
+#ifdef _WIN32
+const char* WebSocketTest::pipeName = "\\\\.\\pipe\\websocket-unit-test";
+#else
+const char* WebSocketTest::pipeName = "/tmp/websocket-unit-test";
+#endif
+const uint8_t WebSocketTest::testMask[4] = {0x11, 0x22, 0x33, 0x44};
+
+void WebSocketTest::SetUpTestCase() {
+#ifndef _WIN32
+ unlink(pipeName);
+#endif
+}
+
+std::vector<uint8_t> WebSocketTest::BuildHeader(uint8_t opcode, bool fin,
+ bool masking, uint64_t len) {
+ std::vector<uint8_t> data;
+ data.push_back(opcode | (fin ? 0x80u : 0x00u));
+ if (len < 126) {
+ data.push_back(len | (masking ? 0x80 : 0x00u));
+ } else if (len < 65536) {
+ data.push_back(126u | (masking ? 0x80 : 0x00u));
+ data.push_back(len >> 8);
+ data.push_back(len & 0xff);
+ } else {
+ data.push_back(127u | (masking ? 0x80u : 0x00u));
+ for (int i = 56; i >= 0; i -= 8) data.push_back((len >> i) & 0xff);
+ }
+ if (masking) data.insert(data.end(), &testMask[0], &testMask[4]);
+ return data;
+}
+
+std::vector<uint8_t> WebSocketTest::BuildMessage(uint8_t opcode, bool fin,
+ bool masking,
+ ArrayRef<uint8_t> data) {
+ auto finalData = BuildHeader(opcode, fin, masking, data.size());
+ size_t headerSize = finalData.size();
+ finalData.insert(finalData.end(), data.begin(), data.end());
+ if (masking) {
+ uint8_t mask[4] = {finalData[headerSize - 4], finalData[headerSize - 3],
+ finalData[headerSize - 2], finalData[headerSize - 1]};
+ int n = 0;
+ for (size_t i = headerSize, end = finalData.size(); i < end; ++i) {
+ finalData[i] ^= mask[n++];
+ if (n >= 4) n = 0;
+ }
+ }
+ return finalData;
+}
+
+// If the message is masked, changes the mask to match the mask set by
+// BuildHeader() by unmasking and remasking.
+void WebSocketTest::AdjustMasking(MutableArrayRef<uint8_t> message) {
+ if (message.size() < 2) return;
+ if ((message[1] & 0x80) == 0) return; // not masked
+ size_t maskPos;
+ uint8_t len = message[1] & 0x7f;
+ if (len == 126)
+ maskPos = 4;
+ else if (len == 127)
+ maskPos = 10;
+ else
+ maskPos = 2;
+ uint8_t mask[4] = {message[maskPos], message[maskPos + 1],
+ message[maskPos + 2], message[maskPos + 3]};
+ message[maskPos] = testMask[0];
+ message[maskPos + 1] = testMask[1];
+ message[maskPos + 2] = testMask[2];
+ message[maskPos + 3] = testMask[3];
+ int n = 0;
+ for (auto& ch : message.slice(maskPos + 4)) {
+ ch ^= mask[n] ^ testMask[n];
+ if (++n >= 4) n = 0;
+ }
+}
+
+TEST_F(WebSocketTest, CreateClientBasic) {
+ int gotHost = 0;
+ int gotUpgrade = 0;
+ int gotConnection = 0;
+ int gotKey = 0;
+ int gotVersion = 0;
+
+ HttpParser req{HttpParser::kRequest};
+ req.url.connect([](StringRef url) { ASSERT_EQ(url, "/test"); });
+ req.header.connect([&](StringRef name, StringRef value) {
+ if (name.equals_lower("host")) {
+ ASSERT_EQ(value, pipeName);
+ ++gotHost;
+ } else if (name.equals_lower("upgrade")) {
+ ASSERT_EQ(value, "websocket");
+ ++gotUpgrade;
+ } else if (name.equals_lower("connection")) {
+ ASSERT_EQ(value, "Upgrade");
+ ++gotConnection;
+ } else if (name.equals_lower("sec-websocket-key")) {
+ ++gotKey;
+ } else if (name.equals_lower("sec-websocket-version")) {
+ ASSERT_EQ(value, "13");
+ ++gotVersion;
+ } else {
+ FAIL() << "unexpected header " << name.str();
+ }
+ });
+ req.headersComplete.connect([&](bool) { Finish(); });
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ conn->StartRead();
+ conn->data.connect([&](uv::Buffer& buf, size_t size) {
+ req.Execute(StringRef{buf.base, size});
+ if (req.HasError()) Finish();
+ ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
+ });
+ });
+ clientPipe->Connect(pipeName, [&]() {
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotHost, 1);
+ ASSERT_EQ(gotUpgrade, 1);
+ ASSERT_EQ(gotConnection, 1);
+ ASSERT_EQ(gotKey, 1);
+ ASSERT_EQ(gotVersion, 1);
+}
+
+TEST_F(WebSocketTest, CreateClientExtraHeaders) {
+ int gotExtra1 = 0;
+ int gotExtra2 = 0;
+ HttpParser req{HttpParser::kRequest};
+ req.header.connect([&](StringRef name, StringRef value) {
+ if (name.equals("Extra1")) {
+ ASSERT_EQ(value, "Data1");
+ ++gotExtra1;
+ } else if (name.equals("Extra2")) {
+ ASSERT_EQ(value, "Data2");
+ ++gotExtra2;
+ }
+ });
+ req.headersComplete.connect([&](bool) { Finish(); });
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ conn->StartRead();
+ conn->data.connect([&](uv::Buffer& buf, size_t size) {
+ req.Execute(StringRef{buf.base, size});
+ if (req.HasError()) Finish();
+ ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
+ });
+ });
+ clientPipe->Connect(pipeName, [&]() {
+ WebSocket::ClientOptions options;
+ SmallVector<std::pair<StringRef, StringRef>, 4> extraHeaders;
+ extraHeaders.emplace_back("Extra1", "Data1");
+ extraHeaders.emplace_back("Extra2", "Data2");
+ options.extraHeaders = extraHeaders;
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
+ ArrayRef<StringRef>{}, options);
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotExtra1, 1);
+ ASSERT_EQ(gotExtra2, 1);
+}
+
+TEST_F(WebSocketTest, CreateClientTimeout) {
+ int gotClosed = 0;
+ serverPipe->Listen([&]() { auto conn = serverPipe->Accept(); });
+ clientPipe->Connect(pipeName, [&]() {
+ WebSocket::ClientOptions options;
+ options.handshakeTimeout = uv::Timer::Time{100};
+ auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
+ ArrayRef<StringRef>{}, options);
+ ws->closed.connect([&](uint16_t code, StringRef) {
+ Finish();
+ ++gotClosed;
+ ASSERT_EQ(code, 1006);
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketTest, CreateServerBasic) {
+ int gotStatus = 0;
+ int gotUpgrade = 0;
+ int gotConnection = 0;
+ int gotAccept = 0;
+ int gotOpen = 0;
+
+ HttpParser resp{HttpParser::kResponse};
+ resp.status.connect([&](StringRef status) {
+ ++gotStatus;
+ ASSERT_EQ(resp.GetStatusCode(), 101u) << "status: " << status;
+ });
+ resp.header.connect([&](StringRef name, StringRef value) {
+ if (name.equals_lower("upgrade")) {
+ ASSERT_EQ(value, "websocket");
+ ++gotUpgrade;
+ } else if (name.equals_lower("connection")) {
+ ASSERT_EQ(value, "Upgrade");
+ ++gotConnection;
+ } else if (name.equals_lower("sec-websocket-accept")) {
+ ASSERT_EQ(value, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
+ ++gotAccept;
+ } else {
+ FAIL() << "unexpected header " << name.str();
+ }
+ });
+ resp.headersComplete.connect([&](bool) { Finish(); });
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ auto ws = WebSocket::CreateServer(*conn, "dGhlIHNhbXBsZSBub25jZQ==", "13");
+ ws->open.connect([&](StringRef protocol) {
+ ++gotOpen;
+ ASSERT_TRUE(protocol.empty());
+ });
+ });
+ clientPipe->Connect(pipeName, [&] {
+ clientPipe->StartRead();
+ clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
+ resp.Execute(StringRef{buf.base, size});
+ if (resp.HasError()) Finish();
+ ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotStatus, 1);
+ ASSERT_EQ(gotUpgrade, 1);
+ ASSERT_EQ(gotConnection, 1);
+ ASSERT_EQ(gotAccept, 1);
+ ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketTest, CreateServerProtocol) {
+ int gotProtocol = 0;
+ int gotOpen = 0;
+
+ HttpParser resp{HttpParser::kResponse};
+ resp.header.connect([&](StringRef name, StringRef value) {
+ if (name.equals_lower("sec-websocket-protocol")) {
+ ++gotProtocol;
+ ASSERT_EQ(value, "myProtocol");
+ }
+ });
+ resp.headersComplete.connect([&](bool) { Finish(); });
+
+ serverPipe->Listen([&]() {
+ auto conn = serverPipe->Accept();
+ auto ws = WebSocket::CreateServer(*conn, "foo", "13", "myProtocol");
+ ws->open.connect([&](StringRef protocol) {
+ ++gotOpen;
+ ASSERT_EQ(protocol, "myProtocol");
+ });
+ });
+ clientPipe->Connect(pipeName, [&] {
+ clientPipe->StartRead();
+ clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
+ resp.Execute(StringRef{buf.base, size});
+ if (resp.HasError()) Finish();
+ ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotProtocol, 1);
+ ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketTest, CreateServerBadVersion) {
+ int gotStatus = 0;
+ int gotVersion = 0;
+ int gotUpgrade = 0;
+
+ HttpParser resp{HttpParser::kResponse};
+ resp.status.connect([&](StringRef status) {
+ ++gotStatus;
+ ASSERT_EQ(resp.GetStatusCode(), 426u) << "status: " << status;
+ });
+ resp.header.connect([&](StringRef name, StringRef value) {
+ if (name.equals_lower("sec-websocket-version")) {
+ ++gotVersion;
+ ASSERT_EQ(value, "13");
+ } else if (name.equals_lower("upgrade")) {
+ ++gotUpgrade;
+ ASSERT_EQ(value, "WebSocket");
+ } else {
+ FAIL() << "unexpected header " << name.str();
+ }
+ });
+ resp.headersComplete.connect([&](bool) { Finish(); });
+
+ serverPipe->Listen([&] {
+ auto conn = serverPipe->Accept();
+ auto ws = WebSocket::CreateServer(*conn, "foo", "14");
+ ws->open.connect([&](StringRef) {
+ Finish();
+ FAIL();
+ });
+ });
+ clientPipe->Connect(pipeName, [&] {
+ clientPipe->StartRead();
+ clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
+ resp.Execute(StringRef{buf.base, size});
+ if (resp.HasError()) Finish();
+ ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
+ });
+ });
+
+ loop->Run();
+
+ if (HasFatalFailure()) return;
+ ASSERT_EQ(gotStatus, 1);
+ ASSERT_EQ(gotVersion, 1);
+ ASSERT_EQ(gotUpgrade, 1);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/WebSocketTest.h b/wpiutil/src/test/native/cpp/WebSocketTest.h
new file mode 100644
index 0000000..8b40440
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketTest.h
@@ -0,0 +1,73 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include <cstdio>
+#include <memory>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "wpi/ArrayRef.h"
+#include "wpi/uv/Loop.h"
+#include "wpi/uv/Pipe.h"
+#include "wpi/uv/Timer.h"
+
+namespace wpi {
+
+class WebSocketTest : public ::testing::Test {
+ public:
+ static const char* pipeName;
+
+ static void SetUpTestCase();
+
+ WebSocketTest() {
+ loop = uv::Loop::Create();
+ clientPipe = uv::Pipe::Create(loop);
+ serverPipe = uv::Pipe::Create(loop);
+
+ serverPipe->Bind(pipeName);
+
+#if 0
+ auto debugTimer = uv::Timer::Create(loop);
+ debugTimer->timeout.connect([this] {
+ std::printf("Active handles:\n");
+ uv_print_active_handles(loop->GetRaw(), stdout);
+ });
+ debugTimer->Start(uv::Timer::Time{100}, uv::Timer::Time{100});
+ debugTimer->Unreference();
+#endif
+
+ auto failTimer = uv::Timer::Create(loop);
+ failTimer->timeout.connect([this] {
+ loop->Stop();
+ FAIL() << "loop failed to terminate";
+ });
+ failTimer->Start(uv::Timer::Time{1000});
+ failTimer->Unreference();
+ }
+
+ ~WebSocketTest() { Finish(); }
+
+ void Finish() {
+ loop->Walk([](uv::Handle& it) { it.Close(); });
+ }
+
+ static std::vector<uint8_t> BuildHeader(uint8_t opcode, bool fin,
+ bool masking, uint64_t len);
+ static std::vector<uint8_t> BuildMessage(uint8_t opcode, bool fin,
+ bool masking,
+ ArrayRef<uint8_t> data);
+ static void AdjustMasking(MutableArrayRef<uint8_t> message);
+ static const uint8_t testMask[4];
+
+ std::shared_ptr<uv::Loop> loop;
+ std::shared_ptr<uv::Pipe> clientPipe;
+ std::shared_ptr<uv::Pipe> serverPipe;
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/WorkerThreadTest.cpp b/wpiutil/src/test/native/cpp/WorkerThreadTest.cpp
new file mode 100644
index 0000000..610029c
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WorkerThreadTest.cpp
@@ -0,0 +1,73 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/WorkerThread.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <thread>
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+
+TEST(WorkerThread, Future) {
+ WorkerThread<int(bool)> worker;
+ future<int> f =
+ worker.QueueWork([](bool v) -> int { return v ? 1 : 2; }, true);
+ ASSERT_EQ(f.get(), 1);
+}
+
+TEST(WorkerThread, FutureVoid) {
+ int callbacks = 0;
+ WorkerThread<void(int)> worker;
+ future<void> f = worker.QueueWork(
+ [&](int v) {
+ ++callbacks;
+ ASSERT_EQ(v, 3);
+ },
+ 3);
+ f.get();
+ ASSERT_EQ(callbacks, 1);
+}
+
+TEST(WorkerThread, Loop) {
+ int callbacks = 0;
+ WorkerThread<int(bool)> worker;
+ auto loop = uv::Loop::Create();
+ worker.SetLoop(*loop);
+ worker.QueueWorkThen([](bool v) -> int { return v ? 1 : 2; },
+ [&](int v2) {
+ ++callbacks;
+ loop->Stop();
+ ASSERT_EQ(v2, 1);
+ },
+ true);
+ auto f = worker.QueueWork([](bool) -> int { return 2; }, true);
+ ASSERT_EQ(f.get(), 2);
+ loop->Run();
+ ASSERT_EQ(callbacks, 1);
+}
+
+TEST(WorkerThread, LoopVoid) {
+ int callbacks = 0;
+ WorkerThread<void(bool)> worker;
+ auto loop = uv::Loop::Create();
+ worker.SetLoop(*loop);
+ worker.QueueWorkThen([](bool) {},
+ [&]() {
+ ++callbacks;
+ loop->Stop();
+ },
+ true);
+ auto f = worker.QueueWork([](bool) {}, true);
+ f.get();
+ loop->Run();
+ ASSERT_EQ(callbacks, 1);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/future_test.cpp b/wpiutil/src/test/native/cpp/future_test.cpp
new file mode 100644
index 0000000..b9b79a8
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/future_test.cpp
@@ -0,0 +1,84 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/future.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <thread>
+
+namespace wpi {
+
+TEST(Future, Then) {
+ promise<bool> inPromise;
+ future<int> outFuture =
+ inPromise.get_future().then([](bool v) { return v ? 5 : 6; });
+
+ inPromise.set_value(true);
+ ASSERT_EQ(outFuture.get(), 5);
+}
+
+TEST(Future, ThenSame) {
+ promise<bool> inPromise;
+ future<bool> outFuture =
+ inPromise.get_future().then([](bool v) { return !v; });
+
+ inPromise.set_value(true);
+ ASSERT_EQ(outFuture.get(), false);
+}
+
+TEST(Future, ThenFromVoid) {
+ promise<void> inPromise;
+ future<int> outFuture = inPromise.get_future().then([] { return 5; });
+
+ inPromise.set_value();
+ ASSERT_EQ(outFuture.get(), 5);
+}
+
+TEST(Future, ThenToVoid) {
+ promise<bool> inPromise;
+ future<void> outFuture = inPromise.get_future().then([](bool v) {});
+
+ inPromise.set_value(true);
+ ASSERT_TRUE(outFuture.is_ready());
+}
+
+TEST(Future, ThenVoidVoid) {
+ promise<void> inPromise;
+ future<void> outFuture = inPromise.get_future().then([] {});
+
+ inPromise.set_value();
+ ASSERT_TRUE(outFuture.is_ready());
+}
+
+TEST(Future, Implicit) {
+ promise<bool> inPromise;
+ future<int> outFuture = inPromise.get_future();
+
+ inPromise.set_value(true);
+ ASSERT_EQ(outFuture.get(), 1);
+}
+
+TEST(Future, MoveSame) {
+ promise<bool> inPromise;
+ future<bool> outFuture1 = inPromise.get_future();
+ future<bool> outFuture(std::move(outFuture1));
+
+ inPromise.set_value(true);
+ ASSERT_EQ(outFuture.get(), true);
+}
+
+TEST(Future, MoveVoid) {
+ promise<void> inPromise;
+ future<void> outFuture1 = inPromise.get_future();
+ future<void> outFuture(std::move(outFuture1));
+
+ inPromise.set_value();
+ ASSERT_TRUE(outFuture.is_ready());
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/hostname.cpp b/wpiutil/src/test/native/cpp/hostname.cpp
new file mode 100644
index 0000000..d5ec535
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/hostname.cpp
@@ -0,0 +1,24 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/hostname.h"
+
+#include "gtest/gtest.h"
+#include "wpi/SmallString.h"
+#include "wpi/SmallVector.h"
+
+namespace wpi {
+TEST(HostNameTest, HostNameNotEmpty) { ASSERT_NE(GetHostname(), ""); }
+TEST(HostNameTest, HostNameNotEmptySmallVector) {
+ SmallVector<char, 256> name;
+ ASSERT_NE(GetHostname(name), "");
+}
+TEST(HostNameTest, HostNameEq) {
+ SmallVector<char, 256> nameBuf;
+ ASSERT_EQ(GetHostname(nameBuf), GetHostname());
+}
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/json/unit-algorithms.cpp b/wpiutil/src/test/native/cpp/json/unit-algorithms.cpp
new file mode 100644
index 0000000..f1a8745
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-algorithms.cpp
@@ -0,0 +1,310 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+#include <algorithm>
+
+class JsonAlgorithmsTest : public ::testing::Test {
+ protected:
+ json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"};
+ json j_object = {{"one", 1}, {"two", 2}};
+};
+
+// non-modifying sequence operations
+TEST_F(JsonAlgorithmsTest, AllOf)
+{
+ EXPECT_TRUE(std::all_of(j_array.begin(), j_array.end(), [](const json & value)
+ {
+ return value.size() > 0;
+ }));
+ EXPECT_TRUE(std::all_of(j_object.begin(), j_object.end(), [](const json & value)
+ {
+ return value.type() == json::value_t::number_integer;
+ }));
+}
+
+TEST_F(JsonAlgorithmsTest, AnyOf)
+{
+ EXPECT_TRUE(std::any_of(j_array.begin(), j_array.end(), [](const json & value)
+ {
+ return value.is_string() && value.get<std::string>() == "foo";
+ }));
+ EXPECT_TRUE(std::any_of(j_object.begin(), j_object.end(), [](const json & value)
+ {
+ return value.get<int>() > 1;
+ }));
+}
+
+TEST_F(JsonAlgorithmsTest, NoneOf)
+{
+ EXPECT_TRUE(std::none_of(j_array.begin(), j_array.end(), [](const json & value)
+ {
+ return value.size() == 0;
+ }));
+ EXPECT_TRUE(std::none_of(j_object.begin(), j_object.end(), [](const json & value)
+ {
+ return value.get<int>() <= 0;
+ }));
+}
+
+TEST_F(JsonAlgorithmsTest, ForEachReading)
+{
+ int sum = 0;
+
+ std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value)
+ {
+ if (value.is_number())
+ {
+ sum += static_cast<int>(value);
+ }
+ });
+
+ EXPECT_EQ(sum, 45);
+}
+
+TEST_F(JsonAlgorithmsTest, ForEachWriting)
+{
+ auto add17 = [](json & value)
+ {
+ if (value.is_array())
+ {
+ value.push_back(17);
+ }
+ };
+
+ std::for_each(j_array.begin(), j_array.end(), add17);
+
+ EXPECT_EQ(j_array[6], json({1, 2, 3, 17}));
+}
+
+TEST_F(JsonAlgorithmsTest, Count)
+{
+ EXPECT_EQ(std::count(j_array.begin(), j_array.end(), json(true)), 1);
+}
+
+TEST_F(JsonAlgorithmsTest, CountIf)
+{
+ auto count1 = std::count_if(j_array.begin(), j_array.end(), [](const json & value)
+ {
+ return (value.is_number());
+ });
+ EXPECT_EQ(count1, 3);
+ auto count2 = std::count_if(j_array.begin(), j_array.end(), [](const json&)
+ {
+ return true;
+ });
+ EXPECT_EQ(count2, 9);
+}
+
+TEST_F(JsonAlgorithmsTest, Mismatch)
+{
+ json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"};
+ auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin());
+ EXPECT_EQ(*res.first, json({{"one", 1}, {"two", 2}}));
+ EXPECT_EQ(*res.second, json({{"one", 1}, {"two", 2}, {"three", 3}}));
+}
+
+TEST_F(JsonAlgorithmsTest, EqualOperatorEquals)
+{
+ EXPECT_TRUE(std::equal(j_array.begin(), j_array.end(), j_array.begin()));
+ EXPECT_TRUE(std::equal(j_object.begin(), j_object.end(), j_object.begin()));
+ EXPECT_FALSE(std::equal(j_array.begin(), j_array.end(), j_object.begin()));
+}
+
+TEST_F(JsonAlgorithmsTest, EqualUserComparison)
+{
+ // compare objects only by size of its elements
+ json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"};
+ EXPECT_FALSE(std::equal(j_array.begin(), j_array.end(), j_array2.begin()));
+ EXPECT_TRUE(std::equal(j_array.begin(), j_array.end(), j_array2.begin(),
+ [](const json & a, const json & b)
+ {
+ return (a.size() == b.size());
+ }));
+}
+
+TEST_F(JsonAlgorithmsTest, Find)
+{
+ auto it = std::find(j_array.begin(), j_array.end(), json(false));
+ EXPECT_EQ(std::distance(j_array.begin(), it), 5);
+}
+
+TEST_F(JsonAlgorithmsTest, FindIf)
+{
+ auto it = std::find_if(j_array.begin(), j_array.end(),
+ [](const json & value)
+ {
+ return value.is_boolean();
+ });
+ EXPECT_EQ(std::distance(j_array.begin(), it), 4);
+}
+
+TEST_F(JsonAlgorithmsTest, FindIfNot)
+{
+ auto it = std::find_if_not(j_array.begin(), j_array.end(),
+ [](const json & value)
+ {
+ return value.is_number();
+ });
+ EXPECT_EQ(std::distance(j_array.begin(), it), 3);
+}
+
+TEST_F(JsonAlgorithmsTest, AdjacentFind)
+{
+ EXPECT_EQ(std::adjacent_find(j_array.begin(), j_array.end()), j_array.end());
+ auto it = std::adjacent_find(j_array.begin(), j_array.end(),
+ [](const json & v1, const json & v2)
+ {
+ return v1.type() == v2.type();
+ });
+ EXPECT_EQ(it, j_array.begin());
+}
+
+// modifying sequence operations
+TEST_F(JsonAlgorithmsTest, Reverse)
+{
+ std::reverse(j_array.begin(), j_array.end());
+ EXPECT_EQ(j_array, json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13}));
+}
+
+TEST_F(JsonAlgorithmsTest, Rotate)
+{
+ std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end());
+ EXPECT_EQ(j_array, json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13}));
+}
+
+TEST_F(JsonAlgorithmsTest, Partition)
+{
+ auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v)
+ {
+ return v.is_string();
+ });
+ EXPECT_EQ(std::distance(j_array.begin(), it), 2);
+ EXPECT_FALSE(it[2].is_string());
+}
+
+// sorting operations
+TEST_F(JsonAlgorithmsTest, SortOperatorEquals)
+{
+ json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
+ std::sort(j.begin(), j.end());
+ EXPECT_EQ(j, json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
+}
+
+TEST_F(JsonAlgorithmsTest, SortUserComparison)
+{
+ json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr};
+ std::sort(j.begin(), j.end(), [](const json & a, const json & b)
+ {
+ return a.size() < b.size();
+ });
+ EXPECT_EQ(j, json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}}));
+}
+
+TEST_F(JsonAlgorithmsTest, SortObject)
+{
+ json j({{"one", 1}, {"two", 2}});
+ EXPECT_THROW_MSG(std::sort(j.begin(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+}
+
+TEST_F(JsonAlgorithmsTest, PartialSort)
+{
+ json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
+ std::partial_sort(j.begin(), j.begin() + 4, j.end());
+ EXPECT_EQ(j, json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13}));
+}
+
+// set operations
+TEST_F(JsonAlgorithmsTest, Merge)
+{
+ json j1 = {2, 4, 6, 8};
+ json j2 = {1, 2, 3, 5, 7};
+ json j3;
+
+ std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+ EXPECT_EQ(j3, json({1, 2, 2, 3, 4, 5, 6, 7, 8}));
+}
+
+TEST_F(JsonAlgorithmsTest, SetDifference)
+{
+ json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
+ json j2 = {1, 2, 3, 5, 7};
+ json j3;
+
+ std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+ EXPECT_EQ(j3, json({4, 6, 8}));
+}
+
+TEST_F(JsonAlgorithmsTest, SetIntersection)
+{
+ json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
+ json j2 = {1, 2, 3, 5, 7};
+ json j3;
+
+ std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+ EXPECT_EQ(j3, json({1, 2, 3, 5, 7}));
+}
+
+TEST_F(JsonAlgorithmsTest, SetUnion)
+{
+ json j1 = {2, 4, 6, 8};
+ json j2 = {1, 2, 3, 5, 7};
+ json j3;
+
+ std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+ EXPECT_EQ(j3, json({1, 2, 3, 4, 5, 6, 7, 8}));
+}
+
+TEST_F(JsonAlgorithmsTest, SetSymmetricDifference)
+{
+ json j1 = {2, 4, 6, 8};
+ json j2 = {1, 2, 3, 5, 7};
+ json j3;
+
+ std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
+ EXPECT_EQ(j3, json({1, 3, 4, 5, 6, 7, 8}));
+}
+
+TEST_F(JsonAlgorithmsTest, HeapOperations)
+{
+ std::make_heap(j_array.begin(), j_array.end());
+ EXPECT_TRUE(std::is_heap(j_array.begin(), j_array.end()));
+ std::sort_heap(j_array.begin(), j_array.end());
+ EXPECT_EQ(j_array, json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-capacity.cpp b/wpiutil/src/test/native/cpp/json/unit-capacity.cpp
new file mode 100644
index 0000000..8f5433e
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-capacity.cpp
@@ -0,0 +1,528 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonEmptyTest, Boolean)
+{
+ json j = true;
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_FALSE(j.empty());
+ EXPECT_FALSE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, String)
+{
+ json j = "hello world";
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_FALSE(j.empty());
+ EXPECT_FALSE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, ArrayEmpty)
+{
+ json j = json::array();
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_TRUE(j.empty());
+ EXPECT_TRUE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, ArrayFilled)
+{
+ json j = {1, 2, 3};
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_FALSE(j.empty());
+ EXPECT_FALSE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, ObjectEmpty)
+{
+ json j = json::object();
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_TRUE(j.empty());
+ EXPECT_TRUE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, ObjectFilled)
+{
+ json j = {{"one", 1}, {"two", 2}, {"three", 3}};
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_FALSE(j.empty());
+ EXPECT_FALSE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, NumberInteger)
+{
+ json j = 23;
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_FALSE(j.empty());
+ EXPECT_FALSE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, NumberUnsigned)
+{
+ json j = 23u;
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_FALSE(j.empty());
+ EXPECT_FALSE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, NumberFloat)
+{
+ json j = 23.42;
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_FALSE(j.empty());
+ EXPECT_FALSE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonEmptyTest, Null)
+{
+ json j = nullptr;
+ json j_const(j);
+
+ // result of empty
+ {
+ EXPECT_TRUE(j.empty());
+ EXPECT_TRUE(j_const.empty());
+ }
+
+ // definition of empty
+ {
+ EXPECT_EQ(j.empty(), (j.begin() == j.end()));
+ EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
+ }
+}
+
+TEST(JsonSizeTest, Boolean)
+{
+ json j = true;
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 1u);
+ EXPECT_EQ(j_const.size(), 1u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, String)
+{
+ json j = "hello world";
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 1u);
+ EXPECT_EQ(j_const.size(), 1u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, ArrayEmpty)
+{
+ json j = json::array();
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 0u);
+ EXPECT_EQ(j_const.size(), 0u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, ArrayFilled)
+{
+ json j = {1, 2, 3};
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 3u);
+ EXPECT_EQ(j_const.size(), 3u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, ObjectEmpty)
+{
+ json j = json::object();
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 0u);
+ EXPECT_EQ(j_const.size(), 0u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, ObjectFilled)
+{
+ json j = {{"one", 1}, {"two", 2}, {"three", 3}};
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 3u);
+ EXPECT_EQ(j_const.size(), 3u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, NumberInteger)
+{
+ json j = 23;
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 1u);
+ EXPECT_EQ(j_const.size(), 1u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, NumberUnsigned)
+{
+ json j = 23u;
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 1u);
+ EXPECT_EQ(j_const.size(), 1u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, NumberFloat)
+{
+ json j = 23.42;
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 1u);
+ EXPECT_EQ(j_const.size(), 1u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonSizeTest, Null)
+{
+ json j = nullptr;
+ json j_const(j);
+
+ // result of size
+ {
+ EXPECT_EQ(j.size(), 0u);
+ EXPECT_EQ(j_const.size(), 0u);
+ }
+
+ // definition of size
+ {
+ EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
+ EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
+ static_cast<int>(j_const.size()));
+ }
+}
+
+TEST(JsonMaxSizeTest, Boolean)
+{
+ json j = true;
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_EQ(j.max_size(), 1u);
+ EXPECT_EQ(j_const.max_size(), 1u);
+ }
+}
+
+TEST(JsonMaxSizeTest, String)
+{
+ json j = "hello world";
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_EQ(j.max_size(), 1u);
+ EXPECT_EQ(j_const.max_size(), 1u);
+ }
+}
+
+TEST(JsonMaxSizeTest, ArrayEmpty)
+{
+ json j = json::array();
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_GE(j.max_size(), j.size());
+ EXPECT_GE(j_const.max_size(), j_const.size());
+ }
+}
+
+TEST(JsonMaxSizeTest, ArrayFilled)
+{
+ json j = {1, 2, 3};
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_GE(j.max_size(), j.size());
+ EXPECT_GE(j_const.max_size(), j_const.size());
+ }
+}
+
+TEST(JsonMaxSizeTest, ObjectEmpty)
+{
+ json j = json::object();
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_GE(j.max_size(), j.size());
+ EXPECT_GE(j_const.max_size(), j_const.size());
+ }
+}
+
+TEST(JsonMaxSizeTest, ObjectFilled)
+{
+ json j = {{"one", 1}, {"two", 2}, {"three", 3}};
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_GE(j.max_size(), j.size());
+ EXPECT_GE(j_const.max_size(), j_const.size());
+ }
+}
+
+TEST(JsonMaxSizeTest, NumberInteger)
+{
+ json j = 23;
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_EQ(j.max_size(), 1u);
+ EXPECT_EQ(j_const.max_size(), 1u);
+ }
+}
+
+TEST(JsonMaxSizeTest, NumberUnsigned)
+{
+ json j = 23u;
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_EQ(j.max_size(), 1u);
+ EXPECT_EQ(j_const.max_size(), 1u);
+ }
+}
+
+TEST(JsonMaxSizeTest, NumberFloat)
+{
+ json j = 23.42;
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_EQ(j.max_size(), 1u);
+ EXPECT_EQ(j_const.max_size(), 1u);
+ }
+}
+
+TEST(JsonMaxSizeTest, Null)
+{
+ json j = nullptr;
+ json j_const(j);
+
+ // result of max_size
+ {
+ EXPECT_EQ(j.max_size(), 0u);
+ EXPECT_EQ(j_const.max_size(), 0u);
+ }
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-cbor.cpp b/wpiutil/src/test/native/cpp/json/unit-cbor.cpp
new file mode 100644
index 0000000..84e98eb
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-cbor.cpp
@@ -0,0 +1,1701 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+#include <fstream>
+
+TEST(CborDiscardedTest, Case)
+{
+ // discarded values are not serialized
+ json j = json::value_t::discarded;
+ const auto result = json::to_cbor(j);
+ ASSERT_TRUE(result.empty());
+}
+
+TEST(CborNullTest, Case)
+{
+ json j = nullptr;
+ std::vector<uint8_t> expected = {0xf6};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+TEST(CborBooleanTest, True)
+{
+ json j = true;
+ std::vector<uint8_t> expected = {0xf5};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+TEST(CborBooleanTest, False)
+{
+ json j = false;
+ std::vector<uint8_t> expected = {0xf4};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// -9223372036854775808..-4294967297
+class CborSignedNeg8Test : public ::testing::TestWithParam<int64_t> {};
+TEST_P(CborSignedNeg8Test, Case)
+{
+ // create JSON value with integer number
+ json j = GetParam();
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x3b));
+ uint64_t positive = static_cast<uint64_t>(-1 - GetParam());
+ expected.push_back(static_cast<uint8_t>((positive >> 56) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 48) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 40) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 32) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(positive & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 9u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x3b);
+ uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
+ static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
+ EXPECT_EQ(restored, positive);
+ EXPECT_EQ(-1 - static_cast<int64_t>(restored), GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+static const int64_t neg8_numbers[] = {
+ INT64_MIN,
+ -1000000000000000000,
+ -100000000000000000,
+ -10000000000000000,
+ -1000000000000000,
+ -100000000000000,
+ -10000000000000,
+ -1000000000000,
+ -100000000000,
+ -10000000000,
+ -4294967297,
+};
+
+INSTANTIATE_TEST_CASE_P(CborSignedNeg8Tests, CborSignedNeg8Test,
+ ::testing::ValuesIn(neg8_numbers), );
+
+// -4294967296..-65537
+class CborSignedNeg4Test : public ::testing::TestWithParam<int64_t> {};
+TEST_P(CborSignedNeg4Test, Case)
+{
+ // create JSON value with integer number
+ json j = GetParam();
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x3a));
+ uint32_t positive = static_cast<uint32_t>(static_cast<uint64_t>(-1 - GetParam()) & 0x00000000ffffffff);
+ expected.push_back(static_cast<uint8_t>((positive >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((positive >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(positive & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 5u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x3a);
+ uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
+ static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
+ EXPECT_EQ(restored, positive);
+ EXPECT_EQ(-1ll - restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+static const int64_t neg4_numbers[] = {
+ -65537,
+ -100000,
+ -1000000,
+ -10000000,
+ -100000000,
+ -1000000000,
+ -4294967296,
+};
+
+INSTANTIATE_TEST_CASE_P(CborSignedNeg4Tests, CborSignedNeg4Test,
+ ::testing::ValuesIn(neg4_numbers), );
+
+// -65536..-257
+TEST(CborSignedTest, Neg2)
+{
+ for (int32_t i = -65536; i <= -257; ++i) {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = i;
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x39));
+ uint16_t positive = static_cast<uint16_t>(-1 - i);
+ expected.push_back(static_cast<uint8_t>((positive >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(positive & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 3u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x39);
+ uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
+ EXPECT_EQ(restored, positive);
+ EXPECT_EQ(-1 - restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// -9263 (int 16)
+TEST(CborSignedTest, NegInt16)
+{
+ json j = -9263;
+ std::vector<uint8_t> expected = {0x39,0x24,0x2e};
+
+ const auto result = json::to_cbor(j);
+ ASSERT_EQ(result, expected);
+
+ int16_t restored = static_cast<int16_t>(-1 - ((result[1] << 8) + result[2]));
+ EXPECT_EQ(restored, -9263);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// -256..-24
+TEST(CborSignedTest, Neg1)
+{
+ for (auto i = -256; i < -24; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = i;
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x38));
+ expected.push_back(static_cast<uint8_t>(-1 - i));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 2u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x38);
+ EXPECT_EQ(static_cast<int16_t>(-1 - static_cast<uint8_t>(result[1])), i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// -24..-1
+TEST(CborSignedTest, Neg0)
+{
+ for (auto i = -24; i <= -1; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = i;
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x20 - 1 - static_cast<uint8_t>(i)));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 1u);
+
+ // check individual bytes
+ EXPECT_EQ(static_cast<int8_t>(0x20 - 1 - result[0]), i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// 0..23
+TEST(CborSignedTest, Pos0)
+{
+ for (size_t i = 0; i <= 23; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() = static_cast<int64_t>(i);
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 1u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(i));
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// 24..255
+TEST(CborSignedTest, Pos1)
+{
+ for (size_t i = 24; i <= 255; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() = static_cast<int64_t>(i);
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x18));
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 2u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x18);
+ EXPECT_EQ(result[1], static_cast<uint8_t>(i));
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// 256..65535
+TEST(CborSignedTest, Pos2)
+{
+ for (size_t i = 256; i <= 65535; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() = static_cast<int64_t>(i);
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x19));
+ expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 3u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x19);
+ uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// 65536..4294967295
+class CborSignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
+TEST_P(CborSignedPos4Test, Case)
+{
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() =
+ static_cast<int64_t>(GetParam());
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x1a);
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 5u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x1a);
+ uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
+ static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+static const uint32_t pos4_numbers[] = {
+ 65536u,
+ 77777u,
+ 1048576u,
+};
+
+INSTANTIATE_TEST_CASE_P(CborSignedPos4Tests, CborSignedPos4Test,
+ ::testing::ValuesIn(pos4_numbers), );
+
+// 4294967296..4611686018427387903
+class CborSignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
+TEST_P(CborSignedPos8Test, Case)
+{
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() =
+ static_cast<int64_t>(GetParam());
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x1b);
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 9u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x1b);
+ uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
+ static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+static const uint64_t pos8_numbers[] = {
+ 4294967296ul,
+ 4611686018427387903ul
+};
+
+INSTANTIATE_TEST_CASE_P(CborSignedPos8Tests, CborSignedPos8Test,
+ ::testing::ValuesIn(pos8_numbers), );
+
+/*
+// -32768..-129 (int 16)
+{
+ for (int16_t i = -32768; i <= -129; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = i;
+
+ // check type
+ ASSERT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0xd1);
+ expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result == expected);
+ ASSERT_EQ(result.size(), 3u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0xd1);
+ int16_t restored = (result[1] << 8) + result[2];
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+*/
+
+// 0..23 (Integer)
+TEST(CborUnsignedTest, Pos0)
+{
+ for (size_t i = 0; i <= 23; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with unsigned integer number
+ json j = i;
+
+ // check type
+ ASSERT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 1u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(i));
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// 24..255 (one-byte uint8_t)
+TEST(CborUnsignedTest, Pos1)
+{
+ for (size_t i = 24; i <= 255; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with unsigned integer number
+ json j = i;
+
+ // check type
+ ASSERT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x18);
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 2u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x18);
+ uint8_t restored = static_cast<uint8_t>(result[1]);
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// 256..65535 (two-byte uint16_t)
+TEST(CborUnsignedTest, Pos2)
+{
+ for (size_t i = 256; i <= 65535; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with unsigned integer number
+ json j = i;
+
+ // check type
+ ASSERT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x19);
+ expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 3u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x19);
+ uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// 65536..4294967295 (four-byte uint32_t)
+class CborUnsignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
+TEST_P(CborUnsignedPos4Test, Case)
+{
+ // create JSON value with unsigned integer number
+ json j = GetParam();
+
+ // check type
+ ASSERT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x1a);
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 5u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x1a);
+ uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
+ static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+INSTANTIATE_TEST_CASE_P(CborUnsignedPos4Tests, CborUnsignedPos4Test,
+ ::testing::ValuesIn(pos4_numbers), );
+
+// 4294967296..4611686018427387903 (eight-byte uint64_t)
+class CborUnsignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
+TEST_P(CborUnsignedPos8Test, Case)
+{
+ // create JSON value with integer number
+ json j = GetParam();
+
+ // check type
+ ASSERT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x1b);
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), 9u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], 0x1b);
+ uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
+ static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+INSTANTIATE_TEST_CASE_P(CborUnsignedPos8Tests, CborUnsignedPos8Test,
+ ::testing::ValuesIn(pos8_numbers), );
+
+// 3.1415925
+TEST(CborFloatTest, Number)
+{
+ double v = 3.1415925;
+ json j = v;
+ std::vector<uint8_t> expected = {0xfb,0x40,0x09,0x21,0xfb,0x3f,0xa6,0xde,0xfc};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ EXPECT_EQ(json::from_cbor(result), v);
+}
+
+TEST(CborFloatTest, HalfInfinity)
+{
+ json j = json::from_cbor(std::vector<uint8_t>({0xf9,0x7c,0x00}));
+ double d = j;
+ EXPECT_FALSE(std::isfinite(d));
+ EXPECT_EQ(j.dump(), "null");
+}
+
+TEST(CborFloatTest, HalfNaN)
+{
+ json j = json::from_cbor(std::vector<uint8_t>({0xf9,0x7c,0x01}));
+ double d = j;
+ EXPECT_TRUE(std::isnan(d));
+ EXPECT_EQ(j.dump(), "null");
+}
+
+// N = 0..23
+TEST(CborStringTest, String1)
+{
+ for (size_t N = 0; N <= 0x17; ++N)
+ {
+ SCOPED_TRACE(N);
+
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(N, 'x');
+ json j = s;
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x60 + N));
+ for (size_t i = 0; i < N; ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), N + 1);
+ // check that no null byte is appended
+ if (N > 0)
+ {
+ EXPECT_NE(result.back(), '\x00');
+ }
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// N = 24..255
+TEST(CborStringTest, String2)
+{
+ for (size_t N = 24; N <= 255; ++N)
+ {
+ SCOPED_TRACE(N);
+
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(N, 'x');
+ json j = s;
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0x78));
+ expected.push_back(static_cast<uint8_t>(N));
+ for (size_t i = 0; i < N; ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), N + 2);
+ // check that no null byte is appended
+ EXPECT_NE(result.back(), '\x00');
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+ }
+}
+
+// N = 256..65535
+class CborString3Test : public ::testing::TestWithParam<size_t> {};
+TEST_P(CborString3Test, Case)
+{
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(GetParam(), 'x');
+ json j = s;
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x79);
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+ for (size_t i = 0; i < GetParam(); ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), GetParam() + 3);
+ // check that no null byte is appended
+ EXPECT_NE(result.back(), '\x00');
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+static size_t string3_lens[] = {
+ 256u,
+ 999u,
+ 1025u,
+ 3333u,
+ 2048u,
+ 65535u
+};
+
+INSTANTIATE_TEST_CASE_P(CborString3Tests, CborString3Test,
+ ::testing::ValuesIn(string3_lens), );
+
+// N = 65536..4294967295
+class CborString5Test : public ::testing::TestWithParam<size_t> {};
+TEST_P(CborString5Test, Case)
+{
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(GetParam(), 'x');
+ json j = s;
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(0x7a);
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+ for (size_t i = 0; i < GetParam(); ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // compare result + size
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+ ASSERT_EQ(result.size(), GetParam() + 5);
+ // check that no null byte is appended
+ EXPECT_NE(result.back(), '\x00');
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+static size_t string5_lens[] = {
+ 65536u,
+ 77777u,
+ 1048576u
+};
+
+INSTANTIATE_TEST_CASE_P(CborString5Tests, CborString5Test,
+ ::testing::ValuesIn(string5_lens), );
+
+TEST(CborArrayTest, Empty)
+{
+ json j = json::array();
+ std::vector<uint8_t> expected = {0x80};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// [null]
+TEST(CborArrayTest, Null)
+{
+ json j = {nullptr};
+ std::vector<uint8_t> expected = {0x81,0xf6};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// [1,2,3,4,5]
+TEST(CborArrayTest, Simple)
+{
+ json j = json::parse("[1,2,3,4,5]");
+ std::vector<uint8_t> expected = {0x85,0x01,0x02,0x03,0x04,0x05};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// [[[[]]]]
+TEST(CborArrayTest, NestEmpty)
+{
+ json j = json::parse("[[[[]]]]");
+ std::vector<uint8_t> expected = {0x81,0x81,0x81,0x80};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// array with uint16_t elements
+TEST(CborArrayTest, UInt16)
+{
+ json j(257, nullptr);
+ std::vector<uint8_t> expected(j.size() + 3, 0xf6); // all null
+ expected[0] = static_cast<char>(0x99); // array 16 bit
+ expected[1] = 0x01; // size (0x0101), byte 0
+ expected[2] = 0x01; // size (0x0101), byte 1
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// array with uint32_t elements
+TEST(CborArrayTest, UInt32)
+{
+ json j(65793, nullptr);
+ std::vector<uint8_t> expected(j.size() + 5, 0xf6); // all null
+ expected[0] = static_cast<char>(0x9a); // array 32 bit
+ expected[1] = 0x00; // size (0x00010101), byte 0
+ expected[2] = 0x01; // size (0x00010101), byte 1
+ expected[3] = 0x01; // size (0x00010101), byte 2
+ expected[4] = 0x01; // size (0x00010101), byte 3
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+/*
+// array with uint64_t elements
+TEST(CborArrayTest, UInt64)
+{
+ json j(4294967296, nullptr);
+ std::vector<uint8_t> expected(j.size() + 9, 0xf6); // all null
+ expected[0] = 0x9b; // array 64 bit
+ expected[1] = 0x00; // size (0x0000000100000000), byte 0
+ expected[2] = 0x00; // size (0x0000000100000000), byte 1
+ expected[3] = 0x00; // size (0x0000000100000000), byte 2
+ expected[4] = 0x01; // size (0x0000000100000000), byte 3
+ expected[5] = 0x00; // size (0x0000000100000000), byte 4
+ expected[6] = 0x00; // size (0x0000000100000000), byte 5
+ expected[7] = 0x00; // size (0x0000000100000000), byte 6
+ expected[8] = 0x00; // size (0x0000000100000000), byte 7
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+*/
+
+TEST(CborObjectTest, Empty)
+{
+ json j = json::object();
+ std::vector<uint8_t> expected = {0xa0};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// {"":null}
+TEST(CborObjectTest, EmptyKey)
+{
+ json j = {{"", nullptr}};
+ std::vector<uint8_t> expected = {0xa1,0x60,0xf6};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// {"a": {"b": {"c": {}}}}
+TEST(CborObjectTest, NestedEmpty)
+{
+ json j = json::parse("{\"a\": {\"b\": {\"c\": {}}}}");
+ std::vector<uint8_t> expected = {0xa1,0x61,0x61,0xa1,0x61,0x62,0xa1,0x61,0x63,0xa0};
+ const auto result = json::to_cbor(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// object with uint8_t elements
+TEST(CborObjectTest, UInt8)
+{
+ json j;
+ for (auto i = 0; i < 255; ++i)
+ {
+ // format i to a fixed width of 5
+ // each entry will need 7 bytes: 6 for string, 1 for null
+ std::stringstream ss;
+ ss << std::setw(5) << std::setfill('0') << i;
+ j.emplace(ss.str(), nullptr);
+ }
+
+ const auto result = json::to_cbor(j);
+
+ // Checking against an expected vector byte by byte is
+ // difficult, because no assumption on the order of key/value
+ // pairs are made. We therefore only check the prefix (type and
+ // size and the overall size. The rest is then handled in the
+ // roundtrip check.
+ ASSERT_EQ(result.size(), 1787u); // 1 type, 1 size, 255*7 content
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xb8)); // map 8 bit
+ EXPECT_EQ(result[1], static_cast<uint8_t>(0xff)); // size byte (0xff)
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// object with uint16_t elements
+TEST(CborObjectTest, UInt16)
+{
+ json j;
+ for (auto i = 0; i < 256; ++i)
+ {
+ // format i to a fixed width of 5
+ // each entry will need 7 bytes: 6 for string, 1 for null
+ std::stringstream ss;
+ ss << std::setw(5) << std::setfill('0') << i;
+ j.emplace(ss.str(), nullptr);
+ }
+
+ const auto result = json::to_cbor(j);
+
+ // Checking against an expected vector byte by byte is
+ // difficult, because no assumption on the order of key/value
+ // pairs are made. We therefore only check the prefix (type and
+ // size and the overall size. The rest is then handled in the
+ // roundtrip check.
+ ASSERT_EQ(result.size(), 1795u); // 1 type, 2 size, 256*7 content
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xb9)); // map 16 bit
+ EXPECT_EQ(result[1], 0x01); // byte 0 of size (0x0100)
+ EXPECT_EQ(result[2], 0x00); // byte 1 of size (0x0100)
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// object with uint32_t elements
+TEST(CborObjectTest, UInt32)
+{
+ json j;
+ for (auto i = 0; i < 65536; ++i)
+ {
+ // format i to a fixed width of 5
+ // each entry will need 7 bytes: 6 for string, 1 for null
+ std::stringstream ss;
+ ss << std::setw(5) << std::setfill('0') << i;
+ j.emplace(ss.str(), nullptr);
+ }
+
+ const auto result = json::to_cbor(j);
+
+ // Checking against an expected vector byte by byte is
+ // difficult, because no assumption on the order of key/value
+ // pairs are made. We therefore only check the prefix (type and
+ // size and the overall size. The rest is then handled in the
+ // roundtrip check.
+ ASSERT_EQ(result.size(), 458757u); // 1 type, 4 size, 65536*7 content
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xba)); // map 32 bit
+ EXPECT_EQ(result[1], 0x00); // byte 0 of size (0x00010000)
+ EXPECT_EQ(result[2], 0x01); // byte 1 of size (0x00010000)
+ EXPECT_EQ(result[3], 0x00); // byte 2 of size (0x00010000)
+ EXPECT_EQ(result[4], 0x00); // byte 3 of size (0x00010000)
+
+ // roundtrip
+ EXPECT_EQ(json::from_cbor(result), j);
+}
+
+// 0x7b (string)
+TEST(CborAdditionalDeserializationTest, Case7b)
+{
+ std::vector<uint8_t> given{0x7b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x61};
+ json j = json::from_cbor(given);
+ EXPECT_EQ(j, "a");
+}
+
+// 0x9b (array)
+TEST(CborAdditionalDeserializationTest, Case9b)
+{
+ std::vector<uint8_t> given{0x9b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xf4};
+ json j = json::from_cbor(given);
+ EXPECT_EQ(j, json::parse("[false]"));
+}
+
+// 0xbb (map)
+TEST(CborAdditionalDeserializationTest, Casebb)
+{
+ std::vector<uint8_t> given{0xbb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x60,0xf4};
+ json j = json::from_cbor(given);
+ EXPECT_EQ(j, json::parse("{\"\": false}"));
+}
+
+TEST(CborErrorTest, TooShortByteVector)
+{
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x18})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x19})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x19,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 6: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 7: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 8: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 9: unexpected end of input");
+}
+
+TEST(CborErrorTest, UnsupportedBytesConcrete)
+{
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1c})), json::parse_error,
+ "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0x1c");
+ EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0xf8})), json::parse_error,
+ "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0xf8");
+}
+
+class CborUnsupportedBytesTest : public ::testing::TestWithParam<uint8_t> {
+};
+TEST_P(CborUnsupportedBytesTest, Case)
+{
+ EXPECT_THROW(json::from_cbor(std::vector<uint8_t>({GetParam()})), json::parse_error);
+}
+
+static const uint8_t unsupported_bytes_cases[] = {
+ // ?
+ 0x1c, 0x1d, 0x1e, 0x1f,
+ // ?
+ 0x3c, 0x3d, 0x3e, 0x3f,
+ // byte strings
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ // byte strings
+ 0x58, 0x59, 0x5a, 0x5b,
+ // ?
+ 0x5c, 0x5d, 0x5e,
+ // byte string
+ 0x5f,
+ // ?
+ 0x7c, 0x7d, 0x7e,
+ // ?
+ 0x9c, 0x9d, 0x9e,
+ // ?
+ 0xbc, 0xbd, 0xbe,
+ // date/time
+ 0xc0, 0xc1,
+ // bignum
+ 0xc2, 0xc3,
+ // fraction
+ 0xc4,
+ // bigfloat
+ 0xc5,
+ // tagged item
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4,
+ // expected conversion
+ 0xd5, 0xd6, 0xd7,
+ // more tagged items
+ 0xd8, 0xd9, 0xda, 0xdb,
+ // ?
+ 0xdc, 0xdd, 0xde, 0xdf,
+ // (simple value)
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
+ // undefined
+ 0xf7,
+ // simple value
+ 0xf8,
+};
+
+INSTANTIATE_TEST_CASE_P(CborUnsupportedBytesTests, CborUnsupportedBytesTest,
+ ::testing::ValuesIn(unsupported_bytes_cases), );
+#if 0
+// use this testcase outside [hide] to run it with Valgrind
+TEST(CborRoundtripTest, Sample)
+{
+ std::string filename = "test/data/json_testsuite/sample.json";
+
+ // parse JSON file
+ std::ifstream f_json(filename);
+ json j1 = json::parse(f_json);
+
+ // parse CBOR file
+ std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
+ std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_cbor)),
+ std::istreambuf_iterator<char>());
+ json j2;
+ j2 = json::from_cbor(packed);
+
+ // compare parsed JSON values
+ EXPECT_EQ(j1, j2);
+
+ // check with different start index
+ packed.insert(packed.begin(), 5, 0xff);
+ EXPECT_EQ(j1, json::from_cbor(packed, 5));
+}
+
+/*
+The following test cases were found during a two-day session with
+AFL-Fuzz. As a result, empty byte vectors and excessive lengths are
+detected.
+*/
+class CborRegressionFuzzTest : public ::testing::TestWithParam<const char*> {};
+TEST_P(CborRegressionFuzzTest, Case)
+{
+ try
+ {
+ // parse CBOR file
+ std::ifstream f_cbor(GetParam(), std::ios::binary);
+ std::vector<uint8_t> vec1(
+ (std::istreambuf_iterator<char>(f_cbor)),
+ std::istreambuf_iterator<char>());
+ json j1 = json::from_cbor(vec1);
+
+ try
+ {
+ // step 2: round trip
+ std::string vec2 = json::to_cbor(j1);
+
+ // parse serialization
+ json j2 = json::from_cbor(vec2);
+
+ // deserializations must match
+ EXPECT_EQ(j1, j2);
+ }
+ catch (const json::parse_error&)
+ {
+ // parsing a CBOR serialization must not fail
+ FAIL();
+ }
+ }
+ catch (const json::parse_error&)
+ {
+ // parse errors are ok, because input may be random bytes
+ }
+}
+
+static const char* fuzz_test_cases[] = {
+ "test/data/cbor_regression/test01",
+ "test/data/cbor_regression/test02",
+ "test/data/cbor_regression/test03",
+ "test/data/cbor_regression/test04",
+ "test/data/cbor_regression/test05",
+ "test/data/cbor_regression/test06",
+ "test/data/cbor_regression/test07",
+ "test/data/cbor_regression/test08",
+ "test/data/cbor_regression/test09",
+ "test/data/cbor_regression/test10",
+ "test/data/cbor_regression/test11",
+ "test/data/cbor_regression/test12",
+ "test/data/cbor_regression/test13",
+ "test/data/cbor_regression/test14",
+ "test/data/cbor_regression/test15",
+ "test/data/cbor_regression/test16",
+ "test/data/cbor_regression/test17",
+ "test/data/cbor_regression/test18",
+ "test/data/cbor_regression/test19",
+ "test/data/cbor_regression/test20",
+ "test/data/cbor_regression/test21",
+};
+
+INSTANTIATE_TEST_CASE_P(CborRegressionFuzzTests, CborRegressionFuzzTest,
+ ::testing::ValuesIn(fuzz_test_cases));
+
+class CborRegressionFlynnTest : public ::testing::TestWithParam<const char*> {};
+TEST_F(CborRegressionFlynnTest, Case)
+{
+ // parse JSON file
+ std::ifstream f_json(GetParam());
+ json j1 = json::parse(f_json);
+
+ // parse CBOR file
+ std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
+ std::vector<uint8_t> packed(
+ (std::istreambuf_iterator<char>(f_cbor)),
+ std::istreambuf_iterator<char>());
+ json j2;
+ j2 = json::from_cbor(packed);
+
+ // compare parsed JSON values
+ EXPECT_EQ(j1, j2);
+}
+
+static const char* flynn_test_cases[] = {
+ "test/data/json_nlohmann_tests/all_unicode.json",
+ "test/data/json.org/1.json",
+ "test/data/json.org/2.json",
+ "test/data/json.org/3.json",
+ "test/data/json.org/4.json",
+ "test/data/json.org/5.json",
+ "test/data/json_roundtrip/roundtrip01.json",
+ "test/data/json_roundtrip/roundtrip02.json",
+ "test/data/json_roundtrip/roundtrip03.json",
+ "test/data/json_roundtrip/roundtrip04.json",
+ "test/data/json_roundtrip/roundtrip05.json",
+ "test/data/json_roundtrip/roundtrip06.json",
+ "test/data/json_roundtrip/roundtrip07.json",
+ "test/data/json_roundtrip/roundtrip08.json",
+ "test/data/json_roundtrip/roundtrip09.json",
+ "test/data/json_roundtrip/roundtrip10.json",
+ "test/data/json_roundtrip/roundtrip11.json",
+ "test/data/json_roundtrip/roundtrip12.json",
+ "test/data/json_roundtrip/roundtrip13.json",
+ "test/data/json_roundtrip/roundtrip14.json",
+ "test/data/json_roundtrip/roundtrip15.json",
+ "test/data/json_roundtrip/roundtrip16.json",
+ "test/data/json_roundtrip/roundtrip17.json",
+ "test/data/json_roundtrip/roundtrip18.json",
+ "test/data/json_roundtrip/roundtrip19.json",
+ "test/data/json_roundtrip/roundtrip20.json",
+ "test/data/json_roundtrip/roundtrip21.json",
+ "test/data/json_roundtrip/roundtrip22.json",
+ "test/data/json_roundtrip/roundtrip23.json",
+ "test/data/json_roundtrip/roundtrip24.json",
+ "test/data/json_roundtrip/roundtrip25.json",
+ "test/data/json_roundtrip/roundtrip26.json",
+ "test/data/json_roundtrip/roundtrip27.json",
+ "test/data/json_roundtrip/roundtrip28.json",
+ "test/data/json_roundtrip/roundtrip29.json",
+ "test/data/json_roundtrip/roundtrip30.json",
+ "test/data/json_roundtrip/roundtrip31.json",
+ "test/data/json_roundtrip/roundtrip32.json",
+ "test/data/json_testsuite/sample.json", // kills AppVeyor
+ "test/data/json_tests/pass1.json",
+ "test/data/json_tests/pass2.json",
+ "test/data/json_tests/pass3.json",
+ "test/data/regression/floats.json",
+ "test/data/regression/signed_ints.json",
+ "test/data/regression/unsigned_ints.json",
+ "test/data/regression/working_file.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_arraysWithSpaces.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_empty-string.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_empty.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_ending_with_newline.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_false.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_heterogeneous.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_null.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_1_and_newline.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_leading_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_several_null.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_trailing_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_0e+1.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_0e1.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_negative_one.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_negative_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_neg_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_pos_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_neg_int.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_pos_int.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_very_big_negative_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_basic.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key_and_value.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_empty.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_empty_key.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_escaped_null_in_key.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_extreme_numbers.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_long_strings.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_simple.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_string_unicode.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_with_newlines.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_UTF-16_Surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pair.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pairs.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_allowed_escapes.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_backslash_and_u_escaped_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_backslash_doublequotes.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_comments.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_a.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_n.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_escaped_control_character.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_escaped_noncharacter.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_in_array.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_in_array_with_leading_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_last_surrogates_1_and_2.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_newline_uescaped.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+1FFFF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_null_escape.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_one-byte-utf-8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_pi.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_simple_ascii.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_three-byte-utf-8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_two-byte-utf-8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_u+2028_line_sep.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_u+2029_par_sep.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_uEscape.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unescaped_char_delete.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicodeEscapedBackslash.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_2.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+2064_invisible_plus.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_escaped_double_quote.json",
+ // "test/data/nst_json_testsuite/test_parsing/y_string_utf16.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_utf8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_with_del_character.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_false.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_negative_real.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_null.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_string.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_true.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_string_empty.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_trailing_newline.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_true_in_array.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_whitespace_array.json",
+};
+
+INSTANTIATE_TEST_CASE_P(CborRegressionFlynnTests, CborRegressionFlynnTest,
+ ::testing::ValuesIn(flynn_test_cases));
+
+#endif
+TEST(CborFirstBytesTest, Unsupported)
+{
+ // these bytes will fail immediately with exception parse_error.112
+ std::set<uint8_t> unsupported =
+ {
+ //// types not supported by this library
+
+ // byte strings
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ // byte strings
+ 0x58, 0x59, 0x5a, 0x5b, 0x5f,
+ // date/time
+ 0xc0, 0xc1,
+ // bignum
+ 0xc2, 0xc3,
+ // decimal fracion
+ 0xc4,
+ // bigfloat
+ 0xc5,
+ // tagged item
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
+ 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd8,
+ 0xd9, 0xda, 0xdb,
+ // expected conversion
+ 0xd5, 0xd6, 0xd7,
+ // simple value
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf0,
+ 0xf1, 0xf2, 0xf3,
+ 0xf8,
+ // undefined
+ 0xf7,
+
+ //// bytes not specified by CBOR
+
+ 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x5c, 0x5d, 0x5e,
+ 0x7c, 0x7d, 0x7e,
+ 0x9c, 0x9d, 0x9e,
+ 0xbc, 0xbd, 0xbe,
+ 0xdc, 0xdd, 0xde, 0xdf,
+ 0xee,
+ 0xfc, 0xfe, 0xfd,
+
+ /// break cannot be the first byte
+
+ 0xff
+ };
+
+ for (auto i = 0; i < 256; ++i)
+ {
+ const auto byte = static_cast<uint8_t>(i);
+
+ try
+ {
+ json::from_cbor(std::vector<uint8_t>(1, byte));
+ }
+ catch (const json::parse_error& e)
+ {
+ // check that parse_error.112 is only thrown if the
+ // first byte is in the unsupported set
+ SCOPED_TRACE(e.what());
+ if (std::find(unsupported.begin(), unsupported.end(),
+ static_cast<uint8_t>(byte)) != unsupported.end())
+ {
+ EXPECT_EQ(e.id, 112);
+ }
+ else
+ {
+ EXPECT_NE(e.id, 112);
+ }
+ }
+ }
+}
+
+// examples from RFC 7049 Appendix A
+namespace internal {
+struct CborRoundtripTestParam {
+ const char* plain;
+ std::vector<uint8_t> encoded;
+ bool test_encode;
+};
+} // namespace internal
+
+class CborRoundtripTest
+ : public ::testing::TestWithParam<internal::CborRoundtripTestParam> {
+};
+TEST_P(CborRoundtripTest, Case)
+{
+ if (GetParam().test_encode)
+ {
+ EXPECT_EQ(json::to_cbor(json::parse(GetParam().plain)), GetParam().encoded);
+ }
+ EXPECT_EQ(json::parse(GetParam().plain), json::from_cbor(GetParam().encoded));
+}
+
+static const internal::CborRoundtripTestParam rfc7049_appendix_a_numbers[] = {
+ {"0", {0x00}, true},
+ {"1", {0x01}, true},
+ {"10", {0x0a}, true},
+ {"23", {0x17}, true},
+ {"24", {0x18,0x18}, true},
+ {"25", {0x18,0x19}, true},
+ {"100", {0x18,0x64}, true},
+ {"1000", {0x19,0x03,0xe8}, true},
+ {"1000000", {0x1a,0x00,0x0f,0x42,0x40}, true},
+ {"1000000000000", {0x1b,0x00,0x00,0x00,0xe8,0xd4,0xa5,0x10,0x00}, true},
+ {"18446744073709551615", {0x1b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, true},
+ // positive bignum is not supported
+ //{"18446744073709551616", {0xc2,0x49,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00", 11), true},
+ //{"-18446744073709551616", {0x3b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, true},
+ // negative bignum is not supported
+ //{"-18446744073709551617", {0xc3,0x49,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, true},
+ {"-1", {0x20}, true},
+ {"-10", {0x29}, true},
+ {"-100", {0x38,0x63}, true},
+ {"-1000", {0x39,0x03,0xe7}, true},
+ // half-precision float
+ {"0.0", {0xf9,0x00,0x00}, false},
+ // half-precision float
+ {"-0.0", {0xf9,0x80,0x00}, false},
+ // half-precision float
+ {"1.0", {0xf9,0x3c,0x00}, false},
+ {"1.1", {0xfb,0x3f,0xf1,0x99,0x99,0x99,0x99,0x99,0x9a}, true},
+ // half-precision float
+ {"1.5", {0xf9,0x3e,0x00}, false},
+ // half-precision float
+ {"65504.0", {0xf9,0x7b,0xff}, false},
+ {"100000.0", {0xfa,0x47,0xc3,0x50,0x00}, false},
+ {"3.4028234663852886e+38", {0xfa,0x7f,0x7f,0xff,0xff}, false},
+ {"1.0e+300", {0xfb,0x7e,0x37,0xe4,0x3c,0x88,0x00,0x75,0x9c}, true},
+ // half-precision float
+ {"5.960464477539063e-8", {0xf9,0x00,0x01}, false},
+ // half-precision float
+ {"0.00006103515625", {0xf9,0x04,0x00}, false},
+ // half-precision float
+ {"-4.0", {0xf9,0xc4,0x00}, false},
+ {"-4.1", {0xfb,0xc0,0x10,0x66,0x66,0x66,0x66,0x66,0x66}, true},
+};
+
+INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixANumberTests, CborRoundtripTest,
+ ::testing::ValuesIn(rfc7049_appendix_a_numbers), );
+
+static const internal::CborRoundtripTestParam rfc7049_appendix_a_simple_values[] = {
+ {"false", {0xf4}, true},
+ {"true", {0xf5}, true},
+};
+
+INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixASimpleValueTests, CborRoundtripTest,
+ ::testing::ValuesIn(rfc7049_appendix_a_simple_values), );
+
+static const internal::CborRoundtripTestParam rfc7049_appendix_a_strings[] = {
+ {"\"\"", {0x60}, true},
+ {"\"a\"", {0x61,0x61}, true},
+ {"\"IETF\"", {0x64,0x49,0x45,0x54,0x46}, true},
+ {"\"\\u00fc\"", {0x62,0xc3,0xbc}, true},
+ {"\"\\u6c34\"", {0x63,0xe6,0xb0,0xb4}, true},
+ {"\"\\ud800\\udd51\"", {0x64,0xf0,0x90,0x85,0x91}, true},
+ // indefinite length strings
+ {"\"streaming\"", {0x7f,0x65,0x73,0x74,0x72,0x65,0x61,0x64,0x6d,0x69,0x6e,0x67,0xff}, false},
+};
+
+INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixAStringTests, CborRoundtripTest,
+ ::testing::ValuesIn(rfc7049_appendix_a_strings), );
+
+static const internal::CborRoundtripTestParam rfc7049_appendix_a_arrays[] = {
+ {"[]", {0x80}, true},
+ {"[1, 2, 3]", {0x83,0x01,0x02,0x03}, true},
+ {"[1, [2, 3], [4, 5]]", {0x83,0x01,0x82,0x02,0x03,0x82,0x04,0x05}, true},
+ {"[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]", {0x98,0x19,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x18,0x18,0x19}, true},
+ // indefinite length arrays
+ {"[]", {0x9f,0xff}, false},
+ {"[1, [2, 3], [4, 5]] ", {0x9f,0x01,0x82,0x02,0x03,0x9f,0x04,0x05,0xff,0xff}, false},
+ {"[1, [2, 3], [4, 5]]", {0x9f,0x01,0x82,0x02,0x03,0x82,0x04,0x05,0xff}, false},
+ {"[1, [2, 3], [4, 5]]", {0x83,0x01,0x82,0x02,0x03,0x9f,0x04,0x05,0xff}, false},
+ {"[1, [2, 3], [4, 5]]", {0x83,0x01,0x9f,0x02,0x03,0xff,0x82,0x04,0x05}, false},
+ {"[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]", {0x9f,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x18,0x18,0x19,0xff}, false},
+};
+
+INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixAArrayTests, CborRoundtripTest,
+ ::testing::ValuesIn(rfc7049_appendix_a_arrays), );
+
+static const internal::CborRoundtripTestParam rfc7049_appendix_a_objects[] = {
+ {"{}", {0xa0}, true},
+ {"{\"a\": 1, \"b\": [2, 3]}", {0xa2,0x61,0x61,0x01,0x61,0x62,0x82,0x02,0x03}, true},
+ {"[\"a\", {\"b\": \"c\"}]", {0x82,0x61,0x61,0xa1,0x61,0x62,0x61,0x63}, true},
+ {"{\"a\": \"A\", \"b\": \"B\", \"c\": \"C\", \"d\": \"D\", \"e\": \"E\"}", {0xa5,0x61,0x61,0x61,0x41,0x61,0x62,0x61,0x42,0x61,0x63,0x61,0x43,0x61,0x64,0x61,0x44,0x61,0x65,0x61,0x45}, true},
+ // indefinite length objects
+ {"{\"a\": 1, \"b\": [2, 3]}", {0xbf,0x61,0x61,0x01,0x61,0x62,0x9f,0x02,0x03,0xff,0xff}, false},
+ {"[\"a\", {\"b\": \"c\"}]", {0x82,0x61,0x61,0xbf,0x61,0x62,0x61,0x63,0xff}, false},
+ {"{\"Fun\": true, \"Amt\": -2}", {0xbf,0x63,0x46,0x75,0x6e,0xf5,0x63,0x41,0x6d,0x74,0x21,0xff}, false},
+};
+
+INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixAObjectTests, CborRoundtripTest,
+ ::testing::ValuesIn(rfc7049_appendix_a_objects), );
diff --git a/wpiutil/src/test/native/cpp/json/unit-comparison.cpp b/wpiutil/src/test/native/cpp/json/unit-comparison.cpp
new file mode 100644
index 0000000..18934a2
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-comparison.cpp
@@ -0,0 +1,250 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+class JsonComparisonTypesTest : public ::testing::Test {
+ protected:
+ std::vector<json::value_t> j_types =
+ {
+ json::value_t::null,
+ json::value_t::boolean,
+ json::value_t::number_integer,
+ json::value_t::number_unsigned,
+ json::value_t::number_float,
+ json::value_t::object,
+ json::value_t::array,
+ json::value_t::string
+ };
+};
+
+TEST_F(JsonComparisonTypesTest, Less)
+{
+ static const std::vector<std::vector<bool>> expected =
+ {
+ {false, true, true, true, true, true, true, true},
+ {false, false, true, true, true, true, true, true},
+ {false, false, false, false, false, true, true, true},
+ {false, false, false, false, false, true, true, true},
+ {false, false, false, false, false, true, true, true},
+ {false, false, false, false, false, false, true, true},
+ {false, false, false, false, false, false, false, true},
+ {false, false, false, false, false, false, false, false}
+ };
+
+ for (size_t i = 0; i < j_types.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < j_types.size(); ++j)
+ {
+ SCOPED_TRACE(j);
+ // check precomputed values
+ EXPECT_EQ(operator<(j_types[i], j_types[j]), expected[i][j]);
+ }
+ }
+}
+
+class JsonComparisonValuesTest : public ::testing::Test {
+ protected:
+ json j_values =
+ {
+ nullptr, nullptr,
+ 17, 42,
+ 8u, 13u,
+ 3.14159, 23.42,
+ "foo", "bar",
+ true, false,
+ {1, 2, 3}, {"one", "two", "three"},
+ {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
+ };
+};
+
+TEST_F(JsonComparisonValuesTest, Equal)
+{
+ static const std::vector<std::vector<bool>> expected =
+ {
+ {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}
+ };
+
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < j_values.size(); ++j)
+ {
+ SCOPED_TRACE(j);
+ // check precomputed values
+ EXPECT_EQ(j_values[i] == j_values[j], expected[i][j]);
+ }
+ }
+
+ // comparison with discarded elements
+ json j_discarded(json::value_t::discarded);
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ EXPECT_FALSE(j_values[i] == j_discarded);
+ EXPECT_FALSE(j_discarded == j_values[i]);
+ EXPECT_FALSE(j_discarded == j_discarded);
+ }
+
+ // compare with null pointer
+ json j_null;
+ EXPECT_TRUE(j_null == nullptr);
+ EXPECT_TRUE(nullptr == j_null);
+}
+
+TEST_F(JsonComparisonValuesTest, NotEqual)
+{
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < j_values.size(); ++j)
+ {
+ SCOPED_TRACE(j);
+ // check definition
+ EXPECT_EQ(j_values[i] != j_values[j], !(j_values[i] == j_values[j]));
+ }
+ }
+
+ // compare with null pointer
+ json j_null;
+ EXPECT_FALSE(j_null != nullptr);
+ EXPECT_FALSE(nullptr != j_null);
+ EXPECT_EQ(j_null != nullptr, !(j_null == nullptr));
+ EXPECT_EQ(nullptr != j_null, !(nullptr == j_null));
+}
+
+TEST_F(JsonComparisonValuesTest, Less)
+{
+ static const std::vector<std::vector<bool>> expected =
+ {
+ {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
+ {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
+ {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true},
+ {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true},
+ {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true},
+ {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true},
+ {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true},
+ {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
+ {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true},
+ {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true},
+ {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false},
+ {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false},
+ {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false}
+ };
+
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < j_values.size(); ++j)
+ {
+ SCOPED_TRACE(j);
+ // check precomputed values
+ EXPECT_EQ(j_values[i] < j_values[j], expected[i][j]);
+ }
+ }
+
+ // comparison with discarded elements
+ json j_discarded(json::value_t::discarded);
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ EXPECT_FALSE(j_values[i] < j_discarded);
+ EXPECT_FALSE(j_discarded < j_values[i]);
+ EXPECT_FALSE(j_discarded < j_discarded);
+ }
+}
+
+TEST_F(JsonComparisonValuesTest, LessEqual)
+{
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < j_values.size(); ++j)
+ {
+ SCOPED_TRACE(j);
+ // check definition
+ EXPECT_EQ(j_values[i] <= j_values[j], !(j_values[j] < j_values[i]));
+ }
+ }
+}
+
+TEST_F(JsonComparisonValuesTest, Greater)
+{
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < j_values.size(); ++j)
+ {
+ SCOPED_TRACE(j);
+ // check definition
+ EXPECT_EQ(j_values[i] > j_values[j], j_values[j] < j_values[i]);
+ }
+ }
+}
+
+TEST_F(JsonComparisonValuesTest, GreaterEqual)
+{
+ for (size_t i = 0; i < j_values.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < j_values.size(); ++j)
+ {
+ SCOPED_TRACE(j);
+ // check definition
+ EXPECT_EQ(j_values[i] >= j_values[j], !(j_values[i] < j_values[j]));
+ }
+ }
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-concepts.cpp b/wpiutil/src/test/native/cpp/json/unit-concepts.cpp
new file mode 100644
index 0000000..4ce5a5b
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-concepts.cpp
@@ -0,0 +1,166 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonConceptsTest, ContainerRequirements)
+{
+ // X: container class: json
+ // T: type of objects: json
+ // a, b: values of type X: json
+
+ // TABLE 96 - Container Requirements
+
+ // X::value_type must return T
+ EXPECT_TRUE((std::is_same<json::value_type, json>::value));
+
+ // X::reference must return lvalue of T
+ EXPECT_TRUE((std::is_same<json::reference, json&>::value));
+
+ // X::const_reference must return const lvalue of T
+ EXPECT_TRUE((std::is_same<json::const_reference, const json&>::value));
+
+ // X::iterator must return iterator whose value_type is T
+ EXPECT_TRUE((std::is_same<json::iterator::value_type, json>::value));
+ // X::iterator must meet the forward iterator requirements
+ EXPECT_TRUE((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::iterator>::iterator_category>::value));
+ // X::iterator must be convertible to X::const_iterator
+ EXPECT_TRUE((std::is_convertible<json::iterator, json::const_iterator>::value));
+
+ // X::const_iterator must return iterator whose value_type is T
+ EXPECT_TRUE((std::is_same<json::const_iterator::value_type, json>::value));
+ // X::const_iterator must meet the forward iterator requirements
+ EXPECT_TRUE((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::const_iterator>::iterator_category>::value));
+
+ // X::difference_type must return a signed integer
+ EXPECT_TRUE((std::is_signed<json::difference_type>::value));
+ // X::difference_type must be identical to X::iterator::difference_type
+ EXPECT_TRUE((std::is_same<json::difference_type, json::iterator::difference_type>::value));
+ // X::difference_type must be identical to X::const_iterator::difference_type
+ EXPECT_TRUE((std::is_same<json::difference_type, json::const_iterator::difference_type>::value));
+
+ // X::size_type must return an unsigned integer
+ EXPECT_TRUE((std::is_unsigned<json::size_type>::value));
+ // X::size_type can represent any non-negative value of X::difference_type
+ EXPECT_TRUE(static_cast<json::size_type>(std::numeric_limits<json::difference_type>::max()) <=
+ std::numeric_limits<json::size_type>::max());
+
+ // the expression "X u" has the post-condition "u.empty()"
+ {
+ json u;
+ EXPECT_TRUE(u.empty());
+ }
+
+ // the expression "X()" has the post-condition "X().empty()"
+ EXPECT_TRUE(json().empty());
+}
+
+TEST(JsonConceptsTest, DefaultConstructible)
+{
+ EXPECT_TRUE(std::is_nothrow_default_constructible<json>::value);
+}
+
+TEST(JsonConceptsTest, MoveConstructible)
+{
+ EXPECT_TRUE(std::is_nothrow_move_constructible<json>::value);
+}
+
+TEST(JsonConceptsTest, CopyConstructible)
+{
+ EXPECT_TRUE(std::is_copy_constructible<json>::value);
+}
+
+TEST(JsonConceptsTest, MoveAssignable)
+{
+ EXPECT_TRUE(std::is_nothrow_move_assignable<json>::value);
+}
+
+TEST(JsonConceptsTest, CopyAssignable)
+{
+ EXPECT_TRUE(std::is_copy_assignable<json>::value);
+}
+
+TEST(JsonConceptsTest, Destructible)
+{
+ EXPECT_TRUE(std::is_nothrow_destructible<json>::value);
+}
+
+TEST(JsonConceptsTest, StandardLayoutType)
+{
+ EXPECT_TRUE(std::is_standard_layout<json>::value);
+}
+
+TEST(JsonIteratorConceptsTest, CopyConstructible)
+{
+ EXPECT_TRUE(std::is_nothrow_copy_constructible<json::iterator>::value);
+ EXPECT_TRUE(std::is_nothrow_copy_constructible<json::const_iterator>::value);
+}
+
+TEST(JsonIteratorConceptsTest, CopyAssignable)
+{
+ // STL iterators used by json::iterator don't pass this test in Debug mode
+#if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0)
+ EXPECT_TRUE(std::is_nothrow_copy_assignable<json::iterator>::value);
+ EXPECT_TRUE(std::is_nothrow_copy_assignable<json::const_iterator>::value);
+#endif
+}
+
+TEST(JsonIteratorConceptsTest, Destructible)
+{
+ EXPECT_TRUE(std::is_nothrow_destructible<json::iterator>::value);
+ EXPECT_TRUE(std::is_nothrow_destructible<json::const_iterator>::value);
+}
+
+TEST(JsonIteratorConceptsTest, Swappable)
+{
+ json j {1, 2, 3};
+ json::iterator it1 = j.begin();
+ json::iterator it2 = j.end();
+ std::swap(it1, it2);
+ EXPECT_EQ(it1, j.end());
+ EXPECT_EQ(it2, j.begin());
+}
+
+TEST(JsonIteratorConceptsTest, SwappableConst)
+{
+ json j {1, 2, 3};
+ json::const_iterator it1 = j.cbegin();
+ json::const_iterator it2 = j.cend();
+ std::swap(it1, it2);
+ EXPECT_EQ(it1, j.end());
+ EXPECT_EQ(it2, j.begin());
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp b/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp
new file mode 100644
index 0000000..71c4678
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp
@@ -0,0 +1,1068 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include <array>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <map>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "unit-json.h"
+using wpi::json;
+using wpi::JsonTest;
+
+class JsonConstructTypeTest : public ::testing::TestWithParam<json::value_t> {};
+TEST_P(JsonConstructTypeTest, Case)
+{
+ auto t = GetParam();
+ json j(t);
+ EXPECT_EQ(j.type(), t);
+}
+
+static const json::value_t construct_type_cases[] = {
+ json::value_t::null,
+ json::value_t::discarded,
+ json::value_t::object,
+ json::value_t::array,
+ json::value_t::boolean,
+ json::value_t::string,
+ json::value_t::number_integer,
+ json::value_t::number_unsigned,
+ json::value_t::number_float,
+};
+
+INSTANTIATE_TEST_CASE_P(JsonConstructTypeTests, JsonConstructTypeTest,
+ ::testing::ValuesIn(construct_type_cases), );
+
+
+TEST(JsonConstructNullTest, NoParameter)
+{
+ json j{};
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonConstructNullTest, Parameter)
+{
+ json j(nullptr);
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonConstructObjectExplicitTest, Empty)
+{
+ json::object_t o;
+ json j(o);
+ EXPECT_EQ(j.type(), json::value_t::object);
+}
+
+TEST(JsonConstructObjectExplicitTest, Filled)
+{
+ json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
+ json j(o);
+ EXPECT_EQ(j.type(), json::value_t::object);
+}
+
+class JsonConstructObjectImplicitTest : public ::testing::Test {
+ public:
+ JsonConstructObjectImplicitTest() : j_reference(o_reference) {}
+
+ protected:
+ json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
+ json j_reference;
+};
+
+// std::map<std::string, json>
+TEST_F(JsonConstructObjectImplicitTest, StdMapStringJson)
+{
+ std::map<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
+ json j(o);
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j, j_reference);
+}
+
+// std::pair<CompatibleString, T>
+TEST_F(JsonConstructObjectImplicitTest, StdPairStringT)
+{
+ std::pair<std::string, std::string> p{"first", "second"};
+ json j(p);
+
+ EXPECT_EQ(j.get<decltype(p)>(), p);
+
+ std::pair<std::string, int> p2{"first", 1};
+ // use char const*
+ json j2(std::make_pair("first", 1));
+
+ EXPECT_EQ(j2.get<decltype(p2)>(), p2);
+}
+
+// std::map<std::string, std::string>
+TEST_F(JsonConstructObjectImplicitTest, StdMapStringString)
+{
+ std::map<std::string, std::string> m;
+ m["a"] = "b";
+ m["c"] = "d";
+ m["e"] = "f";
+
+ json j(m);
+ EXPECT_EQ(j.get<decltype(m)>(), m);
+}
+
+// std::map<const char*, json>
+TEST_F(JsonConstructObjectImplicitTest, StdMapCharPointerJson)
+{
+ std::map<const char*, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
+ json j(o);
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j, j_reference);
+}
+
+// std::multimap<std::string, json>
+TEST_F(JsonConstructObjectImplicitTest, StdMultiMapStringJson)
+{
+ std::multimap<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
+ json j(o);
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j, j_reference);
+}
+
+// std::unordered_map<std::string, json>
+TEST_F(JsonConstructObjectImplicitTest, StdUnorderedMapStringJson)
+{
+ std::unordered_map<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
+ json j(o);
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j, j_reference);
+}
+
+// std::unordered_multimap<std::string, json>
+TEST_F(JsonConstructObjectImplicitTest, StdUnorderedMultiMapStringJson)
+{
+ std::unordered_multimap<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
+ json j(o);
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j, j_reference);
+}
+
+// associative container literal
+TEST_F(JsonConstructObjectImplicitTest, AssociativeContainerLiteral)
+{
+ json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}});
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j, j_reference);
+}
+
+TEST(JsonConstructArrayExplicitTest, Empty)
+{
+ json::array_t a;
+ json j(a);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructArrayExplicitTest, Filled)
+{
+ json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j(a);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+template <typename T>
+class JsonConstructArrayTest : public ::testing::Test {
+ public:
+ JsonConstructArrayTest() : j_reference(a_reference) {}
+
+ protected:
+ json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j_reference;
+};
+
+typedef ::testing::Types<std::list<json>, std::forward_list<json>,
+ std::array<json, 6>, std::vector<json>,
+ std::deque<json>>
+ JsonConstructArrayTestTypes;
+TYPED_TEST_CASE(JsonConstructArrayTest, JsonConstructArrayTestTypes);
+
+// clang warns on missing braces on the TypeParam initializer line below.
+// Suppress this warning.
+#if defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-braces"
+#endif
+TYPED_TEST(JsonConstructArrayTest, Implicit)
+{
+ TypeParam a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j(a);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, this->j_reference);
+}
+#if defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+// std::set<json>
+TEST(JsonConstructArraySetTest, StdSet)
+{
+ std::set<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j(a);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ // we cannot really check for equality here
+}
+
+// std::unordered_set<json>
+TEST(JsonConstructArraySetTest, StdUnorderedSet)
+{
+ std::unordered_set<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j(a);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ // we cannot really check for equality here
+}
+
+// sequence container literal
+TEST(JsonConstructArrayContainerTest, Case)
+{
+ json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j_reference(a_reference);
+
+ json j({json(1), json(1u), json(2.2), json(false), json("string"), json()});
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, j_reference);
+}
+
+TEST(JsonConstructStringExplicitTest, Empty)
+{
+ std::string s;
+ json j(s);
+ EXPECT_EQ(j.type(), json::value_t::string);
+}
+
+TEST(JsonConstructStringExplicitTest, Filled)
+{
+ std::string s {"Hello world"};
+ json j(s);
+ EXPECT_EQ(j.type(), json::value_t::string);
+}
+
+class JsonConstructStringTest : public ::testing::Test {
+ public:
+ JsonConstructStringTest() : j_reference(s_reference) {}
+
+ protected:
+ std::string s_reference {"Hello world"};
+ json j_reference;
+};
+
+// std::string
+TEST_F(JsonConstructStringTest, StdString)
+{
+ std::string s {"Hello world"};
+ json j(s);
+ EXPECT_EQ(j.type(), json::value_t::string);
+ EXPECT_EQ(j, j_reference);
+}
+
+// char[]
+TEST_F(JsonConstructStringTest, CharArray)
+{
+ char s[] {"Hello world"};
+ json j(s);
+ EXPECT_EQ(j.type(), json::value_t::string);
+ EXPECT_EQ(j, j_reference);
+}
+
+// const char*
+TEST_F(JsonConstructStringTest, ConstCharPointer)
+{
+ const char* s {"Hello world"};
+ json j(s);
+ EXPECT_EQ(j.type(), json::value_t::string);
+ EXPECT_EQ(j, j_reference);
+}
+
+// string literal
+TEST_F(JsonConstructStringTest, StringLiteral)
+{
+ json j("Hello world");
+ EXPECT_EQ(j.type(), json::value_t::string);
+ EXPECT_EQ(j, j_reference);
+}
+
+TEST(JsonConstructBooleanExplicitTest, Empty)
+{
+ bool b{};
+ json j(b);
+ EXPECT_EQ(j.type(), json::value_t::boolean);
+}
+
+TEST(JsonConstructBooleanExplicitTest, True)
+{
+ json j(true);
+ EXPECT_EQ(j.type(), json::value_t::boolean);
+}
+
+TEST(JsonConstructBooleanExplicitTest, False)
+{
+ json j(false);
+ EXPECT_EQ(j.type(), json::value_t::boolean);
+}
+
+TEST(JsonConstructIntegerExplicitTest, Uninitialized)
+{
+ int64_t n{};
+ json j(n);
+ EXPECT_EQ(j.type(), json::value_t::number_integer);
+}
+
+TEST(JsonConstructIntegerExplicitTest, Initialized)
+{
+ int64_t n(42);
+ json j(n);
+ EXPECT_EQ(j.type(), json::value_t::number_integer);
+}
+
+template <typename T>
+class JsonConstructIntegerTest : public ::testing::Test {
+ public:
+ JsonConstructIntegerTest()
+ : j_reference(n_reference), j_unsigned_reference(n_unsigned_reference) {}
+
+ protected:
+ int64_t n_reference = 42;
+ json j_reference;
+ uint64_t n_unsigned_reference = 42u;
+ json j_unsigned_reference;
+};
+
+typedef ::testing::Types<
+ short
+ , unsigned short
+ , int
+ , unsigned int
+ , long
+ , unsigned long
+ , long long
+ , unsigned long long
+ , int8_t
+ , int16_t
+ , int32_t
+ , int64_t
+#if 0
+ , int8_fast_t
+ , int16_fast_t
+ , int32_fast_t
+ , int64_fast_t
+ , int8_least_t
+ , int16_least_t
+ , int32_least_t
+ , int64_least_t
+#endif
+ , uint8_t
+ , uint16_t
+ , uint32_t
+ , uint64_t
+#if 0
+ , uint8_fast_t
+ , uint16_fast_t
+ , uint32_fast_t
+ , uint64_fast_t
+ , uint8_least_t
+ , uint16_least_t
+ , uint32_least_t
+ , uint64_least_t
+#endif
+ > JsonConstructIntegerTestTypes;
+
+TYPED_TEST_CASE(JsonConstructIntegerTest, JsonConstructIntegerTestTypes);
+
+TYPED_TEST(JsonConstructIntegerTest, Implicit)
+{
+ TypeParam n = 42;
+ json j(n);
+ if (std::is_unsigned<TypeParam>::value)
+ {
+ EXPECT_EQ(j.type(), json::value_t::number_unsigned);
+ EXPECT_EQ(j, this->j_unsigned_reference);
+ }
+ else
+ {
+ EXPECT_EQ(j.type(), json::value_t::number_integer);
+ EXPECT_EQ(j, this->j_reference);
+ }
+}
+
+class JsonConstructIntegerLiteralTest : public ::testing::Test {
+ public:
+ JsonConstructIntegerLiteralTest()
+ : j_reference(n_reference), j_unsigned_reference(n_unsigned_reference) {}
+
+ protected:
+ int64_t n_reference = 42;
+ json j_reference;
+ uint64_t n_unsigned_reference = 42u;
+ json j_unsigned_reference;
+};
+
+TEST_F(JsonConstructIntegerLiteralTest, None)
+{
+ json j(42);
+ EXPECT_EQ(j.type(), json::value_t::number_integer);
+ EXPECT_EQ(j, j_reference);
+}
+
+TEST_F(JsonConstructIntegerLiteralTest, U)
+{
+ json j(42u);
+ EXPECT_EQ(j.type(), json::value_t::number_unsigned);
+ EXPECT_EQ(j, j_unsigned_reference);
+}
+
+TEST_F(JsonConstructIntegerLiteralTest, L)
+{
+ json j(42l);
+ EXPECT_EQ(j.type(), json::value_t::number_integer);
+ EXPECT_EQ(j, j_reference);
+}
+
+TEST_F(JsonConstructIntegerLiteralTest, UL)
+{
+ json j(42ul);
+ EXPECT_EQ(j.type(), json::value_t::number_unsigned);
+ EXPECT_EQ(j, j_unsigned_reference);
+}
+
+TEST_F(JsonConstructIntegerLiteralTest, LL)
+{
+ json j(42ll);
+ EXPECT_EQ(j.type(), json::value_t::number_integer);
+ EXPECT_EQ(j, j_reference);
+}
+
+TEST_F(JsonConstructIntegerLiteralTest, ULL)
+{
+ json j(42ull);
+ EXPECT_EQ(j.type(), json::value_t::number_unsigned);
+ EXPECT_EQ(j, j_unsigned_reference);
+}
+
+TEST(JsonConstructFloatExplicitTest, Uninitialized)
+{
+ double n{};
+ json j(n);
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+}
+
+TEST(JsonConstructFloatExplicitTest, Initialized)
+{
+ double n(42.23);
+ json j(n);
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+}
+
+TEST(JsonConstructFloatExplicitTest, Infinity)
+{
+ // infinity is stored properly, but serialized to null
+ double n(std::numeric_limits<double>::infinity());
+ json j(n);
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+
+ // check round trip of infinity
+ double d = j;
+ EXPECT_EQ(d, n);
+
+ // check that inf is serialized to null
+ EXPECT_EQ(j.dump(), "null");
+}
+
+template <typename T>
+class JsonConstructFloatTest : public ::testing::Test {
+ public:
+ JsonConstructFloatTest() : j_reference(n_reference) {}
+
+ protected:
+ double n_reference {42.23};
+ json j_reference;
+};
+
+typedef ::testing::Types<float, double
+#if 0
+ , long double
+#endif
+ >
+ JsonConstructFloatTestTypes;
+
+TYPED_TEST_CASE(JsonConstructFloatTest, JsonConstructFloatTestTypes);
+
+TYPED_TEST(JsonConstructFloatTest, Implicit)
+{
+ TypeParam n = 42.23f;
+ json j(n);
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+ EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
+ JsonTest::GetValue(this->j_reference).number_float),
+ 0.001);
+}
+
+class JsonConstructFloatLiteralTest : public ::testing::Test {
+ public:
+ JsonConstructFloatLiteralTest() : j_reference(n_reference) {}
+
+ protected:
+ double n_reference {42.23};
+ json j_reference;
+};
+
+TEST_F(JsonConstructFloatLiteralTest, None)
+{
+ json j(42.23);
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+ EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
+ JsonTest::GetValue(this->j_reference).number_float),
+ 0.001);
+}
+
+TEST_F(JsonConstructFloatLiteralTest, F)
+{
+ json j(42.23f);
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+ EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
+ JsonTest::GetValue(this->j_reference).number_float),
+ 0.001);
+}
+
+#if 0
+TEST_F(JsonConstructFloatLiteralTest, L)
+{
+ json j(42.23l);
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+ EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
+ JsonTest::GetValue(this->j_reference).number_float),
+ 0.001);
+}
+#endif
+
+TEST(JsonConstructInitializerEmptyTest, Explicit)
+{
+ json j(json::initializer_list_t{});
+ EXPECT_EQ(j.type(), json::value_t::object);
+}
+
+TEST(JsonConstructInitializerEmptyTest, Implicit)
+{
+ json j {};
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonConstructInitializerOneTest, ExplicitArray)
+{
+ std::initializer_list<json> l = {json(json::array_t())};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ImplicitArray)
+{
+ json j {json::array_t()};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ExplicitObject)
+{
+ std::initializer_list<json> l = {json(json::object_t())};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ImplicitObject)
+{
+ json j {json::object_t()};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ExplicitString)
+{
+ std::initializer_list<json> l = {json("Hello world")};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ImplicitString)
+{
+ json j {"Hello world"};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ExplicitBoolean)
+{
+ std::initializer_list<json> l = {json(true)};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ImplicitBoolean)
+{
+ json j {true};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ExplicitInteger)
+{
+ std::initializer_list<json> l = {json(1)};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ImplicitInteger)
+{
+ json j {1};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ExplicitUnsigned)
+{
+ std::initializer_list<json> l = {json(1u)};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ImplicitUnsigned)
+{
+ json j {1u};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ExplicitFloat)
+{
+ std::initializer_list<json> l = {json(42.23)};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerOneTest, ImplicitFloat)
+{
+ json j {42.23};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerManyTest, Explicit)
+{
+ std::initializer_list<json> l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()};
+ json j(l);
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerManyTest, Implicit)
+{
+ json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerImplicitTest, Object)
+{
+ json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} };
+ EXPECT_EQ(j.type(), json::value_t::object);
+}
+
+TEST(JsonConstructInitializerImplicitTest, Array)
+{
+ json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 };
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerExplicitTest, EmptyObject)
+{
+ json j = json::object();
+ EXPECT_EQ(j.type(), json::value_t::object);
+}
+
+TEST(JsonConstructInitializerExplicitTest, Object)
+{
+ json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} });
+ EXPECT_EQ(j.type(), json::value_t::object);
+}
+
+TEST(JsonConstructInitializerExplicitTest, ObjectError)
+{
+ EXPECT_THROW_MSG(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
+ json::type_error,
+ "[json.exception.type_error.301] cannot create object from initializer list");
+}
+
+// std::pair<CompatibleString, T> with error
+TEST(JsonConstructInitializerPairErrorTest, WrongFieldNumber)
+{
+ json j{{"too", "much"}, {"string", "fields"}};
+ EXPECT_THROW_MSG((j.get<std::pair<std::string, std::string>>()), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with object");
+}
+
+TEST(JsonConstructInitializerPairErrorTest, WrongJsonType)
+{
+ json j(42);
+ EXPECT_THROW_MSG((j.get<std::pair<std::string, std::string>>()), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+}
+
+TEST(JsonConstructInitializerTest, EmptyArray)
+{
+ json j = json::array();
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConstructInitializerTest, Array)
+{
+ json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} });
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+// create an array of n copies of a given value
+TEST(JsonConstructArrayCopyTest, Case)
+{
+ json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}};
+ json arr(3, v);
+ EXPECT_EQ(arr.size(), 3u);
+ for (auto& x : arr)
+ {
+ EXPECT_EQ(x, v);
+ }
+}
+
+// create a JSON container from an iterator range
+TEST(JsonConstructIteratorTest, ObjectBeginEnd)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json j_new(jobject.begin(), jobject.end());
+ EXPECT_EQ(j_new, jobject);
+#else
+ EXPECT_THROW(json(jobject.begin(), jobject.end()), json::invalid_iterator);
+#endif
+}
+
+TEST(JsonConstructIteratorTest, ObjectBeginEndConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json j_new(jobject.cbegin(), jobject.cend());
+ EXPECT_EQ(j_new, jobject);
+#else
+ EXPECT_THROW(json(jobject.cbegin(), jobject.cend()), json::invalid_iterator);
+#endif
+}
+
+TEST(JsonConstructIteratorTest, ObjectBeginBegin)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json j_new(jobject.begin(), jobject.begin());
+ EXPECT_EQ(j_new, json::object());
+#else
+ EXPECT_THROW(json(jobject.begin(), jobject.end()), json::invalid_iterator);
+#endif
+}
+
+TEST(JsonConstructIteratorTest, ObjectBeginBeginConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json j_new(jobject.cbegin(), jobject.cbegin());
+ EXPECT_EQ(j_new, json::object());
+#else
+ EXPECT_THROW(json(jobject.cbegin(), jobject.cend()), json::invalid_iterator);
+#endif
+}
+#if 0
+TEST(JsonConstructIteratorTest, ObjectSubrange)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
+ json j_new(jobject.find("b"), jobject.find("e"));
+ EXPECT_EQ(j_new, json({{"b", 1}, {"c", 17u}, {"d", false}}));
+}
+#endif
+TEST(JsonConstructIteratorTest, ObjectIncompatibleIterators)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
+ json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ EXPECT_THROW_MSG(json(jobject.begin(), jobject2.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+ EXPECT_THROW_MSG(json(jobject2.begin(), jobject.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+}
+
+TEST(JsonConstructIteratorTest, ObjectIncompatibleIteratorsConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
+ json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ EXPECT_THROW_MSG(json(jobject.cbegin(), jobject2.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+ EXPECT_THROW_MSG(json(jobject2.cbegin(), jobject.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+}
+
+TEST(JsonConstructIteratorTest, ArrayBeginEnd)
+{
+ json jarray = {1, 2, 3, 4, 5};
+ json j_new(jarray.begin(), jarray.end());
+ EXPECT_EQ(j_new, jarray);
+}
+
+TEST(JsonConstructIteratorTest, ArrayBeginEndConst)
+{
+ json jarray = {1, 2, 3, 4, 5};
+ json j_new(jarray.cbegin(), jarray.cend());
+ EXPECT_EQ(j_new, jarray);
+}
+
+TEST(JsonConstructIteratorTest, ArrayBeginBegin)
+{
+ json jarray = {1, 2, 3, 4, 5};
+ json j_new(jarray.begin(), jarray.begin());
+ EXPECT_EQ(j_new, json::array());
+}
+
+TEST(JsonConstructIteratorTest, ArrayBeginBeginConst)
+{
+ json jarray = {1, 2, 3, 4, 5};
+ json j_new(jarray.cbegin(), jarray.cbegin());
+ EXPECT_EQ(j_new, json::array());
+}
+
+TEST(JsonConstructIteratorTest, ArraySubrange)
+{
+ json jarray = {1, 2, 3, 4, 5};
+ json j_new(jarray.begin() + 1, jarray.begin() + 3);
+ EXPECT_EQ(j_new, json({2, 3}));
+}
+
+TEST(JsonConstructIteratorTest, ArraySubrangeConst)
+{
+ json jarray = {1, 2, 3, 4, 5};
+ json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3);
+ EXPECT_EQ(j_new, json({2, 3}));
+}
+
+TEST(JsonConstructIteratorTest, ArrayIncompatibleIterators)
+{
+ json jarray = {1, 2, 3, 4};
+ json jarray2 = {2, 3, 4, 5};
+ EXPECT_THROW_MSG(json(jarray.begin(), jarray2.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+ EXPECT_THROW_MSG(json(jarray2.begin(), jarray.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+}
+
+TEST(JsonConstructIteratorTest, ArrayIncompatibleIteratorsConst)
+{
+ json jarray = {1, 2, 3, 4};
+ json jarray2 = {2, 3, 4, 5};
+ EXPECT_THROW_MSG(json(jarray.cbegin(), jarray2.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+ EXPECT_THROW_MSG(json(jarray2.cbegin(), jarray.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.201] iterators are not compatible");
+}
+
+TEST(JsonConstructTwoValidIteratorTest, Null)
+{
+ json j;
+ EXPECT_THROW_MSG(json(j.begin(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.206] cannot construct with iterators from null");
+}
+
+TEST(JsonConstructTwoValidIteratorTest, NullConst)
+{
+ json j;
+ EXPECT_THROW_MSG(json(j.cbegin(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.206] cannot construct with iterators from null");
+}
+
+TEST(JsonConstructTwoValidIteratorTest, String)
+{
+ json j = "foo";
+ json j_new(j.begin(), j.end());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, StringConst)
+{
+ json j = "bar";
+ json j_new(j.cbegin(), j.cend());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, Boolean)
+{
+ json j = false;
+ json j_new(j.begin(), j.end());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, BooleanConst)
+{
+ json j = true;
+ json j_new(j.cbegin(), j.cend());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, Integer)
+{
+ json j = 17;
+ json j_new(j.begin(), j.end());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, IntegerConst)
+{
+ json j = 17;
+ json j_new(j.cbegin(), j.cend());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, Unsigned)
+{
+ json j = 17u;
+ json j_new(j.begin(), j.end());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, UnsignedConst)
+{
+ json j = 17u;
+ json j_new(j.cbegin(), j.cend());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, Float)
+{
+ json j = 23.42;
+ json j_new(j.begin(), j.end());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoValidIteratorTest, FloatConst)
+{
+ json j = 23.42;
+ json j_new(j.cbegin(), j.cend());
+ EXPECT_EQ(j, j_new);
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, String)
+{
+ json j = "foo";
+ EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, StringConst)
+{
+ json j = "bar";
+ EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, Boolean)
+{
+ json j = false;
+ EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, BooleanConst)
+{
+ json j = true;
+ EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, Integer)
+{
+ json j = 17;
+ EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, IntegerConst)
+{
+ json j = 17;
+ EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, Unsigned)
+{
+ json j = 17u;
+ EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, UnsignedConst)
+{
+ json j = 17u;
+ EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, Float)
+{
+ json j = 23.42;
+ EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonConstructTwoInvalidIteratorTest, FloatConst)
+{
+ json j = 23.42;
+ EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-constructor2.cpp b/wpiutil/src/test/native/cpp/json/unit-constructor2.cpp
new file mode 100644
index 0000000..39f1301
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-constructor2.cpp
@@ -0,0 +1,185 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonCopyConstructorTest, Object)
+{
+ json j {{"foo", 1}, {"bar", false}};
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyConstructorTest, Array)
+{
+ json j {"foo", 1, 42.23, false};
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyConstructorTest, Null)
+{
+ json j(nullptr);
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyConstructorTest, Boolean)
+{
+ json j(true);
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyConstructorTest, String)
+{
+ json j("Hello world");
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyConstructorTest, Integer)
+{
+ json j(42);
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyConstructorTest, Unsigned)
+{
+ json j(42u);
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyConstructorTest, Float)
+{
+ json j(42.23);
+ json k(j);
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonMoveConstructorTest, Case)
+{
+ json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}};
+ EXPECT_EQ(j.type(), json::value_t::object);
+ json k(std::move(j));
+ EXPECT_EQ(k.type(), json::value_t::object);
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonCopyAssignmentTest, Object)
+{
+ json j {{"foo", 1}, {"bar", false}};
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyAssignmentTest, Array)
+{
+ json j {"foo", 1, 42.23, false};
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyAssignmentTest, Null)
+{
+ json j(nullptr);
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyAssignmentTest, Boolean)
+{
+ json j(true);
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyAssignmentTest, String)
+{
+ json j("Hello world");
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyAssignmentTest, Integer)
+{
+ json j(42);
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyAssignmentTest, Unsigned)
+{
+ json j(42u);
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonCopyAssignmentTest, Float)
+{
+ json j(42.23);
+ json k;
+ k = j;
+ EXPECT_EQ(j, k);
+}
+
+TEST(JsonDestructorTest, Object)
+{
+ auto j = new json {{"foo", 1}, {"bar", false}};
+ delete j;
+}
+
+TEST(JsonDestructorTest, Array)
+{
+ auto j = new json {"foo", 1, 1u, false, 23.42};
+ delete j;
+}
+
+TEST(JsonDestructorTest, String)
+{
+ auto j = new json("Hello world");
+ delete j;
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-conversions.cpp b/wpiutil/src/test/native/cpp/json/unit-conversions.cpp
new file mode 100644
index 0000000..e4981f2
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-conversions.cpp
@@ -0,0 +1,560 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+using wpi::JsonTest;
+
+#include <deque>
+//#include <forward_list>
+#include <list>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+
+template <typename T>
+class JsonGetObjectTest : public ::testing::Test {
+ public:
+ JsonGetObjectTest() : j(o_reference) {}
+
+ protected:
+ json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
+ json j;
+};
+
+typedef ::testing::Types<
+ json::object_t
+ , std::map<std::string, json>
+ , std::multimap<std::string, json>
+ , std::unordered_map<std::string, json>
+ , std::unordered_multimap<std::string, json>
+ > JsonGetObjectTestTypes;
+TYPED_TEST_CASE(JsonGetObjectTest, JsonGetObjectTestTypes);
+
+TYPED_TEST(JsonGetObjectTest, Explicit)
+{
+ TypeParam o = (this->j).template get<TypeParam>();
+ EXPECT_EQ(json(o), this->j);
+}
+
+TYPED_TEST(JsonGetObjectTest, Implicit)
+{
+ TypeParam o = this->j;
+ EXPECT_EQ(json(o), this->j);
+}
+
+// exception in case of a non-object type
+TEST(JsonGetObjectExceptionTest, TypeError)
+{
+ EXPECT_THROW_MSG(json(json::value_t::null).get<json::object_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is null");
+ EXPECT_THROW_MSG(json(json::value_t::array).get<json::object_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is array");
+ EXPECT_THROW_MSG(json(json::value_t::string).get<json::object_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is string");
+ EXPECT_THROW_MSG(json(json::value_t::boolean).get<json::object_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is boolean");
+ EXPECT_THROW_MSG(json(json::value_t::number_integer).get<json::object_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<json::object_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_float).get<json::object_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is number");
+}
+
+template <typename T>
+class JsonGetArrayTest : public ::testing::Test {
+ public:
+ JsonGetArrayTest() : j(a_reference) {}
+
+ protected:
+ json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j;
+};
+
+typedef ::testing::Types<json::array_t, std::list<json>,
+ /*std::forward_list<json>,*/ std::vector<json>,
+ std::deque<json>>
+ JsonGetArrayTestTypes;
+TYPED_TEST_CASE(JsonGetArrayTest, JsonGetArrayTestTypes);
+
+TYPED_TEST(JsonGetArrayTest, Explicit)
+{
+ TypeParam a = (this->j).template get<TypeParam>();
+ EXPECT_EQ(json(a), this->j);
+}
+
+TYPED_TEST(JsonGetArrayTest, Implicit)
+{
+ TypeParam a = this->j;
+ EXPECT_EQ(json(a), this->j);
+}
+
+#if !defined(JSON_NOEXCEPTION)
+// reserve is called on containers that supports it
+TEST(JsonGetArrayAdditionalTest, ExplicitStdVectorReserve)
+{
+ json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
+ json j(a_reference);
+
+ // making the call to from_json throw in order to check capacity
+ std::vector<float> v;
+ EXPECT_THROW(wpi::from_json(j, v), json::type_error);
+ EXPECT_EQ(v.capacity(), j.size());
+
+ // make sure all values are properly copied
+ std::vector<int> v2 = json({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+ EXPECT_EQ(v2.size(), 10u);
+}
+#endif
+
+// built-in arrays
+TEST(JsonGetArrayAdditionalTest, ExplicitBuiltinArray)
+{
+ const char str[] = "a string";
+ const int nbs[] = {0, 1, 2};
+
+ json j2 = nbs;
+ json j3 = str;
+
+ auto v = j2.get<std::vector<int>>();
+ auto s = j3.get<std::string>();
+ EXPECT_TRUE(std::equal(v.begin(), v.end(), std::begin(nbs)));
+ EXPECT_EQ(s, str);
+}
+#if 0
+TEST(JsonGetArrayExceptionTest, ForwardList)
+{
+ EXPECT_THROW_MSG(json(json::value_t::null).get<std::forward_list<json>>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is null");
+}
+#endif
+TEST(JsonGetArrayExceptionTest, StdVector)
+{
+ EXPECT_THROW_MSG(json(json::value_t::null).get<std::vector<json>>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is null");
+}
+
+// exception in case of a non-array type
+TEST(JsonGetArrayExceptionTest, TypeError)
+{
+ EXPECT_THROW_MSG(json(json::value_t::object).get<std::vector<int>>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is object");
+ EXPECT_THROW_MSG(json(json::value_t::null).get<json::array_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is null");
+ EXPECT_THROW_MSG(json(json::value_t::object).get<json::array_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is object");
+ EXPECT_THROW_MSG(json(json::value_t::string).get<json::array_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is string");
+ EXPECT_THROW_MSG(json(json::value_t::boolean).get<json::array_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is boolean");
+ EXPECT_THROW_MSG(json(json::value_t::number_integer).get<json::array_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<json::array_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_float).get<json::array_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is number");
+}
+
+template <typename T>
+class JsonGetStringTest : public ::testing::Test {
+ public:
+ JsonGetStringTest() : j(s_reference) {}
+
+ protected:
+ std::string s_reference {"Hello world"};
+ json j;
+};
+
+typedef ::testing::Types<std::string, std::string> JsonGetStringTestTypes;
+TYPED_TEST_CASE(JsonGetStringTest, JsonGetStringTestTypes);
+
+TYPED_TEST(JsonGetStringTest, Explicit)
+{
+ TypeParam s = (this->j).template get<TypeParam>();
+ EXPECT_EQ(json(s), this->j);
+}
+
+TYPED_TEST(JsonGetStringTest, Implicit)
+{
+ TypeParam s = this->j;
+ EXPECT_EQ(json(s), this->j);
+}
+
+// exception in case of a non-string type
+TEST(JsonGetStringExceptionTest, TypeError)
+{
+ EXPECT_THROW_MSG(json(json::value_t::null).get<std::string>(), json::type_error,
+ "[json.exception.type_error.302] type must be string, but is null");
+ EXPECT_THROW_MSG(json(json::value_t::object).get<std::string>(), json::type_error,
+ "[json.exception.type_error.302] type must be string, but is object");
+ EXPECT_THROW_MSG(json(json::value_t::array).get<std::string>(), json::type_error,
+ "[json.exception.type_error.302] type must be string, but is array");
+ EXPECT_THROW_MSG(json(json::value_t::boolean).get<std::string>(), json::type_error,
+ "[json.exception.type_error.302] type must be string, but is boolean");
+ EXPECT_THROW_MSG(json(json::value_t::number_integer).get<std::string>(), json::type_error,
+ "[json.exception.type_error.302] type must be string, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<std::string>(), json::type_error,
+ "[json.exception.type_error.302] type must be string, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_float).get<std::string>(), json::type_error,
+ "[json.exception.type_error.302] type must be string, but is number");
+}
+
+template <typename T>
+class JsonGetBooleanTest : public ::testing::Test {
+ public:
+ JsonGetBooleanTest() : j(b_reference) {}
+
+ protected:
+ bool b_reference {true};
+ json j;
+};
+
+typedef ::testing::Types<bool, bool> JsonGetBooleanTestTypes;
+TYPED_TEST_CASE(JsonGetBooleanTest, JsonGetBooleanTestTypes);
+
+TYPED_TEST(JsonGetBooleanTest, Explicit)
+{
+ TypeParam b = (this->j).template get<TypeParam>();
+ EXPECT_EQ(json(b), this->j);
+}
+
+TYPED_TEST(JsonGetBooleanTest, Implicit)
+{
+ TypeParam b = this->j;
+ EXPECT_EQ(json(b), this->j);
+}
+
+// exception in case of a non-string type
+TEST(JsonGetBooleanExceptionTest, TypeError)
+{
+ EXPECT_THROW_MSG(json(json::value_t::null).get<bool>(), json::type_error,
+ "[json.exception.type_error.302] type must be boolean, but is null");
+ EXPECT_THROW_MSG(json(json::value_t::object).get<bool>(), json::type_error,
+ "[json.exception.type_error.302] type must be boolean, but is object");
+ EXPECT_THROW_MSG(json(json::value_t::array).get<bool>(), json::type_error,
+ "[json.exception.type_error.302] type must be boolean, but is array");
+ EXPECT_THROW_MSG(json(json::value_t::string).get<bool>(), json::type_error,
+ "[json.exception.type_error.302] type must be boolean, but is string");
+ EXPECT_THROW_MSG(json(json::value_t::number_integer).get<bool>(), json::type_error,
+ "[json.exception.type_error.302] type must be boolean, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<bool>(), json::type_error,
+ "[json.exception.type_error.302] type must be boolean, but is number");
+ EXPECT_THROW_MSG(json(json::value_t::number_float).get<bool>(), json::type_error,
+ "[json.exception.type_error.302] type must be boolean, but is number");
+}
+
+template <typename T>
+class JsonGetIntegerTest : public ::testing::Test {
+ public:
+ JsonGetIntegerTest() : j(n_reference), j_unsigned(n_unsigned_reference) {}
+
+ protected:
+ int64_t n_reference {42};
+ json j;
+ uint64_t n_unsigned_reference {42u};
+ json j_unsigned;
+};
+
+typedef ::testing::Types<
+ short
+ , unsigned short
+ , int
+ , unsigned int
+ , long
+ , unsigned long
+ , long long
+ , unsigned long long
+ , int8_t
+ , int16_t
+ , int32_t
+ , int64_t
+#if 0
+ , int8_fast_t
+ , int16_fast_t
+ , int32_fast_t
+ , int64_fast_t
+ , int8_least_t
+ , int16_least_t
+ , int32_least_t
+ , int64_least_t
+#endif
+ , uint8_t
+ , uint16_t
+ , uint32_t
+ , uint64_t
+#if 0
+ , uint8_fast_t
+ , uint16_fast_t
+ , uint32_fast_t
+ , uint64_fast_t
+ , uint8_least_t
+ , uint16_least_t
+ , uint32_least_t
+ , uint64_least_t
+#endif
+ > JsonGetIntegerTestTypes;
+
+TYPED_TEST_CASE(JsonGetIntegerTest, JsonGetIntegerTestTypes);
+
+TYPED_TEST(JsonGetIntegerTest, Explicit)
+{
+ TypeParam n = (this->j).template get<TypeParam>();
+ EXPECT_EQ(json(n), this->j);
+}
+
+TYPED_TEST(JsonGetIntegerTest, Implicit)
+{
+ if (std::is_unsigned<TypeParam>::value)
+ {
+ TypeParam n = this->j_unsigned;
+ EXPECT_EQ(json(n), this->j_unsigned);
+ }
+ else
+ {
+ TypeParam n = this->j;
+ EXPECT_EQ(json(n), this->j);
+ }
+}
+
+// exception in case of a non-number type
+TEST(JsonGetIntegerExceptionTest, TypeError)
+{
+ EXPECT_THROW_MSG(json(json::value_t::null).get<int64_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is null");
+ EXPECT_THROW_MSG(json(json::value_t::object).get<int64_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is object");
+ EXPECT_THROW_MSG(json(json::value_t::array).get<int64_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is array");
+ EXPECT_THROW_MSG(json(json::value_t::string).get<int64_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is string");
+ EXPECT_THROW_MSG(json(json::value_t::boolean).get<int64_t>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is boolean");
+
+ EXPECT_NO_THROW(json(json::value_t::number_float).get<int64_t>());
+ EXPECT_NO_THROW(json(json::value_t::number_float).get<uint64_t>());
+}
+
+template <typename T>
+class JsonGetFloatTest : public ::testing::Test {
+ public:
+ JsonGetFloatTest() : j(n_reference) {}
+
+ protected:
+ double n_reference {42.23};
+ json j;
+};
+
+typedef ::testing::Types<double, float, double>
+ JsonGetFloatTestTypes;
+
+TYPED_TEST_CASE(JsonGetFloatTest, JsonGetFloatTestTypes);
+
+TYPED_TEST(JsonGetFloatTest, Explicit)
+{
+ TypeParam n = (this->j).template get<TypeParam>();
+ EXPECT_LT(std::fabs(JsonTest::GetValue(json(n)).number_float -
+ JsonTest::GetValue(this->j).number_float), 0.001);
+}
+
+TYPED_TEST(JsonGetFloatTest, Implicit)
+{
+ TypeParam n = this->j;
+ EXPECT_LT(std::fabs(JsonTest::GetValue(json(n)).number_float -
+ JsonTest::GetValue(this->j).number_float), 0.001);
+}
+
+// exception in case of a non-string type
+TEST(JsonGetFloatExceptionTest, TypeError)
+{
+ EXPECT_THROW_MSG(json(json::value_t::null).get<double>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is null");
+ EXPECT_THROW_MSG(json(json::value_t::object).get<double>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is object");
+ EXPECT_THROW_MSG(json(json::value_t::array).get<double>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is array");
+ EXPECT_THROW_MSG(json(json::value_t::string).get<double>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is string");
+ EXPECT_THROW_MSG(json(json::value_t::boolean).get<double>(), json::type_error,
+ "[json.exception.type_error.302] type must be number, but is boolean");
+
+ EXPECT_NO_THROW(json(json::value_t::number_integer).get<double>());
+ EXPECT_NO_THROW(json(json::value_t::number_unsigned).get<double>());
+}
+
+TEST(JsonGetEnumTest, Case)
+{
+ enum c_enum { value_1, value_2 };
+ enum class cpp_enum { value_1, value_2 };
+
+ EXPECT_EQ(json(value_1).get<c_enum>(), value_1);
+ EXPECT_EQ(json(cpp_enum::value_1).get<cpp_enum>(), cpp_enum::value_1);
+}
+
+class JsonObjectConversionTest : public ::testing::Test {
+ protected:
+ json j1 = {{"one", 1}, {"two", 2}, {"three", 3}};
+ json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}};
+ json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}};
+ json j4 = {{"one", true}, {"two", false}, {"three", true}};
+ json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}};
+};
+
+TEST_F(JsonObjectConversionTest, StdMap)
+{
+ auto m1 = j1.get<std::map<std::string, int>>();
+ auto m2 = j2.get<std::map<std::string, unsigned int>>();
+ auto m3 = j3.get<std::map<std::string, double>>();
+ auto m4 = j4.get<std::map<std::string, bool>>();
+ //auto m5 = j5.get<std::map<std::string, std::string>>();
+}
+
+TEST_F(JsonObjectConversionTest, StdUnorderedMap)
+{
+ auto m1 = j1.get<std::unordered_map<std::string, int>>();
+ auto m2 = j2.get<std::unordered_map<std::string, unsigned int>>();
+ auto m3 = j3.get<std::unordered_map<std::string, double>>();
+ auto m4 = j4.get<std::unordered_map<std::string, bool>>();
+ //auto m5 = j5.get<std::unordered_map<std::string, std::string>>();
+ //CHECK(m5["one"] == "eins");
+}
+
+TEST_F(JsonObjectConversionTest, StdMultiMap)
+{
+ auto m1 = j1.get<std::multimap<std::string, int>>();
+ auto m2 = j2.get<std::multimap<std::string, unsigned int>>();
+ auto m3 = j3.get<std::multimap<std::string, double>>();
+ auto m4 = j4.get<std::multimap<std::string, bool>>();
+ //auto m5 = j5.get<std::multimap<std::string, std::string>>();
+ //CHECK(m5["one"] == "eins");
+}
+
+TEST_F(JsonObjectConversionTest, StdUnorderedMultiMap)
+{
+ auto m1 = j1.get<std::unordered_multimap<std::string, int>>();
+ auto m2 = j2.get<std::unordered_multimap<std::string, unsigned int>>();
+ auto m3 = j3.get<std::unordered_multimap<std::string, double>>();
+ auto m4 = j4.get<std::unordered_multimap<std::string, bool>>();
+ //auto m5 = j5.get<std::unordered_multimap<std::string, std::string>>();
+ //CHECK(m5["one"] == "eins");
+}
+
+// exception in case of a non-object type
+TEST_F(JsonObjectConversionTest, Exception)
+{
+ EXPECT_THROW_MSG((json().get<std::map<std::string, int>>()), json::type_error,
+ "[json.exception.type_error.302] type must be object, but is null");
+}
+
+class JsonArrayConversionTest : public ::testing::Test {
+ protected:
+ json j1 = {1, 2, 3, 4};
+ json j2 = {1u, 2u, 3u, 4u};
+ json j3 = {1.2, 2.3, 3.4, 4.5};
+ json j4 = {true, false, true};
+ json j5 = {"one", "two", "three"};
+};
+
+TEST_F(JsonArrayConversionTest, StdList)
+{
+ auto m1 = j1.get<std::list<int>>();
+ auto m2 = j2.get<std::list<unsigned int>>();
+ auto m3 = j3.get<std::list<double>>();
+ auto m4 = j4.get<std::list<bool>>();
+ auto m5 = j5.get<std::list<std::string>>();
+}
+
+#if 0
+TEST_F(JsonArrayConversionTest, StdForwardList)
+{
+ auto m1 = j1.get<std::forward_list<int>>();
+ auto m2 = j2.get<std::forward_list<unsigned int>>();
+ auto m3 = j3.get<std::forward_list<double>>();
+ auto m4 = j4.get<std::forward_list<bool>>();
+ auto m5 = j5.get<std::forward_list<std::string>>();
+}
+#endif
+
+TEST_F(JsonArrayConversionTest, StdVector)
+{
+ auto m1 = j1.get<std::vector<int>>();
+ auto m2 = j2.get<std::vector<unsigned int>>();
+ auto m3 = j3.get<std::vector<double>>();
+ auto m4 = j4.get<std::vector<bool>>();
+ auto m5 = j5.get<std::vector<std::string>>();
+}
+
+TEST_F(JsonArrayConversionTest, StdDeque)
+{
+ auto m1 = j1.get<std::deque<int>>();
+ auto m2 = j2.get<std::deque<unsigned int>>();
+ auto m3 = j2.get<std::deque<double>>();
+ auto m4 = j4.get<std::deque<bool>>();
+ auto m5 = j5.get<std::deque<std::string>>();
+}
+
+TEST_F(JsonArrayConversionTest, StdSet)
+{
+ auto m1 = j1.get<std::set<int>>();
+ auto m2 = j2.get<std::set<unsigned int>>();
+ auto m3 = j3.get<std::set<double>>();
+ auto m4 = j4.get<std::set<bool>>();
+ auto m5 = j5.get<std::set<std::string>>();
+}
+
+TEST_F(JsonArrayConversionTest, StdUnorderedSet)
+{
+ auto m1 = j1.get<std::unordered_set<int>>();
+ auto m2 = j2.get<std::unordered_set<unsigned int>>();
+ auto m3 = j3.get<std::unordered_set<double>>();
+ auto m4 = j4.get<std::unordered_set<bool>>();
+ auto m5 = j5.get<std::unordered_set<std::string>>();
+}
+
+// exception in case of a non-object type
+TEST_F(JsonArrayConversionTest, Exception)
+{
+ EXPECT_THROW_MSG((json().get<std::list<int>>()), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is null");
+ EXPECT_THROW_MSG((json().get<std::vector<int>>()), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is null");
+ EXPECT_THROW_MSG((json().get<std::vector<json>>()), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is null");
+ EXPECT_THROW_MSG((json().get<std::list<json>>()), json::type_error,
+ "[json.exception.type_error.302] type must be array, but is null");
+ // does type really must be an array? or it rather must not be null?
+ // that's what I thought when other test like this one broke
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp b/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp
new file mode 100644
index 0000000..12ac280
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp
@@ -0,0 +1,138 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+#include "wpi/raw_istream.h"
+using wpi::json;
+
+#include <valarray>
+
+TEST(JsonDeserializationTest, SuccessfulStream)
+{
+ std::string s = "[\"foo\",1,2,3,false,{\"one\":1}]";
+ wpi::raw_mem_istream ss(s.data(), s.size());
+ json j = json::parse(ss);
+ ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
+}
+
+TEST(JsonDeserializationTest, SuccessfulStringLiteral)
+{
+ auto s = "[\"foo\",1,2,3,false,{\"one\":1}]";
+ json j = json::parse(s);
+ ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
+}
+
+TEST(JsonDeserializationTest, SuccessfulStdString)
+{
+ std::string s = "[\"foo\",1,2,3,false,{\"one\":1}]";
+ json j = json::parse(s);
+ ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
+}
+
+TEST(JsonDeserializationTest, SuccessfulStreamOperator)
+{
+ std::string s = "[\"foo\",1,2,3,false,{\"one\":1}]";
+ wpi::raw_mem_istream ss(s.data(), s.size());
+ json j;
+ ss >> j;
+ ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
+}
+
+TEST(JsonDeserializationTest, SuccessfulUserStringLiteral)
+{
+ ASSERT_EQ("[\"foo\",1,2,3,false,{\"one\":1}]"_json, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
+}
+
+TEST(JsonDeserializationTest, UnsuccessfulStream)
+{
+ std::string s = "[\"foo\",1,2,3,false,{\"one\":1}";
+ wpi::raw_mem_istream ss(s.data(), s.size());
+ ASSERT_THROW_MSG(json::parse(ss), json::parse_error,
+ "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
+}
+
+TEST(JsonDeserializationTest, UnsuccessfulStdString)
+{
+ std::string s = "[\"foo\",1,2,3,false,{\"one\":1}";
+ ASSERT_THROW_MSG(json::parse(s), json::parse_error,
+ "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
+}
+
+TEST(JsonDeserializationTest, UnsuccessfulStreamOperator)
+{
+ std::string s = "[\"foo\",1,2,3,false,{\"one\":1}";
+ wpi::raw_mem_istream ss(s.data(), s.size());
+ json j;
+ ASSERT_THROW_MSG(ss >> j, json::parse_error,
+ "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
+}
+
+TEST(JsonDeserializationTest, UnsuccessfulUserStringLiteral)
+{
+ ASSERT_THROW_MSG("[\"foo\",1,2,3,false,{\"one\":1}"_json, json::parse_error,
+ "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
+}
+
+// these cases are required for 100% line coverage
+class JsonDeserializationErrorTest
+ : public ::testing::TestWithParam<const char*> {};
+
+TEST_P(JsonDeserializationErrorTest, ErrorCase)
+{
+ ASSERT_THROW(json::parse(GetParam()), json::parse_error);
+}
+
+static const char* error_cases[] = {
+ "\"aaaaaa\\u",
+ "\"aaaaaa\\u1",
+ "\"aaaaaa\\u11111111",
+ "\"aaaaaau11111111\\",
+ "\"\x7F\xC1",
+ "\"\x7F\xDF\x7F",
+ "\"\x7F\xDF\xC0",
+ "\"\x7F\xE0\x9F",
+ "\"\x7F\xEF\xC0",
+ "\"\x7F\xED\x7F",
+ "\"\x7F\xF0\x8F",
+ "\"\x7F\xF0\xC0",
+ "\"\x7F\xF3\x7F",
+ "\"\x7F\xF3\xC0",
+ "\"\x7F\xF4\x7F",
+};
+
+INSTANTIATE_TEST_CASE_P(JsonDeserializationErrorTests,
+ JsonDeserializationErrorTest,
+ ::testing::ValuesIn(error_cases), );
diff --git a/wpiutil/src/test/native/cpp/json/unit-element_access1.cpp b/wpiutil/src/test/native/cpp/json/unit-element_access1.cpp
new file mode 100644
index 0000000..a97d6b6
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-element_access1.cpp
@@ -0,0 +1,873 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+class JsonElementArrayAccessTestBase {
+ public:
+ JsonElementArrayAccessTestBase() : j_const(j) {}
+
+ protected:
+ json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+ const json j_const;
+};
+
+class JsonElementArrayAccessTest : public ::testing::Test,
+ public JsonElementArrayAccessTestBase {};
+
+TEST_F(JsonElementArrayAccessTest, AtWithinBounds)
+{
+ EXPECT_EQ(j.at(0), json(1));
+ EXPECT_EQ(j.at(1), json(1u));
+ EXPECT_EQ(j.at(2), json(true));
+ EXPECT_EQ(j.at(3), json(nullptr));
+ EXPECT_EQ(j.at(4), json("string"));
+ EXPECT_EQ(j.at(5), json(42.23));
+ EXPECT_EQ(j.at(6), json::object());
+ EXPECT_EQ(j.at(7), json({1, 2, 3}));
+
+ EXPECT_EQ(j_const.at(0), json(1));
+ EXPECT_EQ(j_const.at(1), json(1u));
+ EXPECT_EQ(j_const.at(2), json(true));
+ EXPECT_EQ(j_const.at(3), json(nullptr));
+ EXPECT_EQ(j_const.at(4), json("string"));
+ EXPECT_EQ(j_const.at(5), json(42.23));
+ EXPECT_EQ(j_const.at(6), json::object());
+ EXPECT_EQ(j_const.at(7), json({1, 2, 3}));
+}
+
+TEST_F(JsonElementArrayAccessTest, AtOutsideBounds)
+{
+ EXPECT_THROW_MSG(j.at(8), json::out_of_range,
+ "[json.exception.out_of_range.401] array index 8 is out of range");
+ EXPECT_THROW_MSG(j_const.at(8), json::out_of_range,
+ "[json.exception.out_of_range.401] array index 8 is out of range");
+}
+
+TEST(JsonElementNonArrayAtAccessTest, Null)
+{
+ json j_nonarray(json::value_t::null);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with null");
+ EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with null");
+}
+
+TEST(JsonElementNonArrayAtAccessTest, Boolean)
+{
+ json j_nonarray(json::value_t::boolean);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with boolean");
+ EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with boolean");
+}
+
+TEST(JsonElementNonArrayAtAccessTest, String)
+{
+ json j_nonarray(json::value_t::string);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with string");
+ EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with string");
+}
+
+TEST(JsonElementNonArrayAtAccessTest, Object)
+{
+ json j_nonarray(json::value_t::object);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with object");
+ EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with object");
+}
+
+TEST(JsonElementNonArrayAtAccessTest, Integer)
+{
+ json j_nonarray(json::value_t::number_integer);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+ EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+}
+
+TEST(JsonElementNonArrayAtAccessTest, Unsigned)
+{
+ json j_nonarray(json::value_t::number_unsigned);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+ EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+}
+
+TEST(JsonElementNonArrayAtAccessTest, Float)
+{
+ json j_nonarray(json::value_t::number_float);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+ EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+}
+
+TEST_F(JsonElementArrayAccessTest, FrontAndBack)
+{
+ EXPECT_EQ(j.front(), json(1));
+ EXPECT_EQ(j_const.front(), json(1));
+ EXPECT_EQ(j.back(), json({1, 2, 3}));
+ EXPECT_EQ(j_const.back(), json({1, 2, 3}));
+}
+
+TEST_F(JsonElementArrayAccessTest, OperatorWithinBounds)
+{
+ EXPECT_EQ(j[0], json(1));
+ EXPECT_EQ(j[1], json(1u));
+ EXPECT_EQ(j[2], json(true));
+ EXPECT_EQ(j[3], json(nullptr));
+ EXPECT_EQ(j[4], json("string"));
+ EXPECT_EQ(j[5], json(42.23));
+ EXPECT_EQ(j[6], json::object());
+ EXPECT_EQ(j[7], json({1, 2, 3}));
+
+ EXPECT_EQ(j_const[0], json(1));
+ EXPECT_EQ(j_const[1], json(1u));
+ EXPECT_EQ(j_const[2], json(true));
+ EXPECT_EQ(j_const[3], json(nullptr));
+ EXPECT_EQ(j_const[4], json("string"));
+ EXPECT_EQ(j_const[5], json(42.23));
+ EXPECT_EQ(j_const[6], json::object());
+ EXPECT_EQ(j_const[7], json({1, 2, 3}));
+}
+
+TEST(JsonElementNonArrayOperatorAccessTest, NullStandard)
+{
+ json j_nonarray(json::value_t::null);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_NO_THROW(j_nonarray[0]);
+ EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with null");
+}
+
+// implicit transformation to properly filled array
+TEST(JsonElementNonArrayOperatorAccessTest, NullImplicitFilled)
+{
+ json j_nonarray;
+ j_nonarray[3] = 42;
+ EXPECT_EQ(j_nonarray, json({nullptr, nullptr, nullptr, 42}));
+}
+
+TEST(JsonElementNonArrayOperatorAccessTest, Boolean)
+{
+ json j_nonarray(json::value_t::boolean);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with boolean");
+ EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with boolean");
+}
+
+TEST(JsonElementNonArrayOperatorAccessTest, String)
+{
+ json j_nonarray(json::value_t::string);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with string");
+ EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with string");
+}
+
+TEST(JsonElementNonArrayOperatorAccessTest, Object)
+{
+ json j_nonarray(json::value_t::object);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with object");
+ EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with object");
+}
+
+TEST(JsonElementNonArrayOperatorAccessTest, Integer)
+{
+ json j_nonarray(json::value_t::number_integer);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+}
+
+TEST(JsonElementNonArrayOperatorAccessTest, Unsigned)
+{
+ json j_nonarray(json::value_t::number_unsigned);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+}
+
+TEST(JsonElementNonArrayOperatorAccessTest, Float)
+{
+ json j_nonarray(json::value_t::number_float);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+}
+
+class JsonElementArrayRemoveTest : public ::testing::Test,
+ public JsonElementArrayAccessTestBase {};
+
+
+// remove element by index
+TEST_F(JsonElementArrayRemoveTest, Index0)
+{
+ j.erase(0);
+ EXPECT_EQ(j, json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index1)
+{
+ j.erase(1);
+ EXPECT_EQ(j, json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index2)
+{
+ j.erase(2);
+ EXPECT_EQ(j, json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index3)
+{
+ j.erase(3);
+ EXPECT_EQ(j, json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index4)
+{
+ j.erase(4);
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index5)
+{
+ j.erase(5);
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index6)
+{
+ j.erase(6);
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index7)
+{
+ j.erase(7);
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, json::object()}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, Index8)
+{
+ EXPECT_THROW_MSG(j.erase(8), json::out_of_range,
+ "[json.exception.out_of_range.401] array index 8 is out of range");
+}
+
+// erase(begin())
+TEST_F(JsonElementArrayRemoveTest, Begin)
+{
+ j.erase(j.begin());
+ EXPECT_EQ(j, json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, BeginConst)
+{
+ j.erase(j.cbegin());
+ EXPECT_EQ(j, json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+// erase(begin(), end())
+TEST_F(JsonElementArrayRemoveTest, BeginEnd)
+{
+ j.erase(j.begin(), j.end());
+ EXPECT_EQ(j, json::array());
+}
+TEST_F(JsonElementArrayRemoveTest, BeginEndConst)
+{
+ j.erase(j.cbegin(), j.cend());
+ EXPECT_EQ(j, json::array());
+}
+
+// erase(begin(), begin())
+TEST_F(JsonElementArrayRemoveTest, BeginBegin)
+{
+ j.erase(j.begin(), j.begin());
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, BeginBeginConst)
+{
+ j.erase(j.cbegin(), j.cbegin());
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+}
+
+// erase at offset
+TEST_F(JsonElementArrayRemoveTest, Offset)
+{
+ json::iterator it = j.begin() + 4;
+ j.erase(it);
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, OffsetConst)
+{
+ json::const_iterator it = j.cbegin() + 4;
+ j.erase(it);
+ EXPECT_EQ(j, json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
+}
+
+// erase subrange
+TEST_F(JsonElementArrayRemoveTest, Subrange)
+{
+ j.erase(j.begin() + 3, j.begin() + 6);
+ EXPECT_EQ(j, json({1, 1u, true, json::object(), {1, 2, 3}}));
+}
+
+TEST_F(JsonElementArrayRemoveTest, SubrangeConst)
+{
+ j.erase(j.cbegin() + 3, j.cbegin() + 6);
+ EXPECT_EQ(j, json({1, 1u, true, json::object(), {1, 2, 3}}));
+}
+
+// different arrays
+TEST_F(JsonElementArrayRemoveTest, Different)
+{
+ json j2 = {"foo", "bar"};
+ EXPECT_THROW_MSG(j.erase(j2.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(j.erase(j.begin(), j2.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(j.erase(j2.begin(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(j.erase(j2.begin(), j2.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+}
+
+TEST_F(JsonElementArrayRemoveTest, DifferentConst)
+{
+ json j2 = {"foo", "bar"};
+ EXPECT_THROW_MSG(j.erase(j2.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(j.erase(j.cbegin(), j2.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(j.erase(j2.cbegin(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(j.erase(j2.cbegin(), j2.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+}
+
+// remove element by index in non-array type
+TEST(JsonElementNonArrayIndexRemoveTest, Null)
+{
+ json j_nonobject(json::value_t::null);
+ EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with null");
+}
+
+TEST(JsonElementNonArrayIndexRemoveTest, Boolean)
+{
+ json j_nonobject(json::value_t::boolean);
+ EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with boolean");
+}
+
+TEST(JsonElementNonArrayIndexRemoveTest, String)
+{
+ json j_nonobject(json::value_t::string);
+ EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with string");
+}
+
+TEST(JsonElementNonArrayIndexRemoveTest, Object)
+{
+ json j_nonobject(json::value_t::object);
+ EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with object");
+}
+
+TEST(JsonElementNonArrayIndexRemoveTest, Integer)
+{
+ json j_nonobject(json::value_t::number_integer);
+ EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with number");
+}
+
+TEST(JsonElementNonArrayIndexRemoveTest, Unsigned)
+{
+ json j_nonobject(json::value_t::number_unsigned);
+ EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with number");
+}
+
+TEST(JsonElementNonArrayIndexRemoveTest, Float)
+{
+ json j_nonobject(json::value_t::number_float);
+ EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with number");
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, Null)
+{
+ json j;
+ EXPECT_THROW_MSG(j.front(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(j.back(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, NullConst)
+{
+ const json j{};
+ EXPECT_THROW_MSG(j.front(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(j.back(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, String)
+{
+ json j = "foo";
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, StringConst)
+{
+ const json j = "bar";
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, Boolean)
+{
+ json j = false;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, BooleanConst)
+{
+ const json j = true;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, Integer)
+{
+ json j = 17;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, IntegerConst)
+{
+ const json j = 17;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, Unsigned)
+{
+ json j = 17u;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, UnsignedConst)
+{
+ const json j = 17u;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, Float)
+{
+ json j = 23.42;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayFrontBackAccessTest, FloatConst)
+{
+ const json j = 23.42;
+ EXPECT_EQ(j.front(), j);
+ EXPECT_EQ(j.back(), j);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Null)
+{
+ json j;
+ EXPECT_THROW_MSG(j.erase(j.begin()), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with null");
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, NullConst)
+{
+ json j;
+ EXPECT_THROW_MSG(j.erase(j.cbegin()), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with null");
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, String)
+{
+ json j = "foo";
+ j.erase(j.begin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, StringConst)
+{
+ json j = "bar";
+ j.erase(j.cbegin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Boolean)
+{
+ json j = false;
+ j.erase(j.begin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, BooleanConst)
+{
+ json j = true;
+ j.erase(j.cbegin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Integer)
+{
+ json j = 17;
+ j.erase(j.begin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, IntegerConst)
+{
+ json j = 17;
+ j.erase(j.cbegin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Unsigned)
+{
+ json j = 17u;
+ j.erase(j.begin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, UnsignedConst)
+{
+ json j = 17u;
+ j.erase(j.cbegin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Float)
+{
+ json j = 23.42;
+ j.erase(j.begin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneValidIteratorRemoveTest, FloatConst)
+{
+ json j = 23.42;
+ j.erase(j.cbegin());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, String)
+{
+ json j = "foo";
+ EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, StringConst)
+{
+ json j = "bar";
+ EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Boolean)
+{
+ json j = false;
+ EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, BooleanConst)
+{
+ json j = true;
+ EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Integer)
+{
+ json j = 17;
+ EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, IntegerConst)
+{
+ json j = 17;
+ EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Unsigned)
+{
+ json j = 17u;
+ EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, UnsignedConst)
+{
+ json j = 17u;
+ EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Float)
+{
+ json j = 23.42;
+ EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, FloatConst)
+{
+ json j = 23.42;
+ EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.205] iterator out of range");
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Null)
+{
+ json j;
+ EXPECT_THROW_MSG(j.erase(j.begin(), j.end()), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with null");
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, NullConst)
+{
+ json j;
+ EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cend()), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with null");
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, String)
+{
+ json j = "foo";
+ j.erase(j.begin(), j.end());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, StringConst)
+{
+ json j = "bar";
+ j.erase(j.cbegin(), j.cend());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Boolean)
+{
+ json j = false;
+ j.erase(j.begin(), j.end());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, BooleanConst)
+{
+ json j = true;
+ j.erase(j.cbegin(), j.cend());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Integer)
+{
+ json j = 17;
+ j.erase(j.begin(), j.end());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, IntegerConst)
+{
+ json j = 17;
+ j.erase(j.cbegin(), j.cend());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Unsigned)
+{
+ json j = 17u;
+ j.erase(j.begin(), j.end());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, UnsignedConst)
+{
+ json j = 17u;
+ j.erase(j.cbegin(), j.cend());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Float)
+{
+ json j = 23.42;
+ j.erase(j.begin(), j.end());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, FloatConst)
+{
+ json j = 23.42;
+ j.erase(j.cbegin(), j.cend());
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, String)
+{
+ json j = "foo";
+ EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, StringConst)
+{
+ json j = "bar";
+ EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Boolean)
+{
+ json j = false;
+ EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, BooleanConst)
+{
+ json j = true;
+ EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Integer)
+{
+ json j = 17;
+ EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, IntegerConst)
+{
+ json j = 17;
+ EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Unsigned)
+{
+ json j = 17u;
+ EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, UnsignedConst)
+{
+ json j = 17u;
+ EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Float)
+{
+ json j = 23.42;
+ EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
+
+TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, FloatConst)
+{
+ json j = 23.42;
+ EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+ EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.204] iterators out of range");
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp b/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp
new file mode 100644
index 0000000..4b64123
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp
@@ -0,0 +1,923 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+class JsonElementObjectAccessTestBase {
+ public:
+ JsonElementObjectAccessTestBase() : j_const(j) {}
+
+ protected:
+ json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}};
+ const json j_const;
+};
+
+class JsonElementObjectAccessTest : public ::testing::Test,
+ public JsonElementObjectAccessTestBase {};
+
+TEST_F(JsonElementObjectAccessTest, AtWithinBounds)
+{
+ EXPECT_EQ(j.at("integer"), json(1));
+ EXPECT_EQ(j.at("unsigned"), json(1u));
+ EXPECT_EQ(j.at("boolean"), json(true));
+ EXPECT_EQ(j.at("null"), json(nullptr));
+ EXPECT_EQ(j.at("string"), json("hello world"));
+ EXPECT_EQ(j.at("floating"), json(42.23));
+ EXPECT_EQ(j.at("object"), json::object());
+ EXPECT_EQ(j.at("array"), json({1, 2, 3}));
+
+ EXPECT_EQ(j_const.at("integer"), json(1));
+ EXPECT_EQ(j_const.at("unsigned"), json(1u));
+ EXPECT_EQ(j_const.at("boolean"), json(true));
+ EXPECT_EQ(j_const.at("null"), json(nullptr));
+ EXPECT_EQ(j_const.at("string"), json("hello world"));
+ EXPECT_EQ(j_const.at("floating"), json(42.23));
+ EXPECT_EQ(j_const.at("object"), json::object());
+ EXPECT_EQ(j_const.at("array"), json({1, 2, 3}));
+}
+
+TEST_F(JsonElementObjectAccessTest, AtOutsideBounds)
+{
+ EXPECT_THROW_MSG(j.at("foo"), json::out_of_range,
+ "[json.exception.out_of_range.403] key 'foo' not found");
+ EXPECT_THROW_MSG(j_const.at("foo"), json::out_of_range,
+ "[json.exception.out_of_range.403] key 'foo' not found");
+}
+
+TEST(JsonElementNonObjectAtAccessTest, Null)
+{
+ json j_nonobject(json::value_t::null);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with null");
+ EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with null");
+}
+
+TEST(JsonElementNonObjectAtAccessTest, Boolean)
+{
+ json j_nonobject(json::value_t::boolean);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with boolean");
+ EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with boolean");
+}
+
+TEST(JsonElementNonObjectAtAccessTest, String)
+{
+ json j_nonobject(json::value_t::string);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with string");
+ EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with string");
+}
+
+TEST(JsonElementNonObjectAtAccessTest, Array)
+{
+ json j_nonobject(json::value_t::array);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with array");
+ EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with array");
+}
+
+TEST(JsonElementNonObjectAtAccessTest, Integer)
+{
+ json j_nonobject(json::value_t::number_integer);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+}
+
+TEST(JsonElementNonObjectAtAccessTest, Unsigned)
+{
+ json j_nonobject(json::value_t::number_unsigned);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+}
+
+TEST(JsonElementNonObjectAtAccessTest, Float)
+{
+ json j_nonobject(json::value_t::number_float);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
+ "[json.exception.type_error.304] cannot use at() with number");
+}
+
+TEST_F(JsonElementObjectAccessTest, KeyValueExist)
+{
+ EXPECT_EQ(j.value("integer", 2), 1);
+ EXPECT_LT(std::fabs(j.value("integer", 1.0) - 1), 0.001);
+ EXPECT_EQ(j.value("unsigned", 2), 1);
+ EXPECT_LT(std::fabs(j.value("unsigned", 1.0) - 1), 0.001);
+ EXPECT_EQ(j.value("null", json(1)), json());
+ EXPECT_EQ(j.value("boolean", false), true);
+ EXPECT_EQ(j.value("string", "bar"), "hello world");
+ EXPECT_EQ(j.value("string", std::string("bar")), "hello world");
+ EXPECT_LT(std::fabs(j.value("floating", 12.34) - 42.23), 0.001);
+ EXPECT_EQ(j.value("floating", 12), 42);
+ EXPECT_EQ(j.value("object", json({{"foo", "bar"}})), json::object());
+ EXPECT_EQ(j.value("array", json({10, 100})), json({1, 2, 3}));
+
+ EXPECT_EQ(j_const.value("integer", 2), 1);
+ EXPECT_LT(std::fabs(j_const.value("integer", 1.0) - 1), 0.001);
+ EXPECT_EQ(j_const.value("unsigned", 2), 1);
+ EXPECT_LT(std::fabs(j_const.value("unsigned", 1.0) - 1), 0.001);
+ EXPECT_EQ(j_const.value("boolean", false), true);
+ EXPECT_EQ(j_const.value("string", "bar"), "hello world");
+ EXPECT_EQ(j_const.value("string", std::string("bar")), "hello world");
+ EXPECT_LT(std::fabs(j_const.value("floating", 12.34) - 42.23), 0.001);
+ EXPECT_EQ(j_const.value("floating", 12), 42);
+ EXPECT_EQ(j_const.value("object", json({{"foo", "bar"}})), json::object());
+ EXPECT_EQ(j_const.value("array", json({10, 100})), json({1, 2, 3}));
+}
+
+TEST_F(JsonElementObjectAccessTest, KeyValueNotExist)
+{
+ EXPECT_EQ(j.value("_", 2), 2);
+ EXPECT_EQ(j.value("_", 2u), 2u);
+ EXPECT_EQ(j.value("_", false), false);
+ EXPECT_EQ(j.value("_", "bar"), "bar");
+ EXPECT_LT(std::fabs(j.value("_", 12.34) - 12.34), 0.001);
+ EXPECT_EQ(j.value("_", json({{"foo", "bar"}})), json({{"foo", "bar"}}));
+ EXPECT_EQ(j.value("_", json({10, 100})), json({10, 100}));
+
+ EXPECT_EQ(j_const.value("_", 2), 2);
+ EXPECT_EQ(j_const.value("_", 2u), 2u);
+ EXPECT_EQ(j_const.value("_", false), false);
+ EXPECT_EQ(j_const.value("_", "bar"), "bar");
+ EXPECT_LT(std::fabs(j_const.value("_", 12.34) - 12.34), 0.001);
+ EXPECT_EQ(j_const.value("_", json({{"foo", "bar"}})), json({{"foo", "bar"}}));
+ EXPECT_EQ(j_const.value("_", json({10, 100})), json({10, 100}));
+}
+
+TEST(JsonElementNonObjectKeyValueAccessTest, Null)
+{
+ json j_nonobject(json::value_t::null);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with null");
+ EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with null");
+}
+
+TEST(JsonElementNonObjectKeyValueAccessTest, Boolean)
+{
+ json j_nonobject(json::value_t::boolean);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with boolean");
+ EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with boolean");
+}
+
+TEST(JsonElementNonObjectKeyValueAccessTest, String)
+{
+ json j_nonobject(json::value_t::string);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with string");
+ EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with string");
+}
+
+TEST(JsonElementNonObjectKeyValueAccessTest, Array)
+{
+ json j_nonobject(json::value_t::array);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with array");
+ EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with array");
+}
+
+TEST(JsonElementNonObjectKeyValueAccessTest, Integer)
+{
+ json j_nonobject(json::value_t::number_integer);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+}
+
+TEST(JsonElementNonObjectKeyValueAccessTest, Unsigned)
+{
+ json j_nonobject(json::value_t::number_unsigned);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+}
+
+TEST(JsonElementNonObjectKeyValueAccessTest, Float)
+{
+ json j_nonobject(json::value_t::number_float);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+}
+
+TEST_F(JsonElementObjectAccessTest, PointerValueExist)
+{
+ EXPECT_EQ(j.value("/integer"_json_pointer, 2), 1);
+ EXPECT_LT(std::fabs(j.value("/integer"_json_pointer, 1.0) - 1), 0.001);
+ EXPECT_EQ(j.value("/unsigned"_json_pointer, 2), 1);
+ EXPECT_LT(std::fabs(j.value("/unsigned"_json_pointer, 1.0) - 1), 0.001);
+ EXPECT_EQ(j.value("/null"_json_pointer, json(1)), json());
+ EXPECT_EQ(j.value("/boolean"_json_pointer, false), true);
+ EXPECT_EQ(j.value("/string"_json_pointer, "bar"), "hello world");
+ EXPECT_EQ(j.value("/string"_json_pointer, std::string("bar")), "hello world");
+ EXPECT_LT(std::fabs(j.value("/floating"_json_pointer, 12.34) - 42.23), 0.001);
+ EXPECT_EQ(j.value("/floating"_json_pointer, 12), 42);
+ EXPECT_EQ(j.value("/object"_json_pointer, json({{"foo", "bar"}})), json::object());
+ EXPECT_EQ(j.value("/array"_json_pointer, json({10, 100})), json({1, 2, 3}));
+
+ EXPECT_EQ(j_const.value("/integer"_json_pointer, 2), 1);
+ EXPECT_LT(std::fabs(j_const.value("/integer"_json_pointer, 1.0) - 1), 0.001);
+ EXPECT_EQ(j_const.value("/unsigned"_json_pointer, 2), 1);
+ EXPECT_LT(std::fabs(j_const.value("/unsigned"_json_pointer, 1.0) - 1), 0.001);
+ EXPECT_EQ(j_const.value("/boolean"_json_pointer, false), true);
+ EXPECT_EQ(j_const.value("/string"_json_pointer, "bar"), "hello world");
+ EXPECT_EQ(j_const.value("/string"_json_pointer, std::string("bar")), "hello world");
+ EXPECT_LT(std::fabs(j_const.value("/floating"_json_pointer, 12.34) - 42.23), 0.001);
+ EXPECT_EQ(j_const.value("/floating"_json_pointer, 12), 42);
+ EXPECT_EQ(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})), json::object());
+ EXPECT_EQ(j_const.value("/array"_json_pointer, json({10, 100})), json({1, 2, 3}));
+}
+
+TEST(JsonElementNonObjectPointerValueAccessTest, Null)
+{
+ json j_nonobject(json::value_t::null);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with null");
+ EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with null");
+}
+
+TEST(JsonElementNonObjectPointerValueAccessTest, Boolean)
+{
+ json j_nonobject(json::value_t::boolean);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with boolean");
+ EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with boolean");
+}
+
+TEST(JsonElementNonObjectPointerValueAccessTest, String)
+{
+ json j_nonobject(json::value_t::string);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with string");
+ EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with string");
+}
+
+TEST(JsonElementNonObjectPointerValueAccessTest, Array)
+{
+ json j_nonobject(json::value_t::array);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with array");
+ EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with array");
+}
+
+TEST(JsonElementNonObjectPointerValueAccessTest, Integer)
+{
+ json j_nonobject(json::value_t::number_integer);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+}
+
+TEST(JsonElementNonObjectPointerValueAccessTest, Unsigned)
+{
+ json j_nonobject(json::value_t::number_unsigned);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+}
+
+TEST(JsonElementNonObjectPointerValueAccessTest, Float)
+{
+ json j_nonobject(json::value_t::number_float);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+ EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
+ "[json.exception.type_error.306] cannot use value() with number");
+}
+#if 0
+TEST_F(JsonElementObjectAccessTest, FrontAndBack)
+{
+ // "array" is the smallest key
+ EXPECT_EQ(j.front(), json({1, 2, 3}));
+ EXPECT_EQ(j_const.front(), json({1, 2, 3}));
+ // "unsigned" is the largest key
+ EXPECT_EQ(j.back(), json(1u));
+ EXPECT_EQ(j_const.back(), json(1u));
+}
+#endif
+TEST_F(JsonElementObjectAccessTest, OperatorWithinBounds)
+{
+ EXPECT_EQ(j["integer"], json(1));
+ EXPECT_EQ(j[json::object_t::key_type("integer")], j["integer"]);
+
+ EXPECT_EQ(j["unsigned"], json(1u));
+ EXPECT_EQ(j[json::object_t::key_type("unsigned")], j["unsigned"]);
+
+ EXPECT_EQ(j["boolean"], json(true));
+ EXPECT_EQ(j[json::object_t::key_type("boolean")], j["boolean"]);
+
+ EXPECT_EQ(j["null"], json(nullptr));
+ EXPECT_EQ(j[json::object_t::key_type("null")], j["null"]);
+
+ EXPECT_EQ(j["string"], json("hello world"));
+ EXPECT_EQ(j[json::object_t::key_type("string")], j["string"]);
+
+ EXPECT_EQ(j["floating"], json(42.23));
+ EXPECT_EQ(j[json::object_t::key_type("floating")], j["floating"]);
+
+ EXPECT_EQ(j["object"], json::object());
+ EXPECT_EQ(j[json::object_t::key_type("object")], j["object"]);
+
+ EXPECT_EQ(j["array"], json({1, 2, 3}));
+ EXPECT_EQ(j[json::object_t::key_type("array")], j["array"]);
+
+ EXPECT_EQ(j_const["integer"], json(1));
+ EXPECT_EQ(j_const[json::object_t::key_type("integer")], j["integer"]);
+
+ EXPECT_EQ(j_const["boolean"], json(true));
+ EXPECT_EQ(j_const[json::object_t::key_type("boolean")], j["boolean"]);
+
+ EXPECT_EQ(j_const["null"], json(nullptr));
+ EXPECT_EQ(j_const[json::object_t::key_type("null")], j["null"]);
+
+ EXPECT_EQ(j_const["string"], json("hello world"));
+ EXPECT_EQ(j_const[json::object_t::key_type("string")], j["string"]);
+
+ EXPECT_EQ(j_const["floating"], json(42.23));
+ EXPECT_EQ(j_const[json::object_t::key_type("floating")], j["floating"]);
+
+ EXPECT_EQ(j_const["object"], json::object());
+ EXPECT_EQ(j_const[json::object_t::key_type("object")], j["object"]);
+
+ EXPECT_EQ(j_const["array"], json({1, 2, 3}));
+ EXPECT_EQ(j_const[json::object_t::key_type("array")], j["array"]);
+}
+
+TEST(JsonElementNonObjectOperatorAccessTest, Null)
+{
+ json j_nonobject(json::value_t::null);
+ json j_nonobject2(json::value_t::null);
+ const json j_const_nonobject(j_nonobject);
+ EXPECT_NO_THROW(j_nonobject["foo"]);
+ EXPECT_NO_THROW(j_nonobject2[json::object_t::key_type("foo")]);
+ EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with null");
+ EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with null");
+}
+
+TEST(JsonElementNonObjectOperatorAccessTest, Boolean)
+{
+ json j_nonobject(json::value_t::boolean);
+ const json j_const_nonobject(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with boolean");
+ EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with boolean");
+ EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with boolean");
+ EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with boolean");
+}
+
+TEST(JsonElementNonObjectOperatorAccessTest, String)
+{
+ json j_nonobject(json::value_t::string);
+ const json j_const_nonobject(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with string");
+ EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with string");
+ EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with string");
+ EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with string");
+}
+
+TEST(JsonElementNonObjectOperatorAccessTest, Array)
+{
+ json j_nonobject(json::value_t::array);
+ const json j_const_nonobject(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with array");
+ EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with array");
+ EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with array");
+ EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with array");
+}
+
+TEST(JsonElementNonObjectOperatorAccessTest, Integer)
+{
+ json j_nonobject(json::value_t::number_integer);
+ const json j_const_nonobject(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+}
+
+TEST(JsonElementNonObjectOperatorAccessTest, Unsigned)
+{
+ json j_nonobject(json::value_t::number_unsigned);
+ const json j_const_nonobject(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+}
+
+TEST(JsonElementNonObjectOperatorAccessTest, Float)
+{
+ json j_nonobject(json::value_t::number_float);
+ const json j_const_nonobject(j_nonobject);
+ EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+ EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
+ "[json.exception.type_error.305] cannot use operator[] with number");
+}
+
+class JsonElementObjectRemoveTest : public ::testing::Test,
+ public JsonElementObjectAccessTestBase {};
+
+TEST_F(JsonElementObjectRemoveTest, Key)
+{
+ EXPECT_NE(j.find("integer"), j.end());
+ EXPECT_EQ(j.erase("integer"), 1u);
+ EXPECT_EQ(j.find("integer"), j.end());
+ EXPECT_EQ(j.erase("integer"), 0u);
+
+ EXPECT_NE(j.find("unsigned"), j.end());
+ EXPECT_EQ(j.erase("unsigned"), 1u);
+ EXPECT_EQ(j.find("unsigned"), j.end());
+ EXPECT_EQ(j.erase("unsigned"), 0u);
+
+ EXPECT_NE(j.find("boolean"), j.end());
+ EXPECT_EQ(j.erase("boolean"), 1u);
+ EXPECT_EQ(j.find("boolean"), j.end());
+ EXPECT_EQ(j.erase("boolean"), 0u);
+
+ EXPECT_NE(j.find("null"), j.end());
+ EXPECT_EQ(j.erase("null"), 1u);
+ EXPECT_EQ(j.find("null"), j.end());
+ EXPECT_EQ(j.erase("null"), 0u);
+
+ EXPECT_NE(j.find("string"), j.end());
+ EXPECT_EQ(j.erase("string"), 1u);
+ EXPECT_EQ(j.find("string"), j.end());
+ EXPECT_EQ(j.erase("string"), 0u);
+
+ EXPECT_NE(j.find("floating"), j.end());
+ EXPECT_EQ(j.erase("floating"), 1u);
+ EXPECT_EQ(j.find("floating"), j.end());
+ EXPECT_EQ(j.erase("floating"), 0u);
+
+ EXPECT_NE(j.find("object"), j.end());
+ EXPECT_EQ(j.erase("object"), 1u);
+ EXPECT_EQ(j.find("object"), j.end());
+ EXPECT_EQ(j.erase("object"), 0u);
+
+ EXPECT_NE(j.find("array"), j.end());
+ EXPECT_EQ(j.erase("array"), 1u);
+ EXPECT_EQ(j.find("array"), j.end());
+ EXPECT_EQ(j.erase("array"), 0u);
+}
+
+// erase(begin())
+TEST_F(JsonElementObjectRemoveTest, Begin)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ jobject.erase(jobject.begin());
+ EXPECT_EQ(jobject, json({{"b", 1}, {"c", 17u}}));
+}
+
+TEST_F(JsonElementObjectRemoveTest, BeginConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ jobject.erase(jobject.cbegin());
+ EXPECT_EQ(jobject, json({{"b", 1}, {"c", 17u}}));
+}
+
+// erase(begin(), end())
+TEST_F(JsonElementObjectRemoveTest, BeginEnd)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json::iterator it2 = jobject.erase(jobject.begin(), jobject.end());
+ EXPECT_EQ(jobject, json::object());
+ EXPECT_EQ(it2, jobject.end());
+#else
+ EXPECT_THROW(jobject.erase(jobject.begin(), jobject.end()), json::type_error);
+#endif
+}
+
+TEST_F(JsonElementObjectRemoveTest, BeginEndConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend());
+ EXPECT_EQ(jobject, json::object());
+ EXPECT_EQ(it2, jobject.cend());
+#else
+ EXPECT_THROW(jobject.erase(jobject.cbegin(), jobject.cend()), json::type_error);
+#endif
+}
+
+TEST_F(JsonElementObjectRemoveTest, BeginBegin)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin());
+ EXPECT_EQ(jobject, json({{"a", "a"}, {"b", 1}, {"c", 17u}}));
+ EXPECT_EQ(*it2, json("a"));
+#else
+ EXPECT_THROW(jobject.erase(jobject.begin(), jobject.end()), json::type_error);
+#endif
+}
+
+TEST_F(JsonElementObjectRemoveTest, BeginBeginConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+#if 0
+ json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin());
+ EXPECT_EQ(jobject, json({{"a", "a"}, {"b", 1}, {"c", 17u}}));
+ EXPECT_EQ(*it2, json("a"));
+#else
+ EXPECT_THROW(jobject.erase(jobject.cbegin(), jobject.cbegin()), json::type_error);
+#endif
+}
+
+TEST_F(JsonElementObjectRemoveTest, Offset)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ json::iterator it = jobject.find("b");
+ jobject.erase(it);
+ EXPECT_EQ(jobject, json({{"a", "a"}, {"c", 17u}}));
+}
+
+TEST_F(JsonElementObjectRemoveTest, OffsetConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ json::const_iterator it = jobject.find("b");
+ jobject.erase(it);
+ EXPECT_EQ(jobject, json({{"a", "a"}, {"c", 17u}}));
+}
+
+TEST_F(JsonElementObjectRemoveTest, Subrange)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
+#if 0
+ json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
+ EXPECT_EQ(jobject, json({{"a", "a"}, {"e", true}}));
+ EXPECT_EQ(*it2, json(true));
+#else
+ EXPECT_THROW(jobject.erase(jobject.find("b"), jobject.find("e")), json::type_error);
+#endif
+}
+
+TEST_F(JsonElementObjectRemoveTest, SubrangeConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
+#if 0
+ json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
+ EXPECT_EQ(jobject, json({{"a", "a"}, {"e", true}}));
+ EXPECT_EQ(*it2, json(true));
+#else
+ EXPECT_THROW(jobject.erase(jobject.find("b"), jobject.find("e")), json::type_error);
+#endif
+}
+
+TEST_F(JsonElementObjectRemoveTest, Different)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
+ json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ EXPECT_THROW_MSG(jobject.erase(jobject2.begin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(jobject.erase(jobject.begin(), jobject2.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(jobject.erase(jobject2.begin(), jobject.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(jobject.erase(jobject2.begin(), jobject2.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+}
+
+TEST_F(JsonElementObjectRemoveTest, DifferentConst)
+{
+ json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
+ json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
+ EXPECT_THROW_MSG(jobject.erase(jobject2.cbegin()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(jobject.erase(jobject.cbegin(), jobject2.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(jobject.erase(jobject2.cbegin(), jobject.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+ EXPECT_THROW_MSG(jobject.erase(jobject2.cbegin(), jobject2.cend()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.203] iterators do not fit current value");
+}
+
+// remove element by key in non-object type
+TEST(JsonElementNonObjectKeyRemoveTest, Null)
+{
+ json j_nonobject(json::value_t::null);
+ EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with null");
+}
+
+TEST(JsonElementNonObjectKeyRemoveTest, Boolean)
+{
+ json j_nonobject(json::value_t::boolean);
+ EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with boolean");
+}
+
+TEST(JsonElementNonObjectKeyRemoveTest, String)
+{
+ json j_nonobject(json::value_t::string);
+ EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with string");
+}
+
+TEST(JsonElementNonObjectKeyRemoveTest, Array)
+{
+ json j_nonobject(json::value_t::array);
+ EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with array");
+}
+
+TEST(JsonElementNonObjectKeyRemoveTest, Integer)
+{
+ json j_nonobject(json::value_t::number_integer);
+ EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with number");
+}
+
+TEST(JsonElementNonObjectKeyRemoveTest, Float)
+{
+ json j_nonobject(json::value_t::number_float);
+ EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
+ "[json.exception.type_error.307] cannot use erase() with number");
+}
+
+TEST_F(JsonElementObjectAccessTest, FindExist)
+{
+ for (auto key :
+ {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array"
+ })
+ {
+ EXPECT_NE(j.find(key), j.end());
+ EXPECT_EQ(*j.find(key), j.at(key));
+ EXPECT_NE(j_const.find(key), j_const.end());
+ EXPECT_EQ(*j_const.find(key), j_const.at(key));
+ }
+}
+
+TEST_F(JsonElementObjectAccessTest, FindNotExist)
+{
+ EXPECT_EQ(j.find("foo"), j.end());
+ EXPECT_EQ(j_const.find("foo"), j_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, Null)
+{
+ json j_nonarray(json::value_t::null);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, String)
+{
+ json j_nonarray(json::value_t::string);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, Object)
+{
+ json j_nonarray(json::value_t::object);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, Array)
+{
+ json j_nonarray(json::value_t::array);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, Boolean)
+{
+ json j_nonarray(json::value_t::boolean);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, Integer)
+{
+ json j_nonarray(json::value_t::number_integer);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, Unsigned)
+{
+ json j_nonarray(json::value_t::number_unsigned);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST(JsonElementNonObjectFindAccessTest, Float)
+{
+ json j_nonarray(json::value_t::number_float);
+ const json j_nonarray_const(j_nonarray);
+ EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
+ EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
+}
+
+TEST_F(JsonElementObjectAccessTest, CountExist)
+{
+ for (auto key :
+ {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array"
+ })
+ {
+ EXPECT_EQ(j.count(key), 1u);
+ EXPECT_EQ(j_const.count(key), 1u);
+ }
+}
+
+TEST_F(JsonElementObjectAccessTest, CountNotExist)
+{
+ EXPECT_EQ(j.count("foo"), 0u);
+ EXPECT_EQ(j_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, Null)
+{
+ json j_nonobject(json::value_t::null);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, String)
+{
+ json j_nonobject(json::value_t::string);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, Object)
+{
+ json j_nonobject(json::value_t::object);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, Array)
+{
+ json j_nonobject(json::value_t::array);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, Boolean)
+{
+ json j_nonobject(json::value_t::boolean);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, Integer)
+{
+ json j_nonobject(json::value_t::number_integer);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, Unsigned)
+{
+ json j_nonobject(json::value_t::number_unsigned);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST(JsonElementNonObjectCountAccessTest, Float)
+{
+ json j_nonobject(json::value_t::number_float);
+ const json j_nonobject_const(j_nonobject);
+ EXPECT_EQ(j_nonobject.count("foo"), 0u);
+ EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
+}
+
+TEST_F(JsonElementObjectAccessTest, PointerValueNotExist)
+{
+ EXPECT_EQ(j.value("/not/existing"_json_pointer, 2), 2);
+ EXPECT_EQ(j.value("/not/existing"_json_pointer, 2u), 2u);
+ EXPECT_EQ(j.value("/not/existing"_json_pointer, false), false);
+ EXPECT_EQ(j.value("/not/existing"_json_pointer, "bar"), "bar");
+ EXPECT_LT(std::fabs(j.value("/not/existing"_json_pointer, 12.34) - 12.34), 0.001);
+ EXPECT_EQ(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})), json({{"foo", "bar"}}));
+ EXPECT_EQ(j.value("/not/existing"_json_pointer, json({10, 100})), json({10, 100}));
+
+ EXPECT_EQ(j_const.value("/not/existing"_json_pointer, 2), 2);
+ EXPECT_EQ(j_const.value("/not/existing"_json_pointer, 2u), 2u);
+ EXPECT_EQ(j_const.value("/not/existing"_json_pointer, false), false);
+ EXPECT_EQ(j_const.value("/not/existing"_json_pointer, "bar"), "bar");
+ EXPECT_LT(std::fabs(j_const.value("/not/existing"_json_pointer, 12.34) - 12.34), 0.001);
+ EXPECT_EQ(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})), json({{"foo", "bar"}}));
+ EXPECT_EQ(j_const.value("/not/existing"_json_pointer, json({10, 100})), json({10, 100}));
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-inspection.cpp b/wpiutil/src/test/native/cpp/json/unit-inspection.cpp
new file mode 100644
index 0000000..79c63f0
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-inspection.cpp
@@ -0,0 +1,385 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonConvTypeCheckTest, Object)
+{
+ json j {{"foo", 1}, {"bar", false}};
+ EXPECT_FALSE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_FALSE(j.is_number());
+ EXPECT_FALSE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_TRUE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_FALSE(j.is_primitive());
+ EXPECT_TRUE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, Array)
+{
+ json j {"foo", 1, 1u, 42.23, false};
+ EXPECT_FALSE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_FALSE(j.is_number());
+ EXPECT_FALSE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_TRUE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_FALSE(j.is_primitive());
+ EXPECT_TRUE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, Null)
+{
+ json j(nullptr);
+ EXPECT_TRUE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_FALSE(j.is_number());
+ EXPECT_FALSE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_TRUE(j.is_primitive());
+ EXPECT_FALSE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, Boolean)
+{
+ json j(true);
+ EXPECT_FALSE(j.is_null());
+ EXPECT_TRUE(j.is_boolean());
+ EXPECT_FALSE(j.is_number());
+ EXPECT_FALSE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_TRUE(j.is_primitive());
+ EXPECT_FALSE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, String)
+{
+ json j("Hello world");
+ EXPECT_FALSE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_FALSE(j.is_number());
+ EXPECT_FALSE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_TRUE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_TRUE(j.is_primitive());
+ EXPECT_FALSE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, Integer)
+{
+ json j(42);
+ EXPECT_FALSE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_TRUE(j.is_number());
+ EXPECT_TRUE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_TRUE(j.is_primitive());
+ EXPECT_FALSE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, Unsigned)
+{
+ json j(42u);
+ EXPECT_FALSE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_TRUE(j.is_number());
+ EXPECT_TRUE(j.is_number_integer());
+ EXPECT_TRUE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_TRUE(j.is_primitive());
+ EXPECT_FALSE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, Float)
+{
+ json j(42.23);
+ EXPECT_FALSE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_TRUE(j.is_number());
+ EXPECT_FALSE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_TRUE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_FALSE(j.is_discarded());
+ EXPECT_TRUE(j.is_primitive());
+ EXPECT_FALSE(j.is_structured());
+}
+
+TEST(JsonConvTypeCheckTest, Discarded)
+{
+ json j(json::value_t::discarded);
+ EXPECT_FALSE(j.is_null());
+ EXPECT_FALSE(j.is_boolean());
+ EXPECT_FALSE(j.is_number());
+ EXPECT_FALSE(j.is_number_integer());
+ EXPECT_FALSE(j.is_number_unsigned());
+ EXPECT_FALSE(j.is_number_float());
+ EXPECT_FALSE(j.is_object());
+ EXPECT_FALSE(j.is_array());
+ EXPECT_FALSE(j.is_string());
+ EXPECT_TRUE(j.is_discarded());
+ EXPECT_FALSE(j.is_primitive());
+ EXPECT_FALSE(j.is_structured());
+}
+
+class JsonConvSerializationTest : public ::testing::Test {
+ protected:
+ json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
+};
+#if 0
+// no indent / indent=-1
+TEST_F(JsonConvSerializationTest, NoIndent)
+{
+ EXPECT_EQ(j.dump(),
+ "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}");
+
+ EXPECT_EQ(j.dump(), j.dump(-1));
+}
+
+// indent=0
+TEST_F(JsonConvSerializationTest, Indent0)
+{
+ EXPECT_EQ(j.dump(0),
+ "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}");
+}
+
+// indent=1, space='\t'
+TEST_F(JsonConvSerializationTest, Indent1)
+{
+ EXPECT_EQ(j.dump(1, '\t'),
+ "{\n\t\"array\": [\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t],\n\t\"boolean\": false,\n\t\"null\": null,\n\t\"number\": 42,\n\t\"object\": {},\n\t\"string\": \"Hello world\"\n}");
+}
+
+// indent=4
+TEST_F(JsonConvSerializationTest, Indent4)
+{
+ EXPECT_EQ(j.dump(4),
+ "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}");
+}
+#endif
+// indent=x
+TEST_F(JsonConvSerializationTest, IndentX)
+{
+ EXPECT_EQ(j.dump().size(), 94u);
+ EXPECT_EQ(j.dump(1).size(), 127u);
+ EXPECT_EQ(j.dump(2).size(), 142u);
+ EXPECT_EQ(j.dump(512).size(), 7792u);
+}
+
+// dump and floating-point numbers
+TEST_F(JsonConvSerializationTest, Float)
+{
+ auto s = json(42.23).dump();
+ EXPECT_NE(s.find("42.23"), std::string::npos);
+}
+
+// dump and small floating-point numbers
+TEST_F(JsonConvSerializationTest, SmallFloat)
+{
+ auto s = json(1.23456e-78).dump();
+ EXPECT_NE(s.find("1.23456e-78"), std::string::npos);
+}
+
+// dump and non-ASCII characters
+TEST_F(JsonConvSerializationTest, NonAscii)
+{
+ EXPECT_EQ(json("ä").dump(), "\"ä\"");
+ EXPECT_EQ(json("Ö").dump(), "\"Ö\"");
+ EXPECT_EQ(json("❤️").dump(), "\"❤️\"");
+}
+
+// serialization of discarded element
+TEST_F(JsonConvSerializationTest, Discarded)
+{
+ json j_discarded(json::value_t::discarded);
+ EXPECT_EQ(j_discarded.dump(), "<discarded>");
+}
+
+TEST(JsonConvRoundTripTest, Case)
+{
+ for (const auto& s :
+{"3.141592653589793", "1000000000000000010E5"
+})
+ {
+ SCOPED_TRACE(s);
+ json j1 = json::parse(s);
+ std::string s1 = j1.dump();
+ json j2 = json::parse(s1);
+ std::string s2 = j2.dump();
+ EXPECT_EQ(s1, s2);
+ }
+}
+
+// return the type of the object (explicit)
+TEST(JsonConvTypeExplicitTest, Null)
+{
+ json j = nullptr;
+ EXPECT_EQ(j.type(), json::value_t::null);
+}
+
+TEST(JsonConvTypeExplicitTest, Object)
+{
+ json j = {{"foo", "bar"}};
+ EXPECT_EQ(j.type(), json::value_t::object);
+}
+
+TEST(JsonConvTypeExplicitTest, Array)
+{
+ json j = {1, 2, 3, 4};
+ EXPECT_EQ(j.type(), json::value_t::array);
+}
+
+TEST(JsonConvTypeExplicitTest, Boolean)
+{
+ json j = true;
+ EXPECT_EQ(j.type(), json::value_t::boolean);
+}
+
+TEST(JsonConvTypeExplicitTest, String)
+{
+ json j = "Hello world";
+ EXPECT_EQ(j.type(), json::value_t::string);
+}
+
+TEST(JsonConvTypeExplicitTest, Integer)
+{
+ json j = 23;
+ EXPECT_EQ(j.type(), json::value_t::number_integer);
+}
+
+TEST(JsonConvTypeExplicitTest, Unsigned)
+{
+ json j = 23u;
+ EXPECT_EQ(j.type(), json::value_t::number_unsigned);
+}
+
+TEST(JsonConvTypeExplicitTest, Float)
+{
+ json j = 42.23;
+ EXPECT_EQ(j.type(), json::value_t::number_float);
+}
+
+// return the type of the object (implicit)
+TEST(JsonConvTypeImplicitTest, Null)
+{
+ json j = nullptr;
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
+
+TEST(JsonConvTypeImplicitTest, Object)
+{
+ json j = {{"foo", "bar"}};
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
+
+TEST(JsonConvTypeImplicitTest, Array)
+{
+ json j = {1, 2, 3, 4};
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
+
+TEST(JsonConvTypeImplicitTest, Boolean)
+{
+ json j = true;
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
+
+TEST(JsonConvTypeImplicitTest, String)
+{
+ json j = "Hello world";
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
+
+TEST(JsonConvTypeImplicitTest, Integer)
+{
+ json j = 23;
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
+
+TEST(JsonConvTypeImplicitTest, Unsigned)
+{
+ json j = 23u;
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
+
+TEST(JsonConvTypeImplicitTest, Float)
+{
+ json j = 42.23;
+ json::value_t t = j;
+ EXPECT_EQ(t, j.type());
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-iterators1.cpp b/wpiutil/src/test/native/cpp/json/unit-iterators1.cpp
new file mode 100644
index 0000000..bae3862
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-iterators1.cpp
@@ -0,0 +1,1617 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+using wpi::JsonTest;
+
+TEST(JsonIteratorBasicTest, Uninitialized)
+{
+ json::iterator it;
+ EXPECT_EQ(JsonTest::GetObject(it), nullptr);
+
+ json::const_iterator cit;
+ EXPECT_EQ(JsonTest::GetObject(cit), nullptr);
+}
+
+class JsonIteratorBooleanTest : public ::testing::Test {
+ public:
+ JsonIteratorBooleanTest() : j_const(j) {}
+
+ protected:
+ json j = true;
+ json j_const;
+};
+
+TEST_F(JsonIteratorBooleanTest, BeginEnd)
+{
+ json::iterator it = j.begin();
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ it--;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ --it;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorBooleanTest, ConstBeginEnd)
+{
+ json::const_iterator it = j_const.begin();
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ it--;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ --it;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+}
+
+TEST_F(JsonIteratorBooleanTest, CBeginEnd)
+{
+ json::const_iterator it = j.cbegin();
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ it--;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ --it;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorBooleanTest, ConstCBeginEnd)
+{
+ json::const_iterator it = j_const.cbegin();
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ it--;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ --it;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+}
+#if 0
+TEST_F(JsonIteratorBooleanTest, RBeginEnd)
+{
+ json::reverse_iterator it = j.rbegin();
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ it--;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ --it;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorBooleanTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it = j.crbegin();
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ it--;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ --it;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorBooleanTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it = j_const.crbegin();
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ it--;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ --it;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+}
+#endif
+TEST_F(JsonIteratorBooleanTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(it.value(), json(true));
+ EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(cit.value(), json(true));
+#if 0
+ auto rit = j.rend();
+ auto crit = j.crend();
+ EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+#endif
+}
+
+class JsonIteratorStringTest : public ::testing::Test {
+ public:
+ JsonIteratorStringTest() : j_const(j) {}
+
+ protected:
+ json j = "hello world";
+ json j_const;
+};
+
+TEST_F(JsonIteratorStringTest, BeginEnd)
+{
+ json::iterator it = j.begin();
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ it--;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ --it;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorStringTest, ConstBeginEnd)
+{
+ json::const_iterator it = j_const.begin();
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ it--;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ --it;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+}
+
+TEST_F(JsonIteratorStringTest, CBeginEnd)
+{
+ json::const_iterator it = j.cbegin();
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ it--;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ --it;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorStringTest, ConstCBeginEnd)
+{
+ json::const_iterator it = j_const.cbegin();
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ it--;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ --it;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+}
+#if 0
+TEST_F(JsonIteratorStringTest, RBeginEnd)
+{
+ json::reverse_iterator it = j.rbegin();
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ it--;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ --it;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorStringTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it = j.crbegin();
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ it--;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ --it;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorStringTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it = j_const.crbegin();
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ it--;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ --it;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+}
+#endif
+TEST_F(JsonIteratorStringTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(it.value(), json("hello world"));
+ EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(cit.value(), json("hello world"));
+#if 0
+ auto rit = j.rend();
+ auto crit = j.crend();
+ EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+#endif
+}
+
+class JsonIteratorArrayTest : public ::testing::Test {
+ public:
+ JsonIteratorArrayTest() : j_const(j) {}
+
+ protected:
+ json j = {1, 2, 3};
+ json j_const;
+};
+
+TEST_F(JsonIteratorArrayTest, BeginEnd)
+{
+ json::iterator it_begin = j.begin();
+ json::iterator it_end = j.end();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[0]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[1]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[2]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorArrayTest, ConstBeginEnd)
+{
+ json::const_iterator it_begin = j_const.begin();
+ json::const_iterator it_end = j_const.end();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const[0]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const[1]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const[2]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorArrayTest, CBeginEnd)
+{
+ json::const_iterator it_begin = j.cbegin();
+ json::const_iterator it_end = j.cend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[0]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[1]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[2]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorArrayTest, ConstCBeginEnd)
+{
+ json::const_iterator it_begin = j_const.cbegin();
+ json::const_iterator it_end = j_const.cend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[0]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[1]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[2]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+#if 0
+TEST_F(JsonIteratorArrayTest, RBeginEnd)
+{
+ json::reverse_iterator it_begin = j.rbegin();
+ json::reverse_iterator it_end = j.rend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[2]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[1]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[0]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorArrayTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it_begin = j.crbegin();
+ json::const_reverse_iterator it_end = j.crend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[2]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[1]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[0]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorArrayTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it_begin = j_const.crbegin();
+ json::const_reverse_iterator it_end = j_const.crend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[2]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[1]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j[0]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+#endif
+TEST_F(JsonIteratorArrayTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(it.value(), json(1));
+ EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(cit.value(), json(1));
+}
+
+class JsonIteratorObjectTest : public ::testing::Test {
+ public:
+ JsonIteratorObjectTest() : j_const(j) {}
+
+ protected:
+ json j = {{"A", 1}, {"B", 2}, {"C", 3}};
+ json j_const;
+};
+
+TEST_F(JsonIteratorObjectTest, BeginEnd)
+{
+ json::iterator it_begin = j.begin();
+ json::iterator it_end = j.end();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["A"]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["B"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["C"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorObjectTest, ConstBeginEnd)
+{
+ json::const_iterator it_begin = j_const.begin();
+ json::const_iterator it_end = j_const.end();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const["A"]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const["B"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const["C"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorObjectTest, CBeginEnd)
+{
+ json::const_iterator it_begin = j.cbegin();
+ json::const_iterator it_end = j.cend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["A"]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["B"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["C"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorObjectTest, ConstCBeginEnd)
+{
+ json::const_iterator it_begin = j_const.cbegin();
+ json::const_iterator it_end = j_const.cend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const["A"]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const["B"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j_const["C"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+#if 0
+TEST_F(JsonIteratorObjectTest, RBeginEnd)
+{
+ json::reverse_iterator it_begin = j.rbegin();
+ json::reverse_iterator it_end = j.rend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["C"]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["B"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["A"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorObjectTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it_begin = j.crbegin();
+ json::const_reverse_iterator it_end = j.crend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["C"]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["B"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["A"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+
+TEST_F(JsonIteratorObjectTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it_begin = j_const.crbegin();
+ json::const_reverse_iterator it_end = j_const.crend();
+
+ auto it = it_begin;
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["C"]);
+
+ it++;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["B"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_NE(it, it_end);
+ EXPECT_EQ(*it, j["A"]);
+
+ ++it;
+ EXPECT_NE(it, it_begin);
+ EXPECT_EQ(it, it_end);
+}
+#endif
+
+TEST_F(JsonIteratorObjectTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_EQ(it.key(), "A");
+ EXPECT_EQ(it.value(), json(1));
+ EXPECT_EQ(cit.key(), "A");
+ EXPECT_EQ(cit.value(), json(1));
+}
+
+class JsonIteratorIntegerTest : public ::testing::Test {
+ public:
+ JsonIteratorIntegerTest() : j_const(j) {}
+
+ protected:
+ json j = 23;
+ json j_const;
+};
+
+TEST_F(JsonIteratorIntegerTest, BeginEnd)
+{
+ json::iterator it = j.begin();
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ it--;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ --it;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorIntegerTest, ConstBeginEnd)
+{
+ json::const_iterator it = j_const.begin();
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ it--;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ --it;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+}
+
+TEST_F(JsonIteratorIntegerTest, CBeginEnd)
+{
+ json::const_iterator it = j.cbegin();
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ it--;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ --it;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorIntegerTest, ConstCBeginEnd)
+{
+ json::const_iterator it = j_const.cbegin();
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ it--;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ --it;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+}
+#if 0
+TEST_F(JsonIteratorIntegerTest, RBeginEnd)
+{
+ json::reverse_iterator it = j.rbegin();
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ it--;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ --it;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorIntegerTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it = j.crbegin();
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ it--;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ --it;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorIntegerTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it = j_const.crbegin();
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ it--;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ --it;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+}
+#endif
+TEST_F(JsonIteratorIntegerTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(it.value(), json(23));
+ EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(cit.value(), json(23));
+#if 0
+ auto rit = j.rend();
+ auto crit = j.crend();
+ EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+#endif
+}
+
+class JsonIteratorUnsignedTest : public ::testing::Test {
+ public:
+ JsonIteratorUnsignedTest() : j_const(j) {}
+
+ protected:
+ json j = 23u;
+ json j_const;
+};
+
+TEST_F(JsonIteratorUnsignedTest, BeginEnd)
+{
+ json::iterator it = j.begin();
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ it--;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ --it;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorUnsignedTest, ConstBeginEnd)
+{
+ json::const_iterator it = j_const.begin();
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ it--;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ --it;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+}
+
+TEST_F(JsonIteratorUnsignedTest, CBeginEnd)
+{
+ json::const_iterator it = j.cbegin();
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ it--;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ --it;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorUnsignedTest, ConstCBeginEnd)
+{
+ json::const_iterator it = j_const.cbegin();
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ it--;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ --it;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+}
+#if 0
+TEST_F(JsonIteratorUnsignedTest, RBeginEnd)
+{
+ json::reverse_iterator it = j.rbegin();
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ it--;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ --it;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorUnsignedTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it = j.crbegin();
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ it--;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ --it;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorUnsignedTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it = j_const.crbegin();
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ it--;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ --it;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+}
+#endif
+TEST_F(JsonIteratorUnsignedTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(it.value(), json(23));
+ EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(cit.value(), json(23));
+#if 0
+ auto rit = j.rend();
+ auto crit = j.crend();
+ EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+#endif
+}
+
+class JsonIteratorFloatTest : public ::testing::Test {
+ public:
+ JsonIteratorFloatTest() : j_const(j) {}
+
+ protected:
+ json j = 23.42;
+ json j_const;
+};
+
+TEST_F(JsonIteratorFloatTest, BeginEnd)
+{
+ json::iterator it = j.begin();
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ it--;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.begin());
+ EXPECT_EQ(it, j.end());
+
+ --it;
+ EXPECT_EQ(it, j.begin());
+ EXPECT_NE(it, j.end());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorFloatTest, ConstBeginEnd)
+{
+ json::const_iterator it = j_const.begin();
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ it--;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.begin());
+ EXPECT_EQ(it, j_const.end());
+
+ --it;
+ EXPECT_EQ(it, j_const.begin());
+ EXPECT_NE(it, j_const.end());
+ EXPECT_EQ(*it, j_const);
+}
+
+TEST_F(JsonIteratorFloatTest, CBeginEnd)
+{
+ json::const_iterator it = j.cbegin();
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ it--;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.cbegin());
+ EXPECT_EQ(it, j.cend());
+
+ --it;
+ EXPECT_EQ(it, j.cbegin());
+ EXPECT_NE(it, j.cend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorFloatTest, ConstCBeginEnd)
+{
+ json::const_iterator it = j_const.cbegin();
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ it--;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.cbegin());
+ EXPECT_EQ(it, j_const.cend());
+
+ --it;
+ EXPECT_EQ(it, j_const.cbegin());
+ EXPECT_NE(it, j_const.cend());
+ EXPECT_EQ(*it, j_const);
+}
+#if 0
+TEST_F(JsonIteratorFloatTest, RBeginEnd)
+{
+ json::reverse_iterator it = j.rbegin();
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ it--;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.rbegin());
+ EXPECT_EQ(it, j.rend());
+
+ --it;
+ EXPECT_EQ(it, j.rbegin());
+ EXPECT_NE(it, j.rend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorFloatTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it = j.crbegin();
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ it++;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ it--;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+
+ ++it;
+ EXPECT_NE(it, j.crbegin());
+ EXPECT_EQ(it, j.crend());
+
+ --it;
+ EXPECT_EQ(it, j.crbegin());
+ EXPECT_NE(it, j.crend());
+ EXPECT_EQ(*it, j);
+}
+
+TEST_F(JsonIteratorFloatTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it = j_const.crbegin();
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ it++;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ it--;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+
+ ++it;
+ EXPECT_NE(it, j_const.crbegin());
+ EXPECT_EQ(it, j_const.crend());
+
+ --it;
+ EXPECT_EQ(it, j_const.crbegin());
+ EXPECT_NE(it, j_const.crend());
+ EXPECT_EQ(*it, j_const);
+}
+#endif
+TEST_F(JsonIteratorFloatTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(it.value(), json(23.42));
+ EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_EQ(cit.value(), json(23.42));
+#if 0
+ auto rit = j.rend();
+ auto crit = j.crend();
+ EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+#endif
+}
+
+class JsonIteratorNullTest : public ::testing::Test {
+ public:
+ JsonIteratorNullTest() : j_const(j) {}
+
+ protected:
+ json j = nullptr;
+ json j_const;
+};
+
+TEST_F(JsonIteratorNullTest, BeginEnd)
+{
+ json::iterator it = j.begin();
+ EXPECT_EQ(it, j.end());
+}
+
+TEST_F(JsonIteratorNullTest, ConstBeginEnd)
+{
+ json::const_iterator it_begin = j_const.begin();
+ json::const_iterator it_end = j_const.end();
+ EXPECT_EQ(it_begin, it_end);
+}
+
+TEST_F(JsonIteratorNullTest, CBeginEnd)
+{
+ json::const_iterator it_begin = j.cbegin();
+ json::const_iterator it_end = j.cend();
+ EXPECT_EQ(it_begin, it_end);
+}
+
+TEST_F(JsonIteratorNullTest, ConstCBeginEnd)
+{
+ json::const_iterator it_begin = j_const.cbegin();
+ json::const_iterator it_end = j_const.cend();
+ EXPECT_EQ(it_begin, it_end);
+}
+#if 0
+TEST_F(JsonIteratorNullTest, RBeginEnd)
+{
+ json::reverse_iterator it = j.rbegin();
+ EXPECT_EQ(it, j.rend());
+}
+
+TEST_F(JsonIteratorNullTest, CRBeginEnd)
+{
+ json::const_reverse_iterator it = j.crbegin();
+ EXPECT_EQ(it, j.crend());
+}
+
+TEST_F(JsonIteratorNullTest, ConstCRBeginEnd)
+{
+ json::const_reverse_iterator it = j_const.crbegin();
+ EXPECT_EQ(it, j_const.crend());
+}
+#endif
+TEST_F(JsonIteratorNullTest, KeyValue)
+{
+ auto it = j.begin();
+ auto cit = j_const.cbegin();
+ EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(it.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(cit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+#if 0
+ auto rit = j.rend();
+ auto crit = j.crend();
+ EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
+ EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+#endif
+}
+
+TEST(JsonIteratorConstConversionTest, Boolean)
+{
+ json j = true;
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
+
+TEST(JsonIteratorConstConversionTest, String)
+{
+ json j = "hello world";
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
+
+TEST(JsonIteratorConstConversionTest, Array)
+{
+ json j = {1, 2, 3};
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
+
+TEST(JsonIteratorConstConversionTest, Object)
+{
+ json j = {{"A", 1}, {"B", 2}, {"C", 3}};
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
+
+TEST(JsonIteratorConstConversionTest, Integer)
+{
+ json j = 23;
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
+
+TEST(JsonIteratorConstConversionTest, Unsigned)
+{
+ json j = 23u;
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
+
+TEST(JsonIteratorConstConversionTest, Float)
+{
+ json j = 23.42;
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
+
+TEST(JsonIteratorConstConversionTest, Null)
+{
+ json j = nullptr;
+ json::const_iterator it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+ it = j.begin();
+ EXPECT_EQ(it, j.cbegin());
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-iterators2.cpp b/wpiutil/src/test/native/cpp/json/unit-iterators2.cpp
new file mode 100644
index 0000000..69a4dac
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-iterators2.cpp
@@ -0,0 +1,899 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonIteratorTest, Comparisons)
+{
+ json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
+
+ for (json& j : j_values)
+ {
+ SCOPED_TRACE(j.dump());
+ auto it1 = j.begin();
+ auto it2 = j.begin();
+ auto it3 = j.begin();
+ ++it2;
+ ++it3;
+ ++it3;
+ auto it1_c = j.cbegin();
+ auto it2_c = j.cbegin();
+ auto it3_c = j.cbegin();
+ ++it2_c;
+ ++it3_c;
+ ++it3_c;
+
+ // comparison: equal
+ {
+ EXPECT_TRUE(it1 == it1);
+ EXPECT_FALSE(it1 == it2);
+ EXPECT_FALSE(it1 == it3);
+ EXPECT_FALSE(it2 == it3);
+ EXPECT_TRUE(it1_c == it1_c);
+ EXPECT_FALSE(it1_c == it2_c);
+ EXPECT_FALSE(it1_c == it3_c);
+ EXPECT_FALSE(it2_c == it3_c);
+ }
+
+ // comparison: not equal
+ {
+ // check definition
+ EXPECT_EQ( (it1 != it1), !(it1 == it1) );
+ EXPECT_EQ( (it1 != it2), !(it1 == it2) );
+ EXPECT_EQ( (it1 != it3), !(it1 == it3) );
+ EXPECT_EQ( (it2 != it3), !(it2 == it3) );
+ EXPECT_EQ( (it1_c != it1_c), !(it1_c == it1_c) );
+ EXPECT_EQ( (it1_c != it2_c), !(it1_c == it2_c) );
+ EXPECT_EQ( (it1_c != it3_c), !(it1_c == it3_c) );
+ EXPECT_EQ( (it2_c != it3_c), !(it2_c == it3_c) );
+ }
+
+ // comparison: smaller
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 < it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 < it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 < it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 < it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c < it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c < it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c < it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c < it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ EXPECT_FALSE(it1 < it1);
+ EXPECT_TRUE(it1 < it2);
+ EXPECT_TRUE(it1 < it3);
+ EXPECT_TRUE(it2 < it3);
+ EXPECT_FALSE(it1_c < it1_c);
+ EXPECT_TRUE(it1_c < it2_c);
+ EXPECT_TRUE(it1_c < it3_c);
+ EXPECT_TRUE(it2_c < it3_c);
+ }
+ }
+
+ // comparison: less than or equal
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 <= it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 <= it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 <= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 <= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c <= it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c <= it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c <= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c <= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ // check definition
+ EXPECT_EQ( (it1 <= it1), !(it1 < it1) );
+ EXPECT_EQ( (it1 <= it2), !(it2 < it1) );
+ EXPECT_EQ( (it1 <= it3), !(it3 < it1) );
+ EXPECT_EQ( (it2 <= it3), !(it3 < it2) );
+ EXPECT_EQ( (it1_c <= it1_c), !(it1_c < it1_c) );
+ EXPECT_EQ( (it1_c <= it2_c), !(it2_c < it1_c) );
+ EXPECT_EQ( (it1_c <= it3_c), !(it3_c < it1_c) );
+ EXPECT_EQ( (it2_c <= it3_c), !(it3_c < it2_c) );
+ }
+ }
+
+ // comparison: greater than
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 > it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 > it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 > it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 > it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c > it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c > it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c > it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c > it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ // check definition
+ EXPECT_EQ( (it1 > it1), (it1 < it1) );
+ EXPECT_EQ( (it1 > it2), (it2 < it1) );
+ EXPECT_EQ( (it1 > it3), (it3 < it1) );
+ EXPECT_EQ( (it2 > it3), (it3 < it2) );
+ EXPECT_EQ( (it1_c > it1_c), (it1_c < it1_c) );
+ EXPECT_EQ( (it1_c > it2_c), (it2_c < it1_c) );
+ EXPECT_EQ( (it1_c > it3_c), (it3_c < it1_c) );
+ EXPECT_EQ( (it2_c > it3_c), (it3_c < it2_c) );
+ }
+ }
+
+ // comparison: greater than or equal
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 >= it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 >= it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 >= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 >= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c >= it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c >= it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c >= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c >= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ // check definition
+ EXPECT_EQ( (it1 >= it1), !(it1 < it1) );
+ EXPECT_EQ( (it1 >= it2), !(it1 < it2) );
+ EXPECT_EQ( (it1 >= it3), !(it1 < it3) );
+ EXPECT_EQ( (it2 >= it3), !(it2 < it3) );
+ EXPECT_EQ( (it1_c >= it1_c), !(it1_c < it1_c) );
+ EXPECT_EQ( (it1_c >= it2_c), !(it1_c < it2_c) );
+ EXPECT_EQ( (it1_c >= it3_c), !(it1_c < it3_c) );
+ EXPECT_EQ( (it2_c >= it3_c), !(it2_c < it3_c) );
+ }
+ }
+ }
+
+ // check exceptions if different objects are compared
+ for (auto j : j_values)
+ {
+ for (auto k : j_values)
+ {
+ if (j != k)
+ {
+ EXPECT_THROW_MSG(j.begin() == k.begin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+ EXPECT_THROW_MSG(j.cbegin() == k.cbegin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+
+ EXPECT_THROW_MSG(j.begin() < k.begin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+ EXPECT_THROW_MSG(j.cbegin() < k.cbegin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+ }
+ }
+ }
+}
+
+class JsonIteratorArithmeticTest : public ::testing::Test {
+ protected:
+ json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
+ json j_array = {1, 2, 3, 4, 5, 6};
+ json j_null = nullptr;
+ json j_value = 42;
+};
+
+TEST_F(JsonIteratorArithmeticTest, AddSubObject)
+{
+ {
+ auto it = j_object.begin();
+ EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.cbegin();
+ EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.begin();
+ EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.cbegin();
+ EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.begin();
+ EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.cbegin();
+ EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.begin();
+ EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.cbegin();
+ EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.begin();
+ EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.cbegin();
+ EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.begin();
+ EXPECT_THROW_MSG(it - it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.cbegin();
+ EXPECT_THROW_MSG(it - it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+}
+
+TEST_F(JsonIteratorArithmeticTest, AddSubArray)
+{
+ auto it = j_array.begin();
+ it += 3;
+ EXPECT_EQ((j_array.begin() + 3), it);
+ EXPECT_EQ(json::iterator(3 + j_array.begin()), it);
+ EXPECT_EQ((it - 3), j_array.begin());
+ EXPECT_EQ((it - j_array.begin()), 3);
+ EXPECT_EQ(*it, json(4));
+ it -= 2;
+ EXPECT_EQ(*it, json(2));
+}
+
+TEST_F(JsonIteratorArithmeticTest, AddSubArrayConst)
+{
+ auto it = j_array.cbegin();
+ it += 3;
+ EXPECT_EQ((j_array.cbegin() + 3), it);
+ EXPECT_EQ(json::const_iterator(3 + j_array.cbegin()), it);
+ EXPECT_EQ((it - 3), j_array.cbegin());
+ EXPECT_EQ((it - j_array.cbegin()), 3);
+ EXPECT_EQ(*it, json(4));
+ it -= 2;
+ EXPECT_EQ(*it, json(2));
+}
+
+TEST_F(JsonIteratorArithmeticTest, AddSubNull)
+{
+ auto it = j_null.begin();
+ it += 3;
+ EXPECT_EQ((j_null.begin() + 3), it);
+ EXPECT_EQ(json::iterator(3 + j_null.begin()), it);
+ EXPECT_EQ((it - 3), j_null.begin());
+ EXPECT_EQ((it - j_null.begin()), 3);
+ EXPECT_NE(it, j_null.end());
+ it -= 3;
+ EXPECT_EQ(it, j_null.end());
+}
+
+TEST_F(JsonIteratorArithmeticTest, AddSubNullConst)
+{
+ auto it = j_null.cbegin();
+ it += 3;
+ EXPECT_EQ((j_null.cbegin() + 3), it);
+ EXPECT_EQ(json::const_iterator(3 + j_null.cbegin()), it);
+ EXPECT_EQ((it - 3), j_null.cbegin());
+ EXPECT_EQ((it - j_null.cbegin()), 3);
+ EXPECT_NE(it, j_null.cend());
+ it -= 3;
+ EXPECT_EQ(it, j_null.cend());
+}
+
+TEST_F(JsonIteratorArithmeticTest, AddSubValue)
+{
+ auto it = j_value.begin();
+ it += 3;
+ EXPECT_EQ((j_value.begin() + 3), it);
+ EXPECT_EQ(json::iterator(3 + j_value.begin()), it);
+ EXPECT_EQ((it - 3), j_value.begin());
+ EXPECT_EQ((it - j_value.begin()), 3);
+ EXPECT_NE(it, j_value.end());
+ it -= 3;
+ EXPECT_EQ(*it, json(42));
+}
+
+TEST_F(JsonIteratorArithmeticTest, AddSubValueConst)
+{
+ auto it = j_value.cbegin();
+ it += 3;
+ EXPECT_EQ((j_value.cbegin() + 3), it);
+ EXPECT_EQ(json::const_iterator(3 + j_value.cbegin()), it);
+ EXPECT_EQ((it - 3), j_value.cbegin());
+ EXPECT_EQ((it - j_value.cbegin()), 3);
+ EXPECT_NE(it, j_value.cend());
+ it -= 3;
+ EXPECT_EQ(*it, json(42));
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptObject)
+{
+ auto it = j_object.begin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptObjectConst)
+{
+ auto it = j_object.cbegin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptArray)
+{
+ auto it = j_array.begin();
+ EXPECT_EQ(it[0], json(1));
+ EXPECT_EQ(it[1], json(2));
+ EXPECT_EQ(it[2], json(3));
+ EXPECT_EQ(it[3], json(4));
+ EXPECT_EQ(it[4], json(5));
+ EXPECT_EQ(it[5], json(6));
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptArrayConst)
+{
+ auto it = j_array.cbegin();
+ EXPECT_EQ(it[0], json(1));
+ EXPECT_EQ(it[1], json(2));
+ EXPECT_EQ(it[2], json(3));
+ EXPECT_EQ(it[3], json(4));
+ EXPECT_EQ(it[4], json(5));
+ EXPECT_EQ(it[5], json(6));
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptNull)
+{
+ auto it = j_null.begin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptNullConst)
+{
+ auto it = j_null.cbegin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptValue)
+{
+ auto it = j_value.begin();
+ EXPECT_EQ(it[0], json(42));
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST_F(JsonIteratorArithmeticTest, SubscriptValueConst)
+{
+ auto it = j_value.cbegin();
+ EXPECT_EQ(it[0], json(42));
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+#if 0
+TEST(JsonReverseIteratorTest, Comparisons)
+{
+ json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
+
+ for (json& j : j_values)
+ {
+ SCOPED_TRACE(j.dump());
+ auto it1 = j.rbegin();
+ auto it2 = j.rbegin();
+ auto it3 = j.rbegin();
+ ++it2;
+ ++it3;
+ ++it3;
+ auto it1_c = j.crbegin();
+ auto it2_c = j.crbegin();
+ auto it3_c = j.crbegin();
+ ++it2_c;
+ ++it3_c;
+ ++it3_c;
+
+ // comparison: equal
+ {
+ EXPECT_TRUE(it1 == it1);
+ EXPECT_FALSE(it1 == it2);
+ EXPECT_FALSE(it1 == it3);
+ EXPECT_FALSE(it2 == it3);
+ EXPECT_TRUE(it1_c == it1_c);
+ EXPECT_FALSE(it1_c == it2_c);
+ EXPECT_FALSE(it1_c == it3_c);
+ EXPECT_FALSE(it2_c == it3_c);
+ }
+
+ // comparison: not equal
+ {
+ // check definition
+ EXPECT_EQ( (it1 != it1), !(it1 == it1) );
+ EXPECT_EQ( (it1 != it2), !(it1 == it2) );
+ EXPECT_EQ( (it1 != it3), !(it1 == it3) );
+ EXPECT_EQ( (it2 != it3), !(it2 == it3) );
+ EXPECT_EQ( (it1_c != it1_c), !(it1_c == it1_c) );
+ EXPECT_EQ( (it1_c != it2_c), !(it1_c == it2_c) );
+ EXPECT_EQ( (it1_c != it3_c), !(it1_c == it3_c) );
+ EXPECT_EQ( (it2_c != it3_c), !(it2_c == it3_c) );
+ }
+
+ // comparison: smaller
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 < it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 < it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 < it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 < it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c < it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c < it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c < it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c < it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ EXPECT_FALSE(it1 < it1);
+ EXPECT_TRUE(it1 < it2);
+ EXPECT_TRUE(it1 < it3);
+ EXPECT_TRUE(it2 < it3);
+ EXPECT_FALSE(it1_c < it1_c);
+ EXPECT_TRUE(it1_c < it2_c);
+ EXPECT_TRUE(it1_c < it3_c);
+ EXPECT_TRUE(it2_c < it3_c);
+ }
+ }
+
+ // comparison: less than or equal
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 <= it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 <= it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 <= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 <= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c <= it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c <= it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c <= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c <= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ // check definition
+ EXPECT_EQ( (it1 <= it1), !(it1 < it1) );
+ EXPECT_EQ( (it1 <= it2), !(it2 < it1) );
+ EXPECT_EQ( (it1 <= it3), !(it3 < it1) );
+ EXPECT_EQ( (it2 <= it3), !(it3 < it2) );
+ EXPECT_EQ( (it1_c <= it1_c), !(it1_c < it1_c) );
+ EXPECT_EQ( (it1_c <= it2_c), !(it2_c < it1_c) );
+ EXPECT_EQ( (it1_c <= it3_c), !(it3_c < it1_c) );
+ EXPECT_EQ( (it2_c <= it3_c), !(it3_c < it2_c) );
+ }
+ }
+
+ // comparison: greater than
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 > it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 > it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 > it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 > it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c > it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c > it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c > it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c > it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ // check definition
+ EXPECT_EQ( (it1 > it1), (it1 < it1) );
+ EXPECT_EQ( (it1 > it2), (it2 < it1) );
+ EXPECT_EQ( (it1 > it3), (it3 < it1) );
+ EXPECT_EQ( (it2 > it3), (it3 < it2) );
+ EXPECT_EQ( (it1_c > it1_c), (it1_c < it1_c) );
+ EXPECT_EQ( (it1_c > it2_c), (it2_c < it1_c) );
+ EXPECT_EQ( (it1_c > it3_c), (it3_c < it1_c) );
+ EXPECT_EQ( (it2_c > it3_c), (it3_c < it2_c) );
+ }
+ }
+
+ // comparison: greater than or equal
+ {
+ if (j.type() == json::value_t::object)
+ {
+ EXPECT_THROW_MSG(it1 >= it1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 >= it2, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2 >= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1 >= it3, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c >= it1_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c >= it2_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it2_c >= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ EXPECT_THROW_MSG(it1_c >= it3_c, json::invalid_iterator,
+ "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
+ }
+ else
+ {
+ // check definition
+ EXPECT_EQ( (it1 >= it1), !(it1 < it1) );
+ EXPECT_EQ( (it1 >= it2), !(it1 < it2) );
+ EXPECT_EQ( (it1 >= it3), !(it1 < it3) );
+ EXPECT_EQ( (it2 >= it3), !(it2 < it3) );
+ EXPECT_EQ( (it1_c >= it1_c), !(it1_c < it1_c) );
+ EXPECT_EQ( (it1_c >= it2_c), !(it1_c < it2_c) );
+ EXPECT_EQ( (it1_c >= it3_c), !(it1_c < it3_c) );
+ EXPECT_EQ( (it2_c >= it3_c), !(it2_c < it3_c) );
+ }
+ }
+ }
+
+ // check exceptions if different objects are compared
+ for (auto j : j_values)
+ {
+ for (auto k : j_values)
+ {
+ if (j != k)
+ {
+ EXPECT_THROW_MSG(j.rbegin() == k.rbegin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+ EXPECT_THROW_MSG(j.crbegin() == k.crbegin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+
+ EXPECT_THROW_MSG(j.rbegin() < k.rbegin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+ EXPECT_THROW_MSG(j.crbegin() < k.crbegin(), json::invalid_iterator,
+ "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
+ }
+ }
+ }
+}
+
+class JsonReverseIteratorArithmeticTest : public ::testing::Test {
+ protected:
+ json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
+ json j_array = {1, 2, 3, 4, 5, 6};
+ json j_null = nullptr;
+ json j_value = 42;
+};
+
+TEST_F(JsonReverseIteratorArithmeticTest, AddSubObject)
+{
+ {
+ auto it = j_object.rbegin();
+ EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.crbegin();
+ EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.rbegin();
+ EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.crbegin();
+ EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.rbegin();
+ EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.crbegin();
+ EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.rbegin();
+ EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.crbegin();
+ EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.rbegin();
+ EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.crbegin();
+ EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.rbegin();
+ EXPECT_THROW_MSG(it - it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+ {
+ auto it = j_object.crbegin();
+ EXPECT_THROW_MSG(it - it, json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ }
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, AddSubArray)
+{
+ auto it = j_array.rbegin();
+ it += 3;
+ EXPECT_EQ((j_array.rbegin() + 3), it);
+ EXPECT_EQ(json::reverse_iterator(3 + j_array.rbegin()), it);
+ EXPECT_EQ((it - 3), j_array.rbegin());
+ EXPECT_EQ((it - j_array.rbegin()), 3);
+ EXPECT_EQ(*it, json(3));
+ it -= 2;
+ EXPECT_EQ(*it, json(5));
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, AddSubArrayConst)
+{
+ auto it = j_array.crbegin();
+ it += 3;
+ EXPECT_EQ((j_array.crbegin() + 3), it);
+ EXPECT_EQ(json::const_reverse_iterator(3 + j_array.crbegin()), it);
+ EXPECT_EQ((it - 3), j_array.crbegin());
+ EXPECT_EQ((it - j_array.crbegin()), 3);
+ EXPECT_EQ(*it, json(3));
+ it -= 2;
+ EXPECT_EQ(*it, json(5));
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, AddSubNull)
+{
+ auto it = j_null.rbegin();
+ it += 3;
+ EXPECT_EQ((j_null.rbegin() + 3), it);
+ EXPECT_EQ(json::reverse_iterator(3 + j_null.rbegin()), it);
+ EXPECT_EQ((it - 3), j_null.rbegin());
+ EXPECT_EQ((it - j_null.rbegin()), 3);
+ EXPECT_NE(it, j_null.rend());
+ it -= 3;
+ EXPECT_EQ(it, j_null.rend());
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, AddSubNullConst)
+{
+ auto it = j_null.crbegin();
+ it += 3;
+ EXPECT_EQ((j_null.crbegin() + 3), it);
+ EXPECT_EQ(json::const_reverse_iterator(3 + j_null.crbegin()), it);
+ EXPECT_EQ((it - 3), j_null.crbegin());
+ EXPECT_EQ((it - j_null.crbegin()), 3);
+ EXPECT_NE(it, j_null.crend());
+ it -= 3;
+ EXPECT_EQ(it, j_null.crend());
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, AddSubValue)
+{
+ auto it = j_value.rbegin();
+ it += 3;
+ EXPECT_EQ((j_value.rbegin() + 3), it);
+ EXPECT_EQ(json::reverse_iterator(3 + j_value.rbegin()), it);
+ EXPECT_EQ((it - 3), j_value.rbegin());
+ EXPECT_EQ((it - j_value.rbegin()), 3);
+ EXPECT_NE(it, j_value.rend());
+ it -= 3;
+ EXPECT_EQ(*it, json(42));
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, AddSubValueConst)
+{
+ auto it = j_value.crbegin();
+ it += 3;
+ EXPECT_EQ((j_value.crbegin() + 3), it);
+ EXPECT_EQ(json::const_reverse_iterator(3 + j_value.crbegin()), it);
+ EXPECT_EQ((it - 3), j_value.crbegin());
+ EXPECT_EQ((it - j_value.crbegin()), 3);
+ EXPECT_NE(it, j_value.crend());
+ it -= 3;
+ EXPECT_EQ(*it, json(42));
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptObject)
+{
+ auto it = j_object.rbegin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptObjectConst)
+{
+ auto it = j_object.crbegin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptArray)
+{
+ auto it = j_array.rbegin();
+ EXPECT_EQ(it[0], json(6));
+ EXPECT_EQ(it[1], json(5));
+ EXPECT_EQ(it[2], json(4));
+ EXPECT_EQ(it[3], json(3));
+ EXPECT_EQ(it[4], json(2));
+ EXPECT_EQ(it[5], json(1));
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptArrayConst)
+{
+ auto it = j_array.crbegin();
+ EXPECT_EQ(it[0], json(6));
+ EXPECT_EQ(it[1], json(5));
+ EXPECT_EQ(it[2], json(4));
+ EXPECT_EQ(it[3], json(3));
+ EXPECT_EQ(it[4], json(2));
+ EXPECT_EQ(it[5], json(1));
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptNull)
+{
+ auto it = j_null.rbegin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptNullConst)
+{
+ auto it = j_null.crbegin();
+ EXPECT_THROW_MSG(it[0], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptValue)
+{
+ auto it = j_value.rbegin();
+ EXPECT_EQ(it[0], json(42));
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+
+TEST_F(JsonReverseIteratorArithmeticTest, SubscriptValueConst)
+{
+ auto it = j_value.crbegin();
+ EXPECT_EQ(it[0], json(42));
+ EXPECT_THROW_MSG(it[1], json::invalid_iterator,
+ "[json.exception.invalid_iterator.214] cannot get value");
+}
+#endif
diff --git a/wpiutil/src/test/native/cpp/json/unit-json.h b/wpiutil/src/test/native/cpp/json/unit-json.h
new file mode 100644
index 0000000..5a764b7
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-json.h
@@ -0,0 +1,88 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/#ifndef UNIT_JSON_H_
+#define UNIT_JSON_H_
+
+#include <ostream>
+
+#include "wpi/json.h"
+
+namespace wpi {
+
+inline
+void PrintTo(const json& j, std::ostream* os) {
+ *os << j.dump();
+}
+
+class JsonTest {
+ public:
+ static const json::json_value& GetValue(const json& j) {
+ return j.m_value;
+ }
+ static json::pointer GetObject(json::iterator it) {
+ return it.m_object;
+ }
+ static json::const_pointer GetObject(json::const_iterator it) {
+ return it.m_object;
+ }
+ static std::string pop_back(json::json_pointer& p) {
+ return p.pop_back();
+ }
+ static json::json_pointer top(const json::json_pointer& p) {
+ return p.top();
+ }
+};
+
+} // namespace wpi
+
+// clang warns on TEST_THROW_MSG(x == y, ...) saying the result is unused.
+// suppress this warning.
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Wunused-comparison"
+#endif
+
+// variant of GTEST_TEST_THROW_ that also checks the exception's message.
+#define TEST_THROW_MSG(statement, expected_exception, expected_msg, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::ConstCharPtr gtest_msg = "") { \
+ bool gtest_caught_expected = false; \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } \
+ catch (expected_exception const& gtest_ex) { \
+ gtest_caught_expected = true; \
+ if (::std::string(gtest_ex.what()) != expected_msg) { \
+ ::testing::AssertionResult gtest_ar = ::testing::AssertionFailure(); \
+ gtest_ar \
+ << "Expected: " #statement " throws an exception with message \"" \
+ << expected_msg "\".\n Actual: it throws message \"" \
+ << gtest_ex.what() << "\"."; \
+ fail(gtest_ar.failure_message()); \
+ } \
+ } \
+ catch (...) { \
+ gtest_msg.value = \
+ "Expected: " #statement " throws an exception of type " \
+ #expected_exception ".\n Actual: it throws a different type."; \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+ } \
+ if (!gtest_caught_expected) { \
+ gtest_msg.value = \
+ "Expected: " #statement " throws an exception of type " \
+ #expected_exception ".\n Actual: it throws nothing."; \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
+ fail(gtest_msg.value)
+
+#define EXPECT_THROW_MSG(statement, expected_exception, expected_msg) \
+ TEST_THROW_MSG(statement, expected_exception, expected_msg, GTEST_NONFATAL_FAILURE_)
+
+#define ASSERT_THROW_MSG(statement, expected_exception, expected_msg) \
+ TEST_THROW_MSG(statement, expected_exception, expected_msg, GTEST_FATAL_FAILURE_)
+
+#endif
diff --git a/wpiutil/src/test/native/cpp/json/unit-json_pointer.cpp b/wpiutil/src/test/native/cpp/json/unit-json_pointer.cpp
new file mode 100644
index 0000000..d54fd6a
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-json_pointer.cpp
@@ -0,0 +1,402 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+using wpi::JsonTest;
+
+TEST(JsonPointerTest, Errors)
+{
+ EXPECT_THROW_MSG(json::json_pointer("foo"), json::parse_error,
+ "[json.exception.parse_error.107] parse error at 1: JSON pointer must be empty or begin with '/' - was: 'foo'");
+
+ EXPECT_THROW_MSG(json::json_pointer("/~~"), json::parse_error,
+ "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'");
+
+ EXPECT_THROW_MSG(json::json_pointer("/~"), json::parse_error,
+ "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'");
+
+ json::json_pointer p;
+ EXPECT_THROW_MSG(JsonTest::top(p), json::out_of_range,
+ "[json.exception.out_of_range.405] JSON pointer has no parent");
+ EXPECT_THROW_MSG(JsonTest::pop_back(p), json::out_of_range,
+ "[json.exception.out_of_range.405] JSON pointer has no parent");
+}
+
+// examples from RFC 6901
+TEST(JsonPointerTest, AccessNonConst)
+{
+ json j = R"(
+ {
+ "foo": ["bar", "baz"],
+ "": 0,
+ "a/b": 1,
+ "c%d": 2,
+ "e^f": 3,
+ "g|h": 4,
+ "i\\j": 5,
+ "k\"l": 6,
+ " ": 7,
+ "m~n": 8
+ }
+ )"_json;
+
+ // the whole document
+ EXPECT_EQ(j[json::json_pointer()], j);
+ EXPECT_EQ(j[json::json_pointer("")], j);
+
+ // array access
+ EXPECT_EQ(j[json::json_pointer("/foo")], j["foo"]);
+ EXPECT_EQ(j[json::json_pointer("/foo/0")], j["foo"][0]);
+ EXPECT_EQ(j[json::json_pointer("/foo/1")], j["foo"][1]);
+ EXPECT_EQ(j["/foo/1"_json_pointer], j["foo"][1]);
+
+ // checked array access
+ EXPECT_EQ(j.at(json::json_pointer("/foo/0")), j["foo"][0]);
+ EXPECT_EQ(j.at(json::json_pointer("/foo/1")), j["foo"][1]);
+
+ // empty string access
+ EXPECT_EQ(j[json::json_pointer("/")], j[""]);
+
+ // other cases
+ EXPECT_EQ(j[json::json_pointer("/ ")], j[" "]);
+ EXPECT_EQ(j[json::json_pointer("/c%d")], j["c%d"]);
+ EXPECT_EQ(j[json::json_pointer("/e^f")], j["e^f"]);
+ EXPECT_EQ(j[json::json_pointer("/g|h")], j["g|h"]);
+ EXPECT_EQ(j[json::json_pointer("/i\\j")], j["i\\j"]);
+ EXPECT_EQ(j[json::json_pointer("/k\"l")], j["k\"l"]);
+
+ // checked access
+ EXPECT_EQ(j.at(json::json_pointer("/ ")), j[" "]);
+ EXPECT_EQ(j.at(json::json_pointer("/c%d")), j["c%d"]);
+ EXPECT_EQ(j.at(json::json_pointer("/e^f")), j["e^f"]);
+ EXPECT_EQ(j.at(json::json_pointer("/g|h")), j["g|h"]);
+ EXPECT_EQ(j.at(json::json_pointer("/i\\j")), j["i\\j"]);
+ EXPECT_EQ(j.at(json::json_pointer("/k\"l")), j["k\"l"]);
+
+ // escaped access
+ EXPECT_EQ(j[json::json_pointer("/a~1b")], j["a/b"]);
+ EXPECT_EQ(j[json::json_pointer("/m~0n")], j["m~n"]);
+
+ // unescaped access
+ // access to nonexisting values yield object creation
+ EXPECT_NO_THROW(j[json::json_pointer("/a/b")] = 42);
+ EXPECT_EQ(j["a"]["b"], json(42));
+ EXPECT_NO_THROW(j[json::json_pointer("/a/c/1")] = 42);
+ EXPECT_EQ(j["a"]["c"], json({nullptr, 42}));
+ EXPECT_NO_THROW(j[json::json_pointer("/a/d/-")] = 42);
+ EXPECT_EQ(j["a"]["d"], json::array({42}));
+ // "/a/b" works for JSON {"a": {"b": 42}}
+ EXPECT_EQ(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")], json(42));
+
+ // unresolved access
+ json j_primitive = 1;
+ EXPECT_THROW_MSG(j_primitive["/foo"_json_pointer], json::out_of_range,
+ "[json.exception.out_of_range.404] unresolved reference token 'foo'");
+ EXPECT_THROW_MSG(j_primitive.at("/foo"_json_pointer), json::out_of_range,
+ "[json.exception.out_of_range.404] unresolved reference token 'foo'");
+}
+
+TEST(JsonPointerTest, AccessConst)
+{
+ const json j = R"(
+ {
+ "foo": ["bar", "baz"],
+ "": 0,
+ "a/b": 1,
+ "c%d": 2,
+ "e^f": 3,
+ "g|h": 4,
+ "i\\j": 5,
+ "k\"l": 6,
+ " ": 7,
+ "m~n": 8
+ }
+ )"_json;
+
+ // the whole document
+ EXPECT_EQ(j[json::json_pointer()], j);
+ EXPECT_EQ(j[json::json_pointer("")], j);
+
+ // array access
+ EXPECT_EQ(j[json::json_pointer("/foo")], j["foo"]);
+ EXPECT_EQ(j[json::json_pointer("/foo/0")], j["foo"][0]);
+ EXPECT_EQ(j[json::json_pointer("/foo/1")], j["foo"][1]);
+ EXPECT_EQ(j["/foo/1"_json_pointer], j["foo"][1]);
+
+ // checked array access
+ EXPECT_EQ(j.at(json::json_pointer("/foo/0")), j["foo"][0]);
+ EXPECT_EQ(j.at(json::json_pointer("/foo/1")), j["foo"][1]);
+
+ // empty string access
+ EXPECT_EQ(j[json::json_pointer("/")], j[""]);
+
+ // other cases
+ EXPECT_EQ(j[json::json_pointer("/ ")], j[" "]);
+ EXPECT_EQ(j[json::json_pointer("/c%d")], j["c%d"]);
+ EXPECT_EQ(j[json::json_pointer("/e^f")], j["e^f"]);
+ EXPECT_EQ(j[json::json_pointer("/g|h")], j["g|h"]);
+ EXPECT_EQ(j[json::json_pointer("/i\\j")], j["i\\j"]);
+ EXPECT_EQ(j[json::json_pointer("/k\"l")], j["k\"l"]);
+
+ // checked access
+ EXPECT_EQ(j.at(json::json_pointer("/ ")), j[" "]);
+ EXPECT_EQ(j.at(json::json_pointer("/c%d")), j["c%d"]);
+ EXPECT_EQ(j.at(json::json_pointer("/e^f")), j["e^f"]);
+ EXPECT_EQ(j.at(json::json_pointer("/g|h")), j["g|h"]);
+ EXPECT_EQ(j.at(json::json_pointer("/i\\j")), j["i\\j"]);
+ EXPECT_EQ(j.at(json::json_pointer("/k\"l")), j["k\"l"]);
+
+ // escaped access
+ EXPECT_EQ(j[json::json_pointer("/a~1b")], j["a/b"]);
+ EXPECT_EQ(j[json::json_pointer("/m~0n")], j["m~n"]);
+
+ // unescaped access
+ EXPECT_THROW_MSG(j.at(json::json_pointer("/a/b")), json::out_of_range,
+ "[json.exception.out_of_range.403] key 'a' not found");
+
+ // unresolved access
+ const json j_primitive = 1;
+ EXPECT_THROW_MSG(j_primitive["/foo"_json_pointer], json::out_of_range,
+ "[json.exception.out_of_range.404] unresolved reference token 'foo'");
+ EXPECT_THROW_MSG(j_primitive.at("/foo"_json_pointer), json::out_of_range,
+ "[json.exception.out_of_range.404] unresolved reference token 'foo'");
+}
+
+TEST(JsonPointerTest, UserStringLiteral)
+{
+ json j = R"(
+ {
+ "foo": ["bar", "baz"],
+ "": 0,
+ "a/b": 1,
+ "c%d": 2,
+ "e^f": 3,
+ "g|h": 4,
+ "i\\j": 5,
+ "k\"l": 6,
+ " ": 7,
+ "m~n": 8
+ }
+ )"_json;
+
+ // the whole document
+ EXPECT_EQ(j[""_json_pointer], j);
+
+ // array access
+ EXPECT_EQ(j["/foo"_json_pointer], j["foo"]);
+ EXPECT_EQ(j["/foo/0"_json_pointer], j["foo"][0]);
+ EXPECT_EQ(j["/foo/1"_json_pointer], j["foo"][1]);
+}
+
+TEST(JsonPointerTest, ArrayNonConst)
+{
+ json j = {1, 2, 3};
+ const json j_const = j;
+
+ // check reading access
+ EXPECT_EQ(j["/0"_json_pointer], j[0]);
+ EXPECT_EQ(j["/1"_json_pointer], j[1]);
+ EXPECT_EQ(j["/2"_json_pointer], j[2]);
+
+ // assign to existing index
+ j["/1"_json_pointer] = 13;
+ EXPECT_EQ(j[1], json(13));
+
+ // assign to nonexisting index
+ j["/3"_json_pointer] = 33;
+ EXPECT_EQ(j[3], json(33));
+
+ // assign to nonexisting index (with gap)
+ j["/5"_json_pointer] = 55;
+ EXPECT_EQ(j, json({1, 13, 3, 33, nullptr, 55}));
+
+ // error with leading 0
+ EXPECT_THROW_MSG(j["/01"_json_pointer], json::parse_error,
+ "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
+ EXPECT_THROW_MSG(j_const["/01"_json_pointer], json::parse_error,
+ "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
+ EXPECT_THROW_MSG(j.at("/01"_json_pointer), json::parse_error,
+ "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
+ EXPECT_THROW_MSG(j_const.at("/01"_json_pointer), json::parse_error,
+ "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
+
+ // error with incorrect numbers
+ EXPECT_THROW_MSG(j["/one"_json_pointer] = 1, json::parse_error,
+ "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
+ EXPECT_THROW_MSG(j_const["/one"_json_pointer] == 1, json::parse_error,
+ "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
+
+ EXPECT_THROW_MSG(j.at("/one"_json_pointer) = 1, json::parse_error,
+ "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
+ EXPECT_THROW_MSG(j_const.at("/one"_json_pointer) == 1, json::parse_error,
+ "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
+
+ EXPECT_THROW_MSG(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), json::parse_error,
+ "[json.exception.parse_error.109] parse error: array index 'three' is not a number");
+
+ // assign to "-"
+ j["/-"_json_pointer] = 99;
+ EXPECT_EQ(j, json({1, 13, 3, 33, nullptr, 55, 99}));
+
+ // error when using "-" in const object
+ EXPECT_THROW_MSG(j_const["/-"_json_pointer], json::out_of_range,
+ "[json.exception.out_of_range.402] array index '-' (3) is out of range");
+
+ // error when using "-" with at
+ EXPECT_THROW_MSG(j.at("/-"_json_pointer), json::out_of_range,
+ "[json.exception.out_of_range.402] array index '-' (7) is out of range");
+ EXPECT_THROW_MSG(j_const.at("/-"_json_pointer), json::out_of_range,
+ "[json.exception.out_of_range.402] array index '-' (3) is out of range");
+}
+
+TEST(JsonPointerTest, ArrayConst)
+{
+ const json j = {1, 2, 3};
+
+ // check reading access
+ EXPECT_EQ(j["/0"_json_pointer], j[0]);
+ EXPECT_EQ(j["/1"_json_pointer], j[1]);
+ EXPECT_EQ(j["/2"_json_pointer], j[2]);
+
+ // assign to nonexisting index
+ EXPECT_THROW_MSG(j.at("/3"_json_pointer), json::out_of_range,
+ "[json.exception.out_of_range.401] array index 3 is out of range");
+
+ // assign to nonexisting index (with gap)
+ EXPECT_THROW_MSG(j.at("/5"_json_pointer), json::out_of_range,
+ "[json.exception.out_of_range.401] array index 5 is out of range");
+
+ // assign to "-"
+ EXPECT_THROW_MSG(j["/-"_json_pointer], json::out_of_range,
+ "[json.exception.out_of_range.402] array index '-' (3) is out of range");
+ EXPECT_THROW_MSG(j.at("/-"_json_pointer), json::out_of_range,
+ "[json.exception.out_of_range.402] array index '-' (3) is out of range");
+}
+
+TEST(JsonPointerTest, Flatten)
+{
+ json j =
+ {
+ {"pi", 3.141},
+ {"happy", true},
+ {"name", "Niels"},
+ {"nothing", nullptr},
+ {
+ "answer", {
+ {"everything", 42}
+ }
+ },
+ {"list", {1, 0, 2}},
+ {
+ "object", {
+ {"currency", "USD"},
+ {"value", 42.99},
+ {"", "empty string"},
+ {"/", "slash"},
+ {"~", "tilde"},
+ {"~1", "tilde1"}
+ }
+ }
+ };
+
+ json j_flatten =
+ {
+ {"/pi", 3.141},
+ {"/happy", true},
+ {"/name", "Niels"},
+ {"/nothing", nullptr},
+ {"/answer/everything", 42},
+ {"/list/0", 1},
+ {"/list/1", 0},
+ {"/list/2", 2},
+ {"/object/currency", "USD"},
+ {"/object/value", 42.99},
+ {"/object/", "empty string"},
+ {"/object/~1", "slash"},
+ {"/object/~0", "tilde"},
+ {"/object/~01", "tilde1"}
+ };
+
+ // check if flattened result is as expected
+ EXPECT_EQ(j.flatten(), j_flatten);
+
+ // check if unflattened result is as expected
+ EXPECT_EQ(j_flatten.unflatten(), j);
+
+ // error for nonobjects
+ EXPECT_THROW_MSG(json(1).unflatten(), json::type_error,
+ "[json.exception.type_error.314] only objects can be unflattened");
+
+ // error for nonprimitve values
+ EXPECT_THROW_MSG(json({{"/1", {1, 2, 3}}}).unflatten(), json::type_error,
+ "[json.exception.type_error.315] values in object must be primitive");
+
+ // error for conflicting values
+ json j_error = {{"", 42}, {"/foo", 17}};
+ EXPECT_THROW_MSG(j_error.unflatten(), json::type_error,
+ "[json.exception.type_error.313] invalid value to unflatten");
+
+ // explicit roundtrip check
+ EXPECT_EQ(j.flatten().unflatten(), j);
+
+ // roundtrip for primitive values
+ json j_null;
+ EXPECT_EQ(j_null.flatten().unflatten(), j_null);
+ json j_number = 42;
+ EXPECT_EQ(j_number.flatten().unflatten(), j_number);
+ json j_boolean = false;
+ EXPECT_EQ(j_boolean.flatten().unflatten(), j_boolean);
+ json j_string = "foo";
+ EXPECT_EQ(j_string.flatten().unflatten(), j_string);
+
+ // roundtrip for empty structured values (will be unflattened to null)
+ json j_array(json::value_t::array);
+ EXPECT_EQ(j_array.flatten().unflatten(), json());
+ json j_object(json::value_t::object);
+ EXPECT_EQ(j_object.flatten().unflatten(), json());
+}
+
+TEST(JsonPointerTest, StringRepresentation)
+{
+ for (auto ptr :
+ {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n"
+ })
+ {
+ SCOPED_TRACE(ptr);
+ EXPECT_EQ(json::json_pointer(ptr).to_string(), ptr);
+ }
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-meta.cpp b/wpiutil/src/test/native/cpp/json/unit-meta.cpp
new file mode 100644
index 0000000..45daf8f
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-meta.cpp
@@ -0,0 +1,54 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonVersionTest, Meta)
+{
+ json j = json::meta();
+
+ EXPECT_EQ(j["name"], "WPI version of JSON for Modern C++");
+ EXPECT_EQ(j["copyright"], "(C) 2013-2017 Niels Lohmann, (C) 2017-2018 FIRST");
+ EXPECT_EQ(j["url"], "https://github.com/wpilibsuite/allwpilib");
+ EXPECT_EQ(j["version"], json(
+ {
+ {"string", "3.1.2"},
+ {"major", 3},
+ {"minor", 1},
+ {"patch", 2}
+ }));
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-modifiers.cpp b/wpiutil/src/test/native/cpp/json/unit-modifiers.cpp
new file mode 100644
index 0000000..4125858
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-modifiers.cpp
@@ -0,0 +1,739 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonClearTest, Boolean)
+{
+ json j = true;
+
+ j.clear();
+ EXPECT_EQ(j, json(json::value_t::boolean));
+}
+
+TEST(JsonClearTest, String)
+{
+ json j = "hello world";
+
+ j.clear();
+ EXPECT_EQ(j, json(json::value_t::string));
+}
+
+TEST(JsonClearTest, ArrayEmpty)
+{
+ json j = json::array();
+
+ j.clear();
+ EXPECT_TRUE(j.empty());
+ EXPECT_EQ(j, json(json::value_t::array));
+}
+
+TEST(JsonClearTest, ArrayFilled)
+{
+ json j = {1, 2, 3};
+
+ j.clear();
+ EXPECT_TRUE(j.empty());
+ EXPECT_EQ(j, json(json::value_t::array));
+}
+
+TEST(JsonClearTest, ObjectEmpty)
+{
+ json j = json::object();
+
+ j.clear();
+ EXPECT_TRUE(j.empty());
+ EXPECT_EQ(j, json(json::value_t::object));
+}
+
+TEST(JsonClearTest, ObjectFilled)
+{
+ json j = {{"one", 1}, {"two", 2}, {"three", 3}};
+
+ j.clear();
+ EXPECT_TRUE(j.empty());
+ EXPECT_EQ(j, json(json::value_t::object));
+}
+
+TEST(JsonClearTest, Integer)
+{
+ json j = 23;
+
+ j.clear();
+ EXPECT_EQ(j, json(json::value_t::number_integer));
+}
+
+TEST(JsonClearTest, Unsigned)
+{
+ json j = 23u;
+
+ j.clear();
+ EXPECT_EQ(j, json(json::value_t::number_integer));
+}
+
+TEST(JsonClearTest, Float)
+{
+ json j = 23.42;
+
+ j.clear();
+ EXPECT_EQ(j, json(json::value_t::number_float));
+}
+
+TEST(JsonClearTest, Null)
+{
+ json j = nullptr;
+
+ j.clear();
+ EXPECT_EQ(j, json(json::value_t::null));
+}
+
+TEST(JsonPushBackArrayTest, RRefNull)
+{
+ json j;
+ j.push_back(1);
+ j.push_back(2);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2}));
+}
+
+TEST(JsonPushBackArrayTest, RRefArray)
+{
+ json j = {1, 2, 3};
+ j.push_back("Hello");
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
+}
+
+TEST(JsonPushBackArrayTest, RRefOther)
+{
+ json j = 1;
+ EXPECT_THROW_MSG(j.push_back("Hello"), json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with number");
+}
+
+TEST(JsonPushBackArrayTest, LRefNull)
+{
+ json j;
+ json k(1);
+ j.push_back(k);
+ j.push_back(k);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 1}));
+}
+
+TEST(JsonPushBackArrayTest, LRefArray)
+{
+ json j = {1, 2, 3};
+ json k("Hello");
+ j.push_back(k);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
+}
+
+TEST(JsonPushBackArrayTest, LRefOther)
+{
+ json j = 1;
+ json k("Hello");
+ EXPECT_THROW_MSG(j.push_back(k), json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with number");
+}
+#if 0
+TEST(JsonPushBackObjectTest, Null)
+{
+ json j;
+ j.push_back(json::object_t::value_type({"one", 1}));
+ j.push_back(json::object_t::value_type({"two", 2}));
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j.size(), 2u);
+ EXPECT_EQ(j["one"], json(1));
+ EXPECT_EQ(j["two"], json(2));
+}
+
+TEST(JsonPushBackObjectTest, Object)
+{
+ json j(json::value_t::object);
+ j.push_back(json::object_t::value_type({"one", 1}));
+ j.push_back(json::object_t::value_type({"two", 2}));
+ EXPECT_EQ(j.size(), 2u);
+ EXPECT_EQ(j["one"], json(1));
+ EXPECT_EQ(j["two"], json(2));
+}
+
+TEST(JsonPushBackObjectTest, Other)
+{
+ json j = 1;
+ json k("Hello");
+ EXPECT_THROW_MSG(j.push_back(json::object_t::value_type({"one", 1})), json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with number");
+}
+#endif
+TEST(JsonPushBackInitListTest, Null)
+{
+ json j;
+ j.push_back({"foo", "bar"});
+ EXPECT_EQ(j, json::array({{"foo", "bar"}}));
+
+ json k;
+ k.push_back({1, 2, 3});
+ EXPECT_EQ(k, json::array({{1, 2, 3}}));
+}
+
+TEST(JsonPushBackInitListTest, Array)
+{
+ json j = {1, 2, 3};
+ j.push_back({"foo", "bar"});
+ EXPECT_EQ(j, json({1, 2, 3, {"foo", "bar"}}));
+
+ json k = {1, 2, 3};
+ k.push_back({1, 2, 3});
+ EXPECT_EQ(k, json({1, 2, 3, {1, 2, 3}}));
+}
+
+TEST(JsonPushBackInitListTest, Object)
+{
+ json j = {{"key1", 1}};
+ j.push_back({"key2", "bar"});
+ EXPECT_EQ(j, json({{"key1", 1}, {"key2", "bar"}}));
+
+ json k = {{"key1", 1}};
+ EXPECT_THROW_MSG(k.push_back({1, 2, 3, 4}), json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with object");
+}
+
+TEST(JsonEmplaceBackArrayTest, Null)
+{
+ json j;
+ j.emplace_back(1);
+ j.emplace_back(2);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2}));
+}
+
+TEST(JsonEmplaceBackArrayTest, Array)
+{
+ json j = {1, 2, 3};
+ j.emplace_back("Hello");
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
+}
+
+TEST(JsonEmplaceBackArrayTest, MultipleValues)
+{
+ json j;
+ j.emplace_back(3, "foo");
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({{"foo", "foo", "foo"}}));
+}
+
+TEST(JsonEmplaceBackArrayTest, Other)
+{
+ json j = 1;
+ EXPECT_THROW_MSG(j.emplace_back("Hello"), json::type_error,
+ "[json.exception.type_error.311] cannot use emplace_back() with number");
+}
+
+TEST(JsonEmplaceObjectTest, Null)
+{
+ // start with a null value
+ json j;
+
+ // add a new key
+ auto res1 = j.emplace("foo", "bar");
+ EXPECT_EQ(res1.second, true);
+ EXPECT_EQ(*res1.first, "bar");
+
+ // the null value is changed to an object
+ EXPECT_EQ(j.type(), json::value_t::object);
+
+ // add a new key
+ auto res2 = j.emplace("baz", "bam");
+ EXPECT_EQ(res2.second, true);
+ EXPECT_EQ(*res2.first, "bam");
+
+ // we try to insert at given key - no change
+ auto res3 = j.emplace("baz", "bad");
+ EXPECT_EQ(res3.second, false);
+ EXPECT_EQ(*res3.first, "bam");
+
+ // the final object
+ EXPECT_EQ(j, json({{"baz", "bam"}, {"foo", "bar"}}));
+}
+
+TEST(JsonEmplaceObjectTest, Object)
+{
+ // start with an object
+ json j = {{"foo", "bar"}};
+
+ // add a new key
+ auto res1 = j.emplace("baz", "bam");
+ EXPECT_EQ(res1.second, true);
+ EXPECT_EQ(*res1.first, "bam");
+
+ // add an existing key
+ auto res2 = j.emplace("foo", "bad");
+ EXPECT_EQ(res2.second, false);
+ EXPECT_EQ(*res2.first, "bar");
+
+ // check final object
+ EXPECT_EQ(j, json({{"baz", "bam"}, {"foo", "bar"}}));
+}
+
+TEST(JsonEmplaceObjectTest, Other)
+{
+ json j = 1;
+ EXPECT_THROW_MSG(j.emplace("foo", "bar"), json::type_error,
+ "[json.exception.type_error.311] cannot use emplace() with number");
+}
+
+TEST(JsonPlusEqualArrayTest, RRefNull)
+{
+ json j;
+ j += 1;
+ j += 2;
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2}));
+}
+
+TEST(JsonPlusEqualArrayTest, RRefArray)
+{
+ json j = {1, 2, 3};
+ j += "Hello";
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
+}
+
+TEST(JsonPlusEqualArrayTest, RRefOther)
+{
+ json j = 1;
+ EXPECT_THROW_MSG(j += "Hello", json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with number");
+}
+
+TEST(JsonPlusEqualArrayTest, LRefNull)
+{
+ json j;
+ json k(1);
+ j += k;
+ j += k;
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 1}));
+}
+
+TEST(JsonPlusEqualArrayTest, LRefArray)
+{
+ json j = {1, 2, 3};
+ json k("Hello");
+ j += k;
+ EXPECT_EQ(j.type(), json::value_t::array);
+ EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
+}
+
+TEST(JsonPlusEqualArrayTest, LRefOther)
+{
+ json j = 1;
+ json k("Hello");
+ EXPECT_THROW_MSG(j += k, json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with number");
+}
+#if 0
+TEST(JsonPlusEqualObjectTest, Null)
+{
+ json j;
+ j += json::object_t::value_type({"one", 1});
+ j += json::object_t::value_type({"two", 2});
+ EXPECT_EQ(j.type(), json::value_t::object);
+ EXPECT_EQ(j.size(), 2u);
+ EXPECT_EQ(j["one"], json(1));
+ EXPECT_EQ(j["two"], json(2));
+}
+
+TEST(JsonPlusEqualObjectTest, Object)
+{
+ json j(json::value_t::object);
+ j += json::object_t::value_type({"one", 1});
+ j += json::object_t::value_type({"two", 2});
+ EXPECT_EQ(j.size(), 2u);
+ EXPECT_EQ(j["one"], json(1));
+ EXPECT_EQ(j["two"], json(2));
+}
+
+TEST(JsonPlusEqualObjectTest, Other)
+{
+ json j = 1;
+ json k("Hello");
+ EXPECT_THROW_MSG(j += json::object_t::value_type({"one", 1}), json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with number");
+}
+#endif
+TEST(JsonPlusEqualInitListTest, Null)
+{
+ json j;
+ j += {"foo", "bar"};
+ EXPECT_EQ(j, json::array({{"foo", "bar"}}));
+
+ json k;
+ k += {1, 2, 3};
+ EXPECT_EQ(k, json::array({{1, 2, 3}}));
+}
+
+TEST(JsonPlusEqualInitListTest, Array)
+{
+ json j = {1, 2, 3};
+ j += {"foo", "bar"};
+ EXPECT_EQ(j, json({1, 2, 3, {"foo", "bar"}}));
+
+ json k = {1, 2, 3};
+ k += {1, 2, 3};
+ EXPECT_EQ(k, json({1, 2, 3, {1, 2, 3}}));
+}
+
+TEST(JsonPlusEqualInitListTest, Object)
+{
+ json j = {{"key1", 1}};
+ j += {"key2", "bar"};
+ EXPECT_EQ(j, json({{"key1", 1}, {"key2", "bar"}}));
+
+ json k = {{"key1", 1}};
+ EXPECT_THROW_MSG((k += {1, 2, 3, 4}), json::type_error,
+ "[json.exception.type_error.308] cannot use push_back() with object");
+}
+
+class JsonInsertTest : public ::testing::Test {
+ protected:
+ json j_array = {1, 2, 3, 4};
+ json j_value = 5;
+ json j_other_array = {"first", "second"};
+ json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
+ json j_object2 = {{"eleven", "elf"}, {"seventeen", "siebzehn"}};
+};
+
+TEST_F(JsonInsertTest, ValueBegin)
+{
+ auto it = j_array.insert(j_array.begin(), j_value);
+ EXPECT_EQ(j_array.size(), 5u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ(j_array.begin(), it);
+ EXPECT_EQ(j_array, json({5, 1, 2, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, ValueMiddle)
+{
+ auto it = j_array.insert(j_array.begin() + 2, j_value);
+ EXPECT_EQ(j_array.size(), 5u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ((it - j_array.begin()), 2);
+ EXPECT_EQ(j_array, json({1, 2, 5, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, ValueEnd)
+{
+ auto it = j_array.insert(j_array.end(), j_value);
+ EXPECT_EQ(j_array.size(), 5u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ((j_array.end() - it), 1);
+ EXPECT_EQ(j_array, json({1, 2, 3, 4, 5}));
+}
+
+TEST_F(JsonInsertTest, RvalueBegin)
+{
+ auto it = j_array.insert(j_array.begin(), 5);
+ EXPECT_EQ(j_array.size(), 5u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ(j_array.begin(), it);
+ EXPECT_EQ(j_array, json({5, 1, 2, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, RvalueMiddle)
+{
+ auto it = j_array.insert(j_array.begin() + 2, 5);
+ EXPECT_EQ(j_array.size(), 5u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ((it - j_array.begin()), 2);
+ EXPECT_EQ(j_array, json({1, 2, 5, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, RvalueEnd)
+{
+ auto it = j_array.insert(j_array.end(), 5);
+ EXPECT_EQ(j_array.size(), 5u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ((j_array.end() - it), 1);
+ EXPECT_EQ(j_array, json({1, 2, 3, 4, 5}));
+}
+
+TEST_F(JsonInsertTest, CopyBegin)
+{
+ auto it = j_array.insert(j_array.begin(), 3, 5);
+ EXPECT_EQ(j_array.size(), 7u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ(j_array.begin(), it);
+ EXPECT_EQ(j_array, json({5, 5, 5, 1, 2, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, CopyMiddle)
+{
+ auto it = j_array.insert(j_array.begin() + 2, 3, 5);
+ EXPECT_EQ(j_array.size(), 7u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ((it - j_array.begin()), 2);
+ EXPECT_EQ(j_array, json({1, 2, 5, 5, 5, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, CopyEnd)
+{
+ auto it = j_array.insert(j_array.end(), 3, 5);
+ EXPECT_EQ(j_array.size(), 7u);
+ EXPECT_EQ(*it, j_value);
+ EXPECT_EQ((j_array.end() - it), 3);
+ EXPECT_EQ(j_array, json({1, 2, 3, 4, 5, 5, 5}));
+}
+
+TEST_F(JsonInsertTest, CopyNothing)
+{
+ auto it = j_array.insert(j_array.end(), 0, 5);
+ EXPECT_EQ(j_array.size(), 4u);
+ // the returned iterator points to the first inserted element;
+ // there were 4 elements, so it should point to the 5th
+ EXPECT_EQ(it, j_array.begin() + 4);
+ EXPECT_EQ(j_array, json({1, 2, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, RangeForArrayProper)
+{
+ auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end());
+ EXPECT_EQ(j_array.size(), 6u);
+ EXPECT_EQ(*it, *j_other_array.begin());
+ EXPECT_EQ((j_array.end() - it), 2);
+ EXPECT_EQ(j_array, json({1, 2, 3, 4, "first", "second"}));
+}
+
+TEST_F(JsonInsertTest, RangeForArrayEmpty)
+{
+ auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin());
+ EXPECT_EQ(j_array.size(), 4u);
+ EXPECT_EQ(it, j_array.end());
+ EXPECT_EQ(j_array, json({1, 2, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, RangeForArrayInvalid)
+{
+ json j_other_array2 = {"first", "second"};
+
+ EXPECT_THROW_MSG(j_array.insert(j_array.end(), j_array.begin(), j_array.end()),
+ json::invalid_iterator,
+ "[json.exception.invalid_iterator.211] passed iterators may not belong to container");
+ EXPECT_THROW_MSG(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()),
+ json::invalid_iterator,
+ "[json.exception.invalid_iterator.210] iterators do not fit");
+}
+
+TEST_F(JsonInsertTest, RangeForObjectProper)
+{
+ j_object1.insert(j_object2.begin(), j_object2.end());
+ EXPECT_EQ(j_object1.size(), 4u);
+}
+
+TEST_F(JsonInsertTest, RangeForObjectEmpty)
+{
+ j_object1.insert(j_object2.begin(), j_object2.begin());
+ EXPECT_EQ(j_object1.size(), 2u);
+}
+
+TEST_F(JsonInsertTest, RangeForObjectInvalid)
+{
+ json j_other_array2 = {"first", "second"};
+
+ EXPECT_THROW_MSG(j_array.insert(j_object2.begin(), j_object2.end()), json::type_error,
+ "[json.exception.type_error.309] cannot use insert() with array");
+ EXPECT_THROW_MSG(j_object1.insert(j_object1.begin(), j_object2.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.210] iterators do not fit");
+ EXPECT_THROW_MSG(j_object1.insert(j_array.begin(), j_array.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterators first and last must point to objects");
+}
+
+TEST_F(JsonInsertTest, InitListBegin)
+{
+ auto it = j_array.insert(j_array.begin(), {7, 8, 9});
+ EXPECT_EQ(j_array.size(), 7u);
+ EXPECT_EQ(*it, json(7));
+ EXPECT_EQ(j_array.begin(), it);
+ EXPECT_EQ(j_array, json({7, 8, 9, 1, 2, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, InitListMiddle)
+{
+ auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9});
+ EXPECT_EQ(j_array.size(), 7u);
+ EXPECT_EQ(*it, json(7));
+ EXPECT_EQ((it - j_array.begin()), 2);
+ EXPECT_EQ(j_array, json({1, 2, 7, 8, 9, 3, 4}));
+}
+
+TEST_F(JsonInsertTest, InitListEnd)
+{
+ auto it = j_array.insert(j_array.end(), {7, 8, 9});
+ EXPECT_EQ(j_array.size(), 7u);
+ EXPECT_EQ(*it, json(7));
+ EXPECT_EQ((j_array.end() - it), 3);
+ EXPECT_EQ(j_array, json({1, 2, 3, 4, 7, 8, 9}));
+}
+
+TEST_F(JsonInsertTest, InvalidIterator)
+{
+ // pass iterator to a different array
+ json j_another_array = {1, 2};
+ json j_yet_another_array = {"first", "second"};
+ EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), 10), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), j_value), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), 10, 11), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+ EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), json::invalid_iterator,
+ "[json.exception.invalid_iterator.202] iterator does not fit current value");
+}
+
+TEST_F(JsonInsertTest, NonArray)
+{
+ // call insert on a non-array type
+ json j_nonarray = 3;
+ json j_yet_another_array = {"first", "second"};
+ EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), 10), json::type_error,
+ "[json.exception.type_error.309] cannot use insert() with number");
+ EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), j_value), json::type_error,
+ "[json.exception.type_error.309] cannot use insert() with number");
+ EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), 10, 11), json::type_error,
+ "[json.exception.type_error.309] cannot use insert() with number");
+ EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(),
+ j_yet_another_array.end()), json::type_error,
+ "[json.exception.type_error.309] cannot use insert() with number");
+ EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), json::type_error,
+ "[json.exception.type_error.309] cannot use insert() with number");
+}
+
+TEST(JsonSwapTest, JsonMember)
+{
+ json j("hello world");
+ json k(42.23);
+
+ j.swap(k);
+
+ EXPECT_EQ(j, json(42.23));
+ EXPECT_EQ(k, json("hello world"));
+}
+
+TEST(JsonSwapTest, JsonNonMember)
+{
+ json j("hello world");
+ json k(42.23);
+
+ std::swap(j, k);
+
+ EXPECT_EQ(j, json(42.23));
+ EXPECT_EQ(k, json("hello world"));
+}
+
+TEST(JsonSwapTest, ArrayT)
+{
+ json j = {1, 2, 3, 4};
+ json::array_t a = {"foo", "bar", "baz"};
+
+ j.swap(a);
+
+ EXPECT_EQ(j, json({"foo", "bar", "baz"}));
+
+ j.swap(a);
+
+ EXPECT_EQ(j, json({1, 2, 3, 4}));
+}
+
+TEST(JsonSwapTest, NonArrayT)
+{
+ json j = 17;
+ json::array_t a = {"foo", "bar", "baz"};
+
+ EXPECT_THROW_MSG(j.swap(a), json::type_error,
+ "[json.exception.type_error.310] cannot use swap() with number");
+}
+
+TEST(JsonSwapTest, ObjectT)
+{
+ json j = {{"one", 1}, {"two", 2}};
+ json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
+
+ j.swap(o);
+
+ EXPECT_EQ(j, json({{"cow", "Kuh"}, {"chicken", "Huhn"}}));
+
+ j.swap(o);
+
+ EXPECT_EQ(j, json({{"one", 1}, {"two", 2}}));
+}
+
+TEST(JsonSwapTest, NonObjectT)
+{
+ json j = 17;
+ json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
+
+ EXPECT_THROW_MSG(j.swap(o), json::type_error,
+ "[json.exception.type_error.310] cannot use swap() with number");
+}
+
+TEST(JsonSwapTest, StringT)
+{
+ json j = "Hello world";
+ std::string s = "Hallo Welt";
+
+ j.swap(s);
+
+ EXPECT_EQ(j, json("Hallo Welt"));
+
+ j.swap(s);
+
+ EXPECT_EQ(j, json("Hello world"));
+}
+
+TEST(JsonSwapTest, NonStringT)
+{
+ json j = 17;
+ std::string s = "Hallo Welt";
+
+ EXPECT_THROW_MSG(j.swap(s), json::type_error,
+ "[json.exception.type_error.310] cannot use swap() with number");
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp b/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp
new file mode 100644
index 0000000..0c03ee0
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp
@@ -0,0 +1,1269 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+#include <fstream>
+
+TEST(MessagePackDiscardedTest, Case)
+{
+ // discarded values are not serialized
+ json j = json::value_t::discarded;
+ const auto result = json::to_msgpack(j);
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(MessagePackNullTest, Case)
+{
+ json j = nullptr;
+ std::vector<uint8_t> expected = {0xc0};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+TEST(MessagePackBooleanTest, True)
+{
+ json j = true;
+ std::vector<uint8_t> expected = {0xc3};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+TEST(MessagePackBooleanTest, False)
+{
+ json j = false;
+ std::vector<uint8_t> expected = {0xc2};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// -32..-1 (negative fixnum)
+TEST(MessagePackSignedTest, Neg0)
+{
+ for (auto i = -32; i <= -1; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = i;
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 1u);
+
+ // check individual bytes
+ EXPECT_EQ(static_cast<int8_t>(result[0]), i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// 0..127 (positive fixnum)
+TEST(MessagePackSignedTest, Pos0)
+{
+ for (size_t i = 0; i <= 127; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() = static_cast<int64_t>(i);
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 1u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(i));
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// 128..255 (int 8)
+TEST(MessagePackSignedTest, Pos1)
+{
+ for (size_t i = 128; i <= 255; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() = static_cast<int64_t>(i);
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xcc));
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 2u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xcc));
+ uint8_t restored = static_cast<uint8_t>(result[1]);
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// 256..65535 (int 16)
+TEST(MessagePackSignedTest, Pos2)
+{
+ for (size_t i = 256; i <= 65535; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() = static_cast<int64_t>(i);
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xcd));
+ expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 3u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xcd));
+ uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// 65536..4294967295 (int 32)
+class MessagePackSignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
+TEST_P(MessagePackSignedPos4Test, Case)
+{
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() = static_cast<int64_t>(GetParam());
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xce));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 5u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xce));
+ uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
+ static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+static const uint32_t pos4_numbers[] = {
+ 65536u,
+ 77777u,
+ 1048576u,
+ 4294967295u,
+};
+
+INSTANTIATE_TEST_CASE_P(MessagePackSignedPos4Tests, MessagePackSignedPos4Test,
+ ::testing::ValuesIn(pos4_numbers), );
+
+// 4294967296..9223372036854775807 (int 64)
+class MessagePackSignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
+TEST_P(MessagePackSignedPos8Test, Case)
+{
+ // create JSON value with integer number
+ json j = -1;
+ j.get_ref<int64_t&>() =
+ static_cast<int64_t>(GetParam());
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xcf));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
+ expected.push_back(static_cast<char>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 9u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xcf));
+ uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
+ static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+static const uint64_t pos8_numbers[] = {
+ 4294967296lu,
+ 9223372036854775807lu,
+};
+
+INSTANTIATE_TEST_CASE_P(MessagePackSignedPos8Tests, MessagePackSignedPos8Test,
+ ::testing::ValuesIn(pos8_numbers), );
+
+// -128..-33 (int 8)
+TEST(MessagePackSignedTest, Neg1)
+{
+ for (auto i = -128; i <= -33; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = i;
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xd0));
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 2u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xd0));
+ EXPECT_EQ(static_cast<int8_t>(result[1]), i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// -32768..-129 (int 16)
+TEST(MessagePackSignedTest, Neg2)
+{
+ for (int16_t i = -32768; i <= -129; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with integer number
+ json j = i;
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xd1));
+ expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 3u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xd1));
+ int16_t restored = static_cast<int16_t>((static_cast<uint8_t>(result[1]) << 8) +
+ static_cast<uint8_t>(result[2]));
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// -32769..-2147483648
+class MessagePackSignedNeg4Test : public ::testing::TestWithParam<int32_t> {};
+TEST_P(MessagePackSignedNeg4Test, Case)
+{
+ // create JSON value with integer number
+ json j = GetParam();
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xd2));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 5u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xd2));
+ uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
+ static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
+ EXPECT_EQ(static_cast<int32_t>(restored), GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+static const int32_t neg4_numbers[] = {
+ -32769,
+ -65536,
+ -77777,
+ -1048576,
+ -2147483648ll,
+};
+
+INSTANTIATE_TEST_CASE_P(MessagePackSignedNeg4Tests, MessagePackSignedNeg4Test,
+ ::testing::ValuesIn(neg4_numbers), );
+
+// -9223372036854775808..-2147483649 (int 64)
+class MessagePackSignedNeg8Test : public ::testing::TestWithParam<int64_t> {};
+TEST_P(MessagePackSignedNeg8Test, Case)
+{
+ // create JSON value with unsigned integer number
+ json j = GetParam();
+
+ // check type
+ EXPECT_TRUE(j.is_number_integer());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xd3));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 9u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xd3));
+ int64_t restored = (static_cast<int64_t>(static_cast<uint8_t>(result[1])) << 070) +
+ (static_cast<int64_t>(static_cast<uint8_t>(result[2])) << 060) +
+ (static_cast<int64_t>(static_cast<uint8_t>(result[3])) << 050) +
+ (static_cast<int64_t>(static_cast<uint8_t>(result[4])) << 040) +
+ (static_cast<int64_t>(static_cast<uint8_t>(result[5])) << 030) +
+ (static_cast<int64_t>(static_cast<uint8_t>(result[6])) << 020) +
+ (static_cast<int64_t>(static_cast<uint8_t>(result[7])) << 010) +
+ static_cast<int64_t>(static_cast<uint8_t>(result[8]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+static const int64_t neg8_numbers[] = {
+ INT64_MIN,
+ -2147483649ll,
+};
+
+INSTANTIATE_TEST_CASE_P(MessagePackSignedNeg8Tests, MessagePackSignedNeg8Test,
+ ::testing::ValuesIn(neg8_numbers), );
+
+// 0..127 (positive fixnum)
+TEST(MessagePackUnsignedTest, Pos0)
+{
+ for (size_t i = 0; i <= 127; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with unsigned integer number
+ json j = i;
+
+ // check type
+ EXPECT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 1u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(i));
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// 128..255 (uint 8)
+TEST(MessagePackUnsignedTest, Pos1)
+{
+ for (size_t i = 128; i <= 255; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with unsigned integer number
+ json j = i;
+
+ // check type
+ EXPECT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xcc));
+ expected.push_back(static_cast<uint8_t>(i));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 2u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xcc));
+ uint8_t restored = static_cast<uint8_t>(result[1]);
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// 256..65535 (uint 16)
+TEST(MessagePackUnsignedTest, Pos2)
+{
+ for (size_t i = 256; i <= 65535; ++i)
+ {
+ SCOPED_TRACE(i);
+
+ // create JSON value with unsigned integer number
+ json j = i;
+
+ // check type
+ EXPECT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xcd));
+ expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 3u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xcd));
+ uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
+ EXPECT_EQ(restored, i);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// 65536..4294967295 (uint 32)
+class MessagePackUnsignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
+TEST_P(MessagePackUnsignedPos4Test, Case)
+{
+ // create JSON value with unsigned integer number
+ json j = GetParam();
+
+ // check type
+ EXPECT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xce));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 5u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xce));
+ uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
+ (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
+ static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+INSTANTIATE_TEST_CASE_P(MessagePackUnsignedPos4Tests,
+ MessagePackUnsignedPos4Test,
+ ::testing::ValuesIn(pos4_numbers), );
+
+// 4294967296..18446744073709551615 (uint 64)
+class MessagePackUnsignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
+TEST_P(MessagePackUnsignedPos8Test, Case)
+{
+ // create JSON value with unsigned integer number
+ json j = GetParam();
+
+ // check type
+ EXPECT_TRUE(j.is_number_unsigned());
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xcf));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), 9u);
+
+ // check individual bytes
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xcf));
+ uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
+ (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
+ static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
+ EXPECT_EQ(restored, GetParam());
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+INSTANTIATE_TEST_CASE_P(MessagePackUnsignedPos8Tests,
+ MessagePackUnsignedPos8Test,
+ ::testing::ValuesIn(pos8_numbers), );
+
+// 3.1415925
+TEST(MessagePackFloatTest, Number)
+{
+ double v = 3.1415925;
+ json j = v;
+ std::vector<uint8_t> expected = {0xcb,0x40,0x09,0x21,0xfb,0x3f,0xa6,0xde,0xfc};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ EXPECT_EQ(json::from_msgpack(result), v);
+}
+
+// N = 0..31
+TEST(MessagePackStringTest, String1)
+{
+ // explicitly enumerate the first byte for all 32 strings
+ const std::vector<uint8_t> first_bytes =
+ {
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
+ 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+ 0xbb, 0xbc, 0xbd, 0xbe, 0xbf
+ };
+
+ for (size_t N = 0; N < first_bytes.size(); ++N)
+ {
+ SCOPED_TRACE(N);
+
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(N, 'x');
+ json j = s;
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(first_bytes[N]));
+ for (size_t i = 0; i < N; ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // check first byte
+ EXPECT_EQ((first_bytes[N] & 0x1f), static_cast<uint8_t>(N));
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), N + 1);
+ // check that no null byte is appended
+ if (N > 0)
+ {
+ EXPECT_NE(result.back(), '\x00');
+ }
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// N = 32..255
+TEST(MessagePackStringTest, String2)
+{
+ for (size_t N = 32; N <= 255; ++N)
+ {
+ SCOPED_TRACE(N);
+
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(N, 'x');
+ json j = s;
+
+ // create expected byte vector
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xd9));
+ expected.push_back(static_cast<uint8_t>(N));
+ for (size_t i = 0; i < N; ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), N + 2);
+ // check that no null byte is appended
+ EXPECT_NE(result.back(), '\x00');
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+ }
+}
+
+// N = 256..65535
+class MessagePackString3Test : public ::testing::TestWithParam<size_t> {};
+TEST_P(MessagePackString3Test, Case)
+{
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(GetParam(), 'x');
+ json j = s;
+
+ // create expected byte vector (hack: create string first)
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xda));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+ for (size_t i = 0; i < GetParam(); ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), GetParam() + 3);
+ // check that no null byte is appended
+ EXPECT_NE(result.back(), '\x00');
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+static size_t string3_lens[] = {
+ 256u,
+ 999u,
+ 1025u,
+ 3333u,
+ 2048u,
+ 65535u
+};
+
+INSTANTIATE_TEST_CASE_P(MessagePackString3Tests, MessagePackString3Test,
+ ::testing::ValuesIn(string3_lens), );
+
+
+// N = 65536..4294967295
+class MessagePackString5Test : public ::testing::TestWithParam<size_t> {};
+TEST_P(MessagePackString5Test, Case)
+{
+ // create JSON value with string containing of N * 'x'
+ const auto s = std::string(GetParam(), 'x');
+ json j = s;
+
+ // create expected byte vector (hack: create string first)
+ std::vector<uint8_t> expected;
+ expected.push_back(static_cast<uint8_t>(0xdb));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
+ expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
+ expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
+ for (size_t i = 0; i < GetParam(); ++i)
+ {
+ expected.push_back('x');
+ }
+
+ // compare result + size
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+ EXPECT_EQ(result.size(), GetParam() + 5);
+ // check that no null byte is appended
+ EXPECT_NE(result.back(), '\x00');
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+static size_t string5_lens[] = {
+ 65536u,
+ 77777u,
+ 1048576u
+};
+
+INSTANTIATE_TEST_CASE_P(MessagePackString5Tests, MessagePackString5Test,
+ ::testing::ValuesIn(string5_lens), );
+
+TEST(MessagePackArrayTest, Empty)
+{
+ json j = json::array();
+ std::vector<uint8_t> expected = {0x90};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// [null]
+TEST(MessagePackArrayTest, Null)
+{
+ json j = {nullptr};
+ std::vector<uint8_t> expected = {0x91,0xc0};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// [1,2,3,4,5]
+TEST(MessagePackArrayTest, Simple)
+{
+ json j = json::parse("[1,2,3,4,5]");
+ std::vector<uint8_t> expected = {0x95,0x01,0x02,0x03,0x04,0x05};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// [[[[]]]]
+TEST(MessagePackArrayTest, NestEmpty)
+{
+ json j = json::parse("[[[[]]]]");
+ std::vector<uint8_t> expected = {0x91,0x91,0x91,0x90};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// array 16
+TEST(MessagePackArrayTest, UInt16)
+{
+ json j(16, nullptr);
+ std::vector<uint8_t> expected(j.size() + 3, static_cast<uint8_t>(0xc0)); // all null
+ expected[0] = static_cast<uint8_t>(0xdc); // array 16
+ expected[1] = 0x00; // size (0x0010), byte 0
+ expected[2] = 0x10; // size (0x0010), byte 1
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// array 32
+TEST(MessagePackArrayTest, UInt32)
+{
+ json j(65536, nullptr);
+ std::vector<uint8_t> expected(j.size() + 5, static_cast<uint8_t>(0xc0)); // all null
+ expected[0] = static_cast<uint8_t>(0xdd); // array 32
+ expected[1] = 0x00; // size (0x00100000), byte 0
+ expected[2] = 0x01; // size (0x00100000), byte 1
+ expected[3] = 0x00; // size (0x00100000), byte 2
+ expected[4] = 0x00; // size (0x00100000), byte 3
+ const auto result = json::to_msgpack(j);
+ //EXPECT_EQ(result, expected);
+
+ EXPECT_EQ(result.size(), expected.size());
+ for (size_t i = 0; i < expected.size(); ++i)
+ {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(result[i], expected[i]);
+ }
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+TEST(MessagePackObjectTest, Empty)
+{
+ json j = json::object();
+ std::vector<uint8_t> expected = {0x80};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// {"":null}
+TEST(MessagePackObjectTest, EmptyKey)
+{
+ json j = {{"", nullptr}};
+ std::vector<uint8_t> expected = {0x81,0xa0,0xc0};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// {"a": {"b": {"c": {}}}}
+TEST(MessagePackObjectTest, NestedEmpty)
+{
+ json j = json::parse("{\"a\": {\"b\": {\"c\": {}}}}");
+ std::vector<uint8_t> expected = {0x81,0xa1,0x61,0x81,0xa1,0x62,0x81,0xa1,0x63,0x80};
+ const auto result = json::to_msgpack(j);
+ EXPECT_EQ(result, expected);
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// map 16
+TEST(MessagePackObjectTest, UInt16)
+{
+ json j = R"({"00": null, "01": null, "02": null, "03": null,
+ "04": null, "05": null, "06": null, "07": null,
+ "08": null, "09": null, "10": null, "11": null,
+ "12": null, "13": null, "14": null, "15": null})"_json;
+
+ const auto result = json::to_msgpack(j);
+
+ // Checking against an expected vector byte by byte is
+ // difficult, because no assumption on the order of key/value
+ // pairs are made. We therefore only check the prefix (type and
+ // size and the overall size. The rest is then handled in the
+ // roundtrip check.
+ EXPECT_EQ(result.size(), 67u); // 1 type, 2 size, 16*4 content
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xde)); // map 16
+ EXPECT_EQ(result[1], 0x00); // byte 0 of size (0x0010)
+ EXPECT_EQ(result[2], 0x10); // byte 1 of size (0x0010)
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// map 32
+TEST(MessagePackObjectTest, UInt32)
+{
+ json j;
+ for (auto i = 0; i < 65536; ++i)
+ {
+ // format i to a fixed width of 5
+ // each entry will need 7 bytes: 6 for fixstr, 1 for null
+ std::stringstream ss;
+ ss << std::setw(5) << std::setfill('0') << i;
+ j.emplace(ss.str(), nullptr);
+ }
+
+ const auto result = json::to_msgpack(j);
+
+ // Checking against an expected vector byte by byte is
+ // difficult, because no assumption on the order of key/value
+ // pairs are made. We therefore only check the prefix (type and
+ // size and the overall size. The rest is then handled in the
+ // roundtrip check.
+ EXPECT_EQ(result.size(), 458757u); // 1 type, 4 size, 65536*7 content
+ EXPECT_EQ(result[0], static_cast<uint8_t>(0xdf)); // map 32
+ EXPECT_EQ(result[1], 0x00); // byte 0 of size (0x00010000)
+ EXPECT_EQ(result[2], 0x01); // byte 1 of size (0x00010000)
+ EXPECT_EQ(result[3], 0x00); // byte 2 of size (0x00010000)
+ EXPECT_EQ(result[4], 0x00); // byte 3 of size (0x00010000)
+
+ // roundtrip
+ EXPECT_EQ(json::from_msgpack(result), j);
+}
+
+// from float32
+TEST(MessagePackFloat32Test, Case)
+{
+ auto given = std::vector<uint8_t>({0xca,0x41,0xc8,0x00,0x01});
+ json j = json::from_msgpack(given);
+ EXPECT_LT(std::fabs(j.get<double>() - 25), 0.001);
+}
+
+TEST(MessagePackErrorTest, TooShortByteVector)
+{
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcc})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcd})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcd,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 6: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 7: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 8: unexpected end of input");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
+ "[json.exception.parse_error.110] parse error at 9: unexpected end of input");
+}
+
+TEST(MessagePackErrorTest, UnsupportedBytesConcrete)
+{
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xc1})), json::parse_error,
+ "[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc1");
+ EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xc6})), json::parse_error,
+ "[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc6");
+}
+
+TEST(MessagePackErrorTest, UnsupportedBytesAll)
+{
+ for (auto byte :
+ {
+ // never used
+ 0xc1,
+ // bin
+ 0xc4, 0xc5, 0xc6,
+ // ext
+ 0xc7, 0xc8, 0xc9,
+ // fixext
+ 0xd4, 0xd5, 0xd6, 0xd7, 0xd8
+ })
+ {
+ EXPECT_THROW(json::from_msgpack(std::vector<uint8_t>({static_cast<uint8_t>(byte)})), json::parse_error);
+ }
+}
+#if 0
+// use this testcase outside [hide] to run it with Valgrind
+TEST(MessagePackRoundtripTest, Sample)
+{
+ std::string filename = "test/data/json_testsuite/sample.json";
+
+ // parse JSON file
+ std::ifstream f_json(filename);
+ json j1 = json::parse(f_json);
+
+ // parse MessagePack file
+ std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary);
+ std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_msgpack)),
+ std::istreambuf_iterator<char>());
+ json j2;
+ EXPECT_NO_THROW(j2 = json::from_msgpack(packed));
+
+ // compare parsed JSON values
+ EXPECT_EQ(j1, j2);
+
+ // check with different start index
+ packed.insert(packed.begin(), 5, 0xff);
+ EXPECT_EQ(j1, json::from_msgpack(packed, 5));
+}
+
+class MessagePackRegressionTest : public ::testing::TestWithParam<const char*> {};
+TEST_P(MessagePackRegressionTest, Case)
+{
+ // parse JSON file
+ std::ifstream f_json(GetParam());
+ json j1 = json::parse(f_json);
+
+ // parse MessagePack file
+ std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary);
+ std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_msgpack)),
+ std::istreambuf_iterator<char>());
+ json j2;
+ EXPECT_NO_THROW(j2 = json::from_msgpack(packed));
+
+ // compare parsed JSON values
+ EXPECT_EQ(j1, j2);
+}
+
+static const char* regression_test_cases[] = {
+ "test/data/json_nlohmann_tests/all_unicode.json",
+ "test/data/json.org/1.json",
+ "test/data/json.org/2.json",
+ "test/data/json.org/3.json",
+ "test/data/json.org/4.json",
+ "test/data/json.org/5.json",
+ "test/data/json_roundtrip/roundtrip01.json",
+ "test/data/json_roundtrip/roundtrip02.json",
+ "test/data/json_roundtrip/roundtrip03.json",
+ "test/data/json_roundtrip/roundtrip04.json",
+ "test/data/json_roundtrip/roundtrip05.json",
+ "test/data/json_roundtrip/roundtrip06.json",
+ "test/data/json_roundtrip/roundtrip07.json",
+ "test/data/json_roundtrip/roundtrip08.json",
+ "test/data/json_roundtrip/roundtrip09.json",
+ "test/data/json_roundtrip/roundtrip10.json",
+ "test/data/json_roundtrip/roundtrip11.json",
+ "test/data/json_roundtrip/roundtrip12.json",
+ "test/data/json_roundtrip/roundtrip13.json",
+ "test/data/json_roundtrip/roundtrip14.json",
+ "test/data/json_roundtrip/roundtrip15.json",
+ "test/data/json_roundtrip/roundtrip16.json",
+ "test/data/json_roundtrip/roundtrip17.json",
+ "test/data/json_roundtrip/roundtrip18.json",
+ "test/data/json_roundtrip/roundtrip19.json",
+ "test/data/json_roundtrip/roundtrip20.json",
+ "test/data/json_roundtrip/roundtrip21.json",
+ "test/data/json_roundtrip/roundtrip22.json",
+ "test/data/json_roundtrip/roundtrip23.json",
+ "test/data/json_roundtrip/roundtrip24.json",
+ "test/data/json_roundtrip/roundtrip25.json",
+ "test/data/json_roundtrip/roundtrip26.json",
+ "test/data/json_roundtrip/roundtrip27.json",
+ "test/data/json_roundtrip/roundtrip28.json",
+ "test/data/json_roundtrip/roundtrip29.json",
+ "test/data/json_roundtrip/roundtrip30.json",
+ "test/data/json_roundtrip/roundtrip31.json",
+ "test/data/json_roundtrip/roundtrip32.json",
+ "test/data/json_testsuite/sample.json", // kills AppVeyor
+ "test/data/json_tests/pass1.json",
+ "test/data/json_tests/pass2.json",
+ "test/data/json_tests/pass3.json",
+ "test/data/regression/floats.json",
+ "test/data/regression/signed_ints.json",
+ "test/data/regression/unsigned_ints.json",
+ "test/data/regression/working_file.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_arraysWithSpaces.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_empty-string.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_empty.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_ending_with_newline.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_false.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_heterogeneous.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_null.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_1_and_newline.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_leading_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_several_null.json",
+ "test/data/nst_json_testsuite/test_parsing/y_array_with_trailing_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_0e+1.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_0e1.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_negative_one.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_negative_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_neg_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_pos_exp.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_neg_int.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_pos_int.json",
+ //"test/data/nst_json_testsuite/test_parsing/y_number_very_big_negative_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_basic.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key_and_value.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_empty.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_empty_key.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_escaped_null_in_key.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_extreme_numbers.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_long_strings.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_simple.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_string_unicode.json",
+ "test/data/nst_json_testsuite/test_parsing/y_object_with_newlines.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_UTF-16_Surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pair.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pairs.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_allowed_escapes.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_backslash_and_u_escaped_zero.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_backslash_doublequotes.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_comments.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_a.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_n.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_escaped_control_character.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_escaped_noncharacter.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_in_array.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_in_array_with_leading_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_last_surrogates_1_and_2.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_newline_uescaped.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+1FFFF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_null_escape.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_one-byte-utf-8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_pi.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_simple_ascii.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_space.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_three-byte-utf-8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_two-byte-utf-8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_u+2028_line_sep.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_u+2029_par_sep.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_uEscape.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unescaped_char_delete.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicodeEscapedBackslash.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_2.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+2064_invisible_plus.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_unicode_escaped_double_quote.json",
+ // "test/data/nst_json_testsuite/test_parsing/y_string_utf16.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_utf8.json",
+ "test/data/nst_json_testsuite/test_parsing/y_string_with_del_character.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_false.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_int.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_negative_real.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_null.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_string.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_true.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_string_empty.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_trailing_newline.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_true_in_array.json",
+ "test/data/nst_json_testsuite/test_parsing/y_structure_whitespace_array.json",
+};
+
+INSTANTIATE_TEST_CASE_P(MessagePackRegressionTests, MessagePackRegressionTest,
+ ::testing::ValuesIn(regression_test_cases));
+#endif
diff --git a/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp b/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp
new file mode 100644
index 0000000..a1a7fa4
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp
@@ -0,0 +1,463 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+TEST(JsonPointerTest, TypesCreate)
+{
+ // create a JSON value with different types
+ json json_types =
+ {
+ {"boolean", true},
+ {
+ "number", {
+ {"integer", 42},
+ {"unsigned", 42u},
+ {"floating-point", 17.23}
+ }
+ },
+ {"string", "Hello, world!"},
+ {"array", {1, 2, 3, 4, 5}},
+ {"null", nullptr}
+ };
+}
+
+// pointer access to object_t
+TEST(JsonPointerTest, ObjectT)
+{
+ using test_type = json::object_t;
+ json value = {{"one", 1}, {"two", 2}};
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_NE(value.get_ptr<json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<double*>(), nullptr);
+}
+
+// pointer access to const object_t
+TEST(JsonPointerTest, ConstObjectT)
+{
+ using test_type = const json::object_t;
+ const json value = {{"one", 1}, {"two", 2}};
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_NE(value.get_ptr<const json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
+}
+
+// pointer access to array_t
+TEST(JsonPointerTest, ArrayT)
+{
+ using test_type = json::array_t;
+ json value = {1, 2, 3, 4};
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<double*>(), nullptr);
+}
+
+// pointer access to const array_t
+TEST(JsonPointerTest, ConstArrayT)
+{
+ using test_type = const json::array_t;
+ const json value = {1, 2, 3, 4};
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<const json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
+}
+
+// pointer access to string_t
+TEST(JsonPointerTest, StringT)
+{
+ using test_type = std::string;
+ json value = "hello";
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<double*>(), nullptr);
+}
+
+// pointer access to const string_t
+TEST(JsonPointerTest, ConstStringT)
+{
+ using test_type = const std::string;
+ const json value = "hello";
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<const std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
+}
+
+// pointer access to boolean_t
+TEST(JsonPointerTest, BooleanT)
+{
+ using test_type = bool;
+ json value = false;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
+ EXPECT_NE(value.get_ptr<bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<double*>(), nullptr);
+}
+
+// pointer access to const boolean_t
+TEST(JsonPointerTest, ConstBooleanT)
+{
+ using test_type = const bool;
+ const json value = false;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ //EXPECT_EQ(*p1, value.get<test_type>());
+
+ //const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ //EXPECT_EQ(*p2, value.get<test_type>());
+
+ //const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ //EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
+ EXPECT_NE(value.get_ptr<const bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
+}
+
+// pointer access to number_integer_t
+TEST(JsonPointerTest, IntegerT)
+{
+ using test_type = int64_t;
+ json value = 23;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
+ EXPECT_NE(value.get_ptr<int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<double*>(), nullptr);
+}
+
+// pointer access to const number_integer_t
+TEST(JsonPointerTest, ConstIntegerT)
+{
+ using test_type = const int64_t;
+ const json value = 23;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
+ EXPECT_NE(value.get_ptr<const int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
+}
+
+// pointer access to number_unsigned_t
+TEST(JsonPointerTest, UnsignedT)
+{
+ using test_type = uint64_t;
+ json value = 23u;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
+ EXPECT_NE(value.get_ptr<int64_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<double*>(), nullptr);
+}
+
+// pointer access to const number_unsigned_t
+TEST(JsonPointerTest, ConstUnsignedT)
+{
+ using test_type = const uint64_t;
+ const json value = 23u;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(*p1, value.get<test_type>());
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_EQ(*p2, value.get<test_type>());
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_EQ(*p3, value.get<test_type>());
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
+ EXPECT_NE(value.get_ptr<const int64_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<const uint64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
+}
+
+// pointer access to number_float_t
+TEST(JsonPointerTest, FloatT)
+{
+ using test_type = double;
+ json value = 42.23;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_LT(std::fabs(*p1 - value.get<test_type>()), 0.001);
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_LT(std::fabs(*p2 - value.get<test_type>()), 0.001);
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_LT(std::fabs(*p3 - value.get<test_type>()), 0.001);
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<double*>(), nullptr);
+}
+
+// pointer access to const number_float_t
+TEST(JsonPointerTest, ConstFloatT)
+{
+ using test_type = const double;
+ const json value = 42.23;
+
+ // check if pointers are returned correctly
+ test_type* p1 = value.get_ptr<test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<test_type*>());
+ EXPECT_LT(std::fabs(*p1 - value.get<test_type>()), 0.001);
+
+ const test_type* p2 = value.get_ptr<const test_type*>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type*>());
+ EXPECT_LT(std::fabs(*p2 - value.get<test_type>()), 0.001);
+
+ const test_type* const p3 = value.get_ptr<const test_type* const>();
+ EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
+ EXPECT_LT(std::fabs(*p3 - value.get<test_type>()), 0.001);
+
+ // check if null pointers are returned correctly
+ EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
+ EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
+ EXPECT_NE(value.get_ptr<const double*>(), nullptr);
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-readme.cpp b/wpiutil/src/test/native/cpp/json/unit-readme.cpp
new file mode 100644
index 0000000..ecb7f79
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-readme.cpp
@@ -0,0 +1,327 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+#include <array>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "wpi/raw_ostream.h"
+
+TEST(JsonReadmeTest, Basic)
+{
+ // create an empty structure (null)
+ json j;
+
+ // add a number that is stored as double (note the implicit conversion of j to an object)
+ j["pi"] = 3.141;
+
+ // add a Boolean that is stored as bool
+ j["happy"] = true;
+
+ // add a string that is stored as std::string
+ j["name"] = "Niels";
+
+ // add another null object by passing nullptr
+ j["nothing"] = nullptr;
+
+ // add an object inside the object
+ j["answer"]["everything"] = 42;
+
+ // add an array that is stored as std::vector (using an initializer list)
+ j["list"] = { 1, 0, 2 };
+
+ // add another object (using an initializer list of pairs)
+ j["object"] = { {"currency", "USD"}, {"value", 42.99} };
+
+ // instead, you could also write (which looks very similar to the JSON above)
+ json j2 =
+ {
+ {"pi", 3.141},
+ {"happy", true},
+ {"name", "Niels"},
+ {"nothing", nullptr},
+ {
+ "answer", {
+ {"everything", 42}
+ }
+ },
+ {"list", {1, 0, 2}},
+ {
+ "object", {
+ {"currency", "USD"},
+ {"value", 42.99}
+ }
+ }
+ };
+}
+
+TEST(JsonReadmeTest, Other)
+{
+ // ways to express the empty array []
+ json empty_array_implicit = {{}};
+ json empty_array_explicit = json::array();
+
+ // a way to express the empty object {}
+ json empty_object_explicit = json::object();
+
+ // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
+ json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) };
+}
+
+TEST(JsonReadmeTest, FromToString)
+{
+ // create object from string literal
+ json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
+
+ // or even nicer with a raw string literal
+ auto j2 = R"(
+ {
+ "happy": true,
+ "pi": 3.141
+ }
+)"_json;
+
+ // or explicitly
+ auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
+
+ // explicit conversion to string
+ std::string s;
+ wpi::raw_string_ostream os(s);
+ j.dump(os); // {\"happy\":true,\"pi\":3.141}
+ EXPECT_EQ(os.str(), "{\"happy\":true,\"pi\":3.141}");
+
+ // serialization with pretty printing
+ // pass in the amount of spaces to indent
+ std::string s2;
+ wpi::raw_string_ostream os2(s2);
+ j2.dump(os2, 4);
+ EXPECT_EQ(os2.str(), "{\n \"happy\": true,\n \"pi\": 3.141\n}");
+ // {
+ // "happy": true,
+ // "pi": 3.141
+ // }
+}
+
+TEST(JsonReadmeTest, Basic2)
+{
+ // create an array using push_back
+ json j;
+ j.push_back("foo");
+ j.push_back(1);
+ j.push_back(true);
+
+ std::string s;
+ wpi::raw_string_ostream os(s);
+
+ // iterate the array
+ for (json::iterator it = j.begin(); it != j.end(); ++it)
+ {
+ os << *it << '\n';
+ }
+
+ // range-based for
+ for (auto element : j)
+ {
+ os << element << '\n';
+ }
+
+ // comparison
+ bool x = (j == "[\"foo\", 1, true]"_json); // true
+ EXPECT_EQ(x, true);
+
+ // getter/setter
+ const std::string tmp = j[0];
+ j[1] = 42;
+ bool foo = j.at(2);
+ EXPECT_EQ(foo, true);
+
+ // other stuff
+ EXPECT_EQ(j.size(), 3u); // 3 entries
+ EXPECT_EQ(j.empty(), false);
+ EXPECT_EQ(j.type(), json::value_t::array);
+ j.clear(); // the array is empty again
+ EXPECT_EQ(j.size(), 0u);
+ EXPECT_EQ(j.empty(), true);
+
+ // create an object
+ json o;
+ o["foo"] = 23;
+ o["bar"] = false;
+ o["baz"] = 3.141;
+
+ // find an entry
+ if (o.find("foo") != o.end())
+ {
+ // there is an entry with key "foo"
+ }
+}
+
+TEST(JsonReadmeTest, OtherContainer)
+{
+ std::vector<int> c_vector {1, 2, 3, 4};
+ json j_vec(c_vector);
+ json j_vec2(wpi::makeArrayRef(c_vector));
+ // [1, 2, 3, 4]
+
+ std::deque<float> c_deque {1.2f, 2.3f, 3.4f, 5.6f};
+ json j_deque(c_deque);
+ // [1.2, 2.3, 3.4, 5.6]
+
+ std::list<bool> c_list {true, true, false, true};
+ json j_list(c_list);
+ // [true, true, false, true]
+
+ std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
+ json j_flist(c_flist);
+ // [12345678909876, 23456789098765, 34567890987654, 45678909876543]
+
+ std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
+ json j_array(c_array);
+ // [1, 2, 3, 4]
+
+ std::set<std::string> c_set {"one", "two", "three", "four", "one"};
+ json j_set(c_set); // only one entry for "one" is used
+ // ["four", "one", "three", "two"]
+
+ std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
+ json j_uset(c_uset); // only one entry for "one" is used
+ // maybe ["two", "three", "four", "one"]
+
+ std::multiset<std::string> c_mset {"one", "two", "one", "four"};
+ json j_mset(c_mset); // both entries for "one" are used
+ // maybe ["one", "two", "one", "four"]
+
+ std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
+ json j_umset(c_umset); // both entries for "one" are used
+ // maybe ["one", "two", "one", "four"]
+}
+
+TEST(JsonReadmeTest, MapContainer)
+{
+ std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
+ json j_map(c_map);
+ // {"one": 1, "two": 2, "three": 3}
+
+#if 0
+ std::unordered_map<const char*, float> c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} };
+ json j_umap(c_umap);
+ // {"one": 1.2, "two": 2.3, "three": 3.4}
+#endif
+
+ std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
+ json j_mmap(c_mmap); // only one entry for key "three" is used
+ // maybe {"one": true, "two": true, "three": true}
+
+ std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
+ json j_ummap(c_ummap); // only one entry for key "three" is used
+ // maybe {"one": true, "two": true, "three": true}
+}
+
+TEST(JsonReadmeTest, Values)
+{
+ // strings
+ std::string s1 = "Hello, world!";
+ json js = s1;
+ std::string s2 = js;
+ EXPECT_EQ(s1, s2);
+
+ // Booleans
+ bool b1 = true;
+ json jb = b1;
+ bool b2 = jb;
+ EXPECT_EQ(b1, b2);
+
+ // numbers
+ int i = 42;
+ json jn = i;
+ double f = jn;
+ EXPECT_EQ(i, f);
+
+ // etc.
+
+ std::string vs = js.get<std::string>();
+ bool vb = jb.get<bool>();
+ int vi = jn.get<int>();
+ EXPECT_EQ(s1, vs);
+ EXPECT_EQ(b1, vb);
+ EXPECT_EQ(i, vi);
+
+ // etc.
+}
+
+#if 0
+TEST(JsonReadmeTest, DiffPatch)
+{
+ // a JSON value
+ json j_original = R"({
+ "baz": ["one", "two", "three"],
+ "foo": "bar"
+})"_json;
+
+ // access members with a JSON pointer (RFC 6901)
+ j_original["/baz/1"_json_pointer];
+ // "two"
+
+ // a JSON patch (RFC 6902)
+ json j_patch = R"([
+ { "op": "replace", "path": "/baz", "value": "boo" },
+ { "op": "add", "path": "/hello", "value": ["world"] },
+ { "op": "remove", "path": "/foo"}
+])"_json;
+
+ // apply the patch
+ json j_result = j_original.patch(j_patch);
+ // {
+ // "baz": "boo",
+ // "hello": ["world"]
+ // }
+
+ // calculate a JSON patch from two JSON values
+ json::diff(j_result, j_original);
+ // [
+ // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
+ // { "op":"remove","path":"/hello" },
+ // { "op":"add","path":"/foo","value":"bar" }
+ // ]
+}
+#endif
diff --git a/wpiutil/src/test/native/cpp/json/unit-reference_access.cpp b/wpiutil/src/test/native/cpp/json/unit-reference_access.cpp
new file mode 100644
index 0000000..7c3cfb1
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-reference_access.cpp
@@ -0,0 +1,197 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+// reference access to object_t
+TEST(JsonReferenceTest, ObjectT)
+{
+ using test_type = json::object_t;
+ json value = {{"one", 1}, {"two", 2}};
+
+ // check if references are returned correctly
+ test_type& p1 = value.get_ref<test_type&>();
+ EXPECT_EQ(&p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(p1, value.get<test_type>());
+
+ const test_type& p2 = value.get_ref<const test_type&>();
+ EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
+ EXPECT_EQ(p2, value.get<test_type>());
+
+ // check if mismatching references throw correctly
+ EXPECT_NO_THROW(value.get_ref<json::object_t&>());
+ EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
+ EXPECT_ANY_THROW(value.get_ref<std::string&>());
+ EXPECT_ANY_THROW(value.get_ref<bool&>());
+ EXPECT_ANY_THROW(value.get_ref<int64_t&>());
+ EXPECT_ANY_THROW(value.get_ref<double&>());
+}
+
+// const reference access to const object_t
+TEST(JsonReferenceTest, ConstObjectT)
+{
+ using test_type = json::object_t;
+ const json value = {{"one", 1}, {"two", 2}};
+
+ // this should not compile
+ // test_type& p1 = value.get_ref<test_type&>();
+
+ // check if references are returned correctly
+ const test_type& p2 = value.get_ref<const test_type&>();
+ EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
+ EXPECT_EQ(p2, value.get<test_type>());
+}
+
+// reference access to array_t
+TEST(JsonReferenceTest, ArrayT)
+{
+ using test_type = json::array_t;
+ json value = {1, 2, 3, 4};
+
+ // check if references are returned correctly
+ test_type& p1 = value.get_ref<test_type&>();
+ EXPECT_EQ(&p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(p1, value.get<test_type>());
+
+ const test_type& p2 = value.get_ref<const test_type&>();
+ EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
+ EXPECT_EQ(p2, value.get<test_type>());
+
+ // check if mismatching references throw correctly
+ EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
+ EXPECT_NO_THROW(value.get_ref<json::array_t&>());
+ EXPECT_ANY_THROW(value.get_ref<std::string&>());
+ EXPECT_ANY_THROW(value.get_ref<bool&>());
+ EXPECT_ANY_THROW(value.get_ref<int64_t&>());
+ EXPECT_ANY_THROW(value.get_ref<double&>());
+}
+
+// reference access to string_t
+TEST(JsonReferenceTest, StringT)
+{
+ using test_type = std::string;
+ json value = "hello";
+
+ // check if references are returned correctly
+ test_type& p1 = value.get_ref<test_type&>();
+ EXPECT_EQ(&p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(p1, value.get<test_type>());
+
+ const test_type& p2 = value.get_ref<const test_type&>();
+ EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
+ EXPECT_EQ(p2, value.get<test_type>());
+
+ // check if mismatching references throw correctly
+ EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
+ EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
+ EXPECT_NO_THROW(value.get_ref<std::string&>());
+ EXPECT_ANY_THROW(value.get_ref<bool&>());
+ EXPECT_ANY_THROW(value.get_ref<int64_t&>());
+ EXPECT_ANY_THROW(value.get_ref<double&>());
+}
+
+// reference access to boolean_t
+TEST(JsonReferenceTest, BooleanT)
+{
+ using test_type = bool;
+ json value = false;
+
+ // check if references are returned correctly
+ test_type& p1 = value.get_ref<test_type&>();
+ EXPECT_EQ(&p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(p1, value.get<test_type>());
+
+ const test_type& p2 = value.get_ref<const test_type&>();
+ EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
+ EXPECT_EQ(p2, value.get<test_type>());
+
+ // check if mismatching references throw correctly
+ EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
+ EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
+ EXPECT_ANY_THROW(value.get_ref<std::string&>());
+ EXPECT_NO_THROW(value.get_ref<bool&>());
+ EXPECT_ANY_THROW(value.get_ref<int64_t&>());
+ EXPECT_ANY_THROW(value.get_ref<double&>());
+}
+
+// reference access to number_integer_t
+TEST(JsonReferenceTest, IntegerT)
+{
+ using test_type = int64_t;
+ json value = 23;
+
+ // check if references are returned correctly
+ test_type& p1 = value.get_ref<test_type&>();
+ EXPECT_EQ(&p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(p1, value.get<test_type>());
+
+ const test_type& p2 = value.get_ref<const test_type&>();
+ EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
+ EXPECT_EQ(p2, value.get<test_type>());
+
+ // check if mismatching references throw correctly
+ EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
+ EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
+ EXPECT_ANY_THROW(value.get_ref<std::string&>());
+ EXPECT_ANY_THROW(value.get_ref<bool&>());
+ EXPECT_NO_THROW(value.get_ref<int64_t&>());
+ EXPECT_ANY_THROW(value.get_ref<double&>());
+}
+
+// reference access to number_float_t
+TEST(JsonReferenceTest, FloatT)
+{
+ using test_type = double;
+ json value = 42.23;
+
+ // check if references are returned correctly
+ test_type& p1 = value.get_ref<test_type&>();
+ EXPECT_EQ(&p1, value.get_ptr<test_type*>());
+ EXPECT_EQ(p1, value.get<test_type>());
+
+ const test_type& p2 = value.get_ref<const test_type&>();
+ EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
+ EXPECT_EQ(p2, value.get<test_type>());
+
+ // check if mismatching references throw correctly
+ EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
+ EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
+ EXPECT_ANY_THROW(value.get_ref<std::string&>());
+ EXPECT_ANY_THROW(value.get_ref<bool&>());
+ EXPECT_ANY_THROW(value.get_ref<int64_t&>());
+ EXPECT_NO_THROW(value.get_ref<double&>());
+}
diff --git a/wpiutil/src/test/native/cpp/json/unit-unicode.cpp b/wpiutil/src/test/native/cpp/json/unit-unicode.cpp
new file mode 100644
index 0000000..e0179b6
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/json/unit-unicode.cpp
@@ -0,0 +1,1093 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 2.1.1
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "gtest/gtest.h"
+
+#include "unit-json.h"
+using wpi::json;
+
+#include "wpi/Format.h"
+#include "wpi/StringExtras.h"
+#include "wpi/raw_ostream.h"
+
+#include <fstream>
+
+// create and check a JSON string with up to four UTF-8 bytes
+::testing::AssertionResult check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
+{
+ std::string json_string = "\"";
+
+ json_string += std::string(1, static_cast<char>(byte1));
+
+ if (byte2 != -1)
+ {
+ json_string += std::string(1, static_cast<char>(byte2));
+ }
+
+ if (byte3 != -1)
+ {
+ json_string += std::string(1, static_cast<char>(byte3));
+ }
+
+ if (byte4 != -1)
+ {
+ json_string += std::string(1, static_cast<char>(byte4));
+ }
+
+ json_string += "\"";
+
+ const char* basemsg = "";
+
+ try {
+ json::parse(json_string);
+ } catch (json::parse_error&) {
+ if (success_expected)
+ {
+ basemsg = "parse_error";
+ goto error;
+ }
+ return ::testing::AssertionSuccess();
+ } catch (...) {
+ basemsg = "other exception";
+ goto error;
+ }
+
+ if (success_expected)
+ {
+ return ::testing::AssertionSuccess();
+ }
+ basemsg = "expected failure";
+
+error:
+ auto result = ::testing::AssertionFailure();
+ result << basemsg << " with {" << wpi::utohexstr(byte1);
+ if (byte2 != -1)
+ {
+ result << ',' << wpi::utohexstr(byte2);
+ }
+ if (byte3 != -1)
+ {
+ result << ',' << wpi::utohexstr(byte3);
+ }
+ if (byte4 != -1)
+ {
+ result << ',' << wpi::utohexstr(byte4);
+ }
+ result << '}';
+ return result;
+}
+
+/*
+RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
+follows:
+
+ A UTF-8 string is a sequence of octets representing a sequence of UCS
+ characters. An octet sequence is valid UTF-8 only if it matches the
+ following syntax, which is derived from the rules for encoding UTF-8
+ and is expressed in the ABNF of [RFC2234].
+
+ UTF8-octets = *( UTF8-char )
+ UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
+ UTF8-1 = %x00-7F
+ UTF8-2 = %xC2-DF UTF8-tail
+ UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
+ %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
+ UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
+ %xF4 %x80-8F 2( UTF8-tail )
+ UTF8-tail = %x80-BF
+*/
+
+// ill-formed first byte
+TEST(JsonUnicodeRfc3629Test, IllFormedFirstByte)
+{
+ for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+
+ for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// UTF8-1 (x00-x7F), well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_1WellFormed)
+{
+ for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1)
+ {
+ // unescaped control characters are parse errors in JSON
+ if (0x00 <= byte1 && byte1 <= 0x1F)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ continue;
+ }
+
+ // a single quote is a parse error in JSON
+ if (byte1 == 0x22)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ continue;
+ }
+
+ // a single backslash is a parse error in JSON
+ if (byte1 == 0x5C)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ continue;
+ }
+
+ // all other characters are OK
+ EXPECT_TRUE(check_utf8string(true, byte1));
+ }
+}
+
+// UTF8-2 (xC2-xDF UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_2WellFormed)
+{
+ for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_2Missing2)
+{
+ for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_2Wrong2)
+{
+ for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0x80 <= byte2 && byte2 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// UTF8-3 (xE0 xA0-BF UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_3AWellFormed)
+{
+ for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
+ {
+ for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3AMissing2)
+{
+ for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: missing third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3AMissing3)
+{
+ for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
+ {
+ for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3AWrong2)
+{
+ for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0xA0 <= byte2 && byte2 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: wrong third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3AWrong3)
+{
+ for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
+ {
+ for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
+ {
+ // skip correct third byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_3BWellFormed)
+{
+ for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3BMissing2)
+{
+ for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: missing third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3BMissing3)
+{
+ for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_3BWrong2)
+{
+ for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0x80 <= byte2 && byte2 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: wrong third byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_3BWrong3)
+{
+ for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
+ {
+ // skip correct third byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// UTF8-3 (xED x80-9F UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_3CWellFormed)
+{
+ for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3CMissing2)
+{
+ for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: missing third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3CMissing3)
+{
+ for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3CWrong2)
+{
+ for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0x80 <= byte2 && byte2 <= 0x9F)
+ {
+ continue;
+ }
+
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: wrong third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3CWrong3)
+{
+ for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
+ {
+ for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
+ {
+ // skip correct third byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_3DWellFormed)
+{
+ for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3DMissing2)
+{
+ for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: missing third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3DMissing3)
+{
+ for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3DWrong2)
+{
+ for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0x80 <= byte2 && byte2 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: wrong third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_3DWrong3)
+{
+ for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
+ {
+ // skip correct third byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// UTF8-4 (xF0 x90-BF UTF8-tail UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_4AWellFormed)
+{
+ for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
+ {
+ for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4AMissing2)
+{
+ for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: missing third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4AMissing3)
+{
+ for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
+ {
+ for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: missing fourth byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4AMissing4)
+{
+ for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
+ {
+ for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4AWrong2)
+{
+ for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0x90 <= byte2 && byte2 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: wrong third byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4AWrong3)
+{
+ for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
+ {
+ for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
+ {
+ // skip correct third byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: wrong fourth byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4AWrong4)
+{
+ for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
+ {
+ for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
+ {
+ // skip fourth second byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// UTF8-4 (xF1-F3 UTF8-tail UTF8-tail UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_4BWellFormed)
+{
+ for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4BMissing2)
+{
+ for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: missing third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4BMissing3)
+{
+ for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: missing fourth byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4BMissing4)
+{
+ for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4BWrong2)
+{
+ for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0x80 <= byte2 && byte2 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: wrong third byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4BWrong3)
+{
+ for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
+ {
+ // skip correct third byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: wrong fourth byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4BWrong4)
+{
+ for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
+ {
+ // skip correct fourth byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// UTF8-4 (xF4 x80-8F UTF8-tail UTF8-tail)
+// well-formed
+TEST(JsonUnicodeRfc3629Test, Utf8_4CWellFormed)
+{
+ for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: missing second byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4CMissing2)
+{
+ for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1));
+ }
+}
+
+// ill-formed: missing third byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4CMissing3)
+{
+ for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2));
+ }
+ }
+}
+
+// ill-formed: missing fourth byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4CMissing4)
+{
+ for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
+ }
+ }
+ }
+}
+
+// ill-formed: wrong second byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4CWrong2)
+{
+ for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
+ {
+ for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
+ {
+ // skip correct second byte
+ if (0x80 <= byte2 && byte2 <= 0x8F)
+ {
+ continue;
+ }
+
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: wrong third byte
+TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4CWrong3)
+{
+ for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
+ {
+ for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
+ {
+ // skip correct third byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
+ {
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// ill-formed: wrong fourth byte
+TEST(JsonUnicodeRfc3629Test, Utf8_4CWrong4)
+{
+ for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
+ {
+ for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
+ {
+ for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
+ {
+ for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
+ {
+ // skip correct fourth byte
+ if (0x80 <= byte3 && byte3 <= 0xBF)
+ {
+ continue;
+ }
+
+ EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
+ }
+ }
+ }
+ }
+}
+
+// \\uxxxx sequences
+
+// create an escaped string from a code point
+static std::string codepoint_to_unicode(std::size_t cp)
+{
+ // code points are represented as a six-character sequence: a
+ // reverse solidus, followed by the lowercase letter u, followed
+ // by four hexadecimal digits that encode the character's code
+ // point
+ std::string s;
+ wpi::raw_string_ostream ss(s);
+ ss << "\\u" << wpi::format_hex_no_prefix(cp, 4);
+ ss.flush();
+ return s;
+}
+
+// correct sequences
+TEST(JsonUnicodeCodepointTest, DISABLED_Correct)
+{
+ // generate all UTF-8 code points; in total, 1112064 code points are
+ // generated: 0x1FFFFF code points - 2048 invalid values between
+ // 0xD800 and 0xDFFF.
+ for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
+ {
+ // string to store the code point as in \uxxxx format
+ std::string json_text = "\"";
+
+ // decide whether to use one or two \uxxxx sequences
+ if (cp < 0x10000u)
+ {
+ // The Unicode standard permanently reserves these code point
+ // values for UTF-16 encoding of the high and low surrogates, and
+ // they will never be assigned a character, so there should be no
+ // reason to encode them. The official Unicode standard says that
+ // no UTF forms, including UTF-16, can encode these code points.
+ if (cp >= 0xD800u && cp <= 0xDFFFu)
+ {
+ // if we would not skip these code points, we would get a
+ // "missing low surrogate" exception
+ continue;
+ }
+
+ // code points in the Basic Multilingual Plane can be
+ // represented with one \uxxxx sequence
+ json_text += codepoint_to_unicode(cp);
+ }
+ else
+ {
+ // To escape an extended character that is not in the Basic
+ // Multilingual Plane, the character is represented as a
+ // 12-character sequence, encoding the UTF-16 surrogate pair
+ const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
+ const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
+ json_text += codepoint_to_unicode(codepoint1) + codepoint_to_unicode(codepoint2);
+ }
+
+ json_text += "\"";
+ SCOPED_TRACE(json_text);
+ EXPECT_NO_THROW(json::parse(json_text));
+ }
+}
+
+#if 0
+// incorrect sequences
+// high surrogate without low surrogate
+TEST(JsonUnicodeCodepointTest, IncorrectHighMissingLow)
+{
+ // D800..DBFF are high surrogates and must be followed by low
+ // surrogates DC00..DFFF; here, nothing follows
+ for (std::size_t cp = 0xD800u; cp <= 0xDBFFu; ++cp)
+ {
+ std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
+ SCOPED_TRACE(json_text);
+ EXPECT_THROW(json::parse(json_text), json::parse_error);
+ }
+}
+
+// high surrogate with wrong low surrogate
+TEST(JsonUnicodeCodepointTest, IncorrectHighWrongLow)
+{
+ // D800..DBFF are high surrogates and must be followed by low
+ // surrogates DC00..DFFF; here a different sequence follows
+ for (std::size_t cp1 = 0xD800u; cp1 <= 0xDBFFu; ++cp1)
+ {
+ for (std::size_t cp2 = 0x0000u; cp2 <= 0xFFFFu; ++cp2)
+ {
+ if (0xDC00u <= cp2 && cp2 <= 0xDFFFu)
+ {
+ continue;
+ }
+
+ std::string json_text = "\"" + codepoint_to_unicode(cp1) + codepoint_to_unicode(cp2) + "\"";
+ SCOPED_TRACE(json_text);
+ EXPECT_THROW(json::parse(json_text), json::parse_error);
+ }
+ }
+}
+
+// low surrogate without high surrogate
+TEST(JsonUnicodeCodepointTest, IncorrectLowMissingHigh)
+{
+ // low surrogates DC00..DFFF must follow high surrogates; here,
+ // they occur alone
+ for (std::size_t cp = 0xDC00u; cp <= 0xDFFFu; ++cp)
+ {
+ std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
+ SCOPED_TRACE(json_text);
+ EXPECT_THROW(json::parse(json_text), json::parse_error);
+ }
+}
+#endif
+
+#if 0
+// read all unicode characters
+TEST(JsonUnicodeTest, ReadAllUnicode)
+{
+ // read a file with all unicode characters stored as single-character
+ // strings in a JSON array
+ std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json");
+ json j;
+ CHECK_NOTHROW(f >> j);
+
+ // the array has 1112064 + 1 elemnts (a terminating "null" value)
+ // Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between
+ // 0xD800 and 0xDFFF.
+ CHECK(j.size() == 1112065);
+
+ SECTION("check JSON Pointers")
+ {
+ for (auto s : j)
+ {
+ // skip non-string JSON values
+ if (not s.is_string())
+ {
+ continue;
+ }
+
+ std::string ptr = s;
+
+ // tilde must be followed by 0 or 1
+ if (ptr == "~")
+ {
+ ptr += "0";
+ }
+
+ // JSON Pointers must begin with "/"
+ ptr = "/" + ptr;
+
+ CHECK_NOTHROW(json::json_pointer("/" + ptr));
+
+ // check escape/unescape roundtrip
+ auto escaped = json::json_pointer::escape(ptr);
+ json::json_pointer::unescape(escaped);
+ CHECK(escaped == ptr);
+ }
+ }
+}
+
+// ignore byte-order-mark
+// in a stream
+TEST(JsonUnicodeTest, IgnoreBOMStream)
+{
+ // read a file with a UTF-8 BOM
+ std::ifstream f("test/data/json_nlohmann_tests/bom.json");
+ json j;
+ EXPECT_NO_THROW(f >> j);
+}
+
+// with an iterator
+TEST(JsonUnicodeTest, IgnoreBOMIterator)
+{
+ std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}";
+ EXPECT_NO_THROW(json::parse(i.begin(), i.end()));
+}
+#endif
+// error for incomplete/wrong BOM
+TEST(JsonUnicodeTest, WrongBOM)
+{
+ EXPECT_THROW(json::parse("\xef\xbb"), json::parse_error);
+ EXPECT_THROW(json::parse("\xef\xbb\xbb"), json::parse_error);
+}
diff --git a/wpiutil/src/test/native/cpp/leb128Test.cpp b/wpiutil/src/test/native/cpp/leb128Test.cpp
new file mode 100644
index 0000000..a3a7ed0
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/leb128Test.cpp
@@ -0,0 +1,112 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+//===- llvm/unittest/Support/LEB128Test.cpp - LEB128 function tests -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdint.h>
+
+#include <string>
+
+#include "gtest/gtest.h"
+#include "wpi/SmallString.h"
+#include "wpi/StringRef.h"
+#include "wpi/leb128.h"
+#include "wpi/raw_istream.h"
+
+namespace wpi {
+
+TEST(LEB128Test, WriteUleb128) {
+#define EXPECT_ULEB128_EQ(EXPECTED, VALUE, PAD) \
+ do { \
+ StringRef expected(EXPECTED, sizeof(EXPECTED) - 1); \
+ SmallString<32> buf; \
+ size_t size = WriteUleb128(buf, VALUE); \
+ EXPECT_EQ(size, buf.size()); \
+ EXPECT_EQ(expected, buf.str()); \
+ } while (0)
+
+ // Write ULEB128
+ EXPECT_ULEB128_EQ("\x00", 0, 0);
+ EXPECT_ULEB128_EQ("\x01", 1, 0);
+ EXPECT_ULEB128_EQ("\x3f", 63, 0);
+ EXPECT_ULEB128_EQ("\x40", 64, 0);
+ EXPECT_ULEB128_EQ("\x7f", 0x7f, 0);
+ EXPECT_ULEB128_EQ("\x80\x01", 0x80, 0);
+ EXPECT_ULEB128_EQ("\x81\x01", 0x81, 0);
+ EXPECT_ULEB128_EQ("\x90\x01", 0x90, 0);
+ EXPECT_ULEB128_EQ("\xff\x01", 0xff, 0);
+ EXPECT_ULEB128_EQ("\x80\x02", 0x100, 0);
+ EXPECT_ULEB128_EQ("\x81\x02", 0x101, 0);
+
+#undef EXPECT_ULEB128_EQ
+}
+
+TEST(LEB128Test, ReadUleb128) {
+#define EXPECT_READ_ULEB128_EQ(EXPECTED, VALUE) \
+ do { \
+ uint64_t val = 0; \
+ size_t size = ReadUleb128(VALUE, &val); \
+ EXPECT_EQ(sizeof(VALUE) - 1, size); \
+ EXPECT_EQ(EXPECTED, val); \
+ } while (0)
+
+ // Read ULEB128
+ EXPECT_READ_ULEB128_EQ(0u, "\x00");
+ EXPECT_READ_ULEB128_EQ(1u, "\x01");
+ EXPECT_READ_ULEB128_EQ(63u, "\x3f");
+ EXPECT_READ_ULEB128_EQ(64u, "\x40");
+ EXPECT_READ_ULEB128_EQ(0x7fu, "\x7f");
+ EXPECT_READ_ULEB128_EQ(0x80u, "\x80\x01");
+ EXPECT_READ_ULEB128_EQ(0x81u, "\x81\x01");
+ EXPECT_READ_ULEB128_EQ(0x90u, "\x90\x01");
+ EXPECT_READ_ULEB128_EQ(0xffu, "\xff\x01");
+ EXPECT_READ_ULEB128_EQ(0x100u, "\x80\x02");
+ EXPECT_READ_ULEB128_EQ(0x101u, "\x81\x02");
+ EXPECT_READ_ULEB128_EQ(8320u, "\x80\xc1\x80\x80\x10");
+
+#undef EXPECT_READ_ULEB128_EQ
+}
+
+TEST(LEB128Test, SizeUleb128) {
+ // Testing Plan:
+ // (1) 128 ^ n ............ need (n+1) bytes
+ // (2) 128 ^ n * 64 ....... need (n+1) bytes
+ // (3) 128 ^ (n+1) - 1 .... need (n+1) bytes
+
+ EXPECT_EQ(1u, SizeUleb128(0)); // special case
+
+ EXPECT_EQ(1u, SizeUleb128(0x1UL));
+ EXPECT_EQ(1u, SizeUleb128(0x40UL));
+ EXPECT_EQ(1u, SizeUleb128(0x7fUL));
+
+ EXPECT_EQ(2u, SizeUleb128(0x80UL));
+ EXPECT_EQ(2u, SizeUleb128(0x2000UL));
+ EXPECT_EQ(2u, SizeUleb128(0x3fffUL));
+
+ EXPECT_EQ(3u, SizeUleb128(0x4000UL));
+ EXPECT_EQ(3u, SizeUleb128(0x100000UL));
+ EXPECT_EQ(3u, SizeUleb128(0x1fffffUL));
+
+ EXPECT_EQ(4u, SizeUleb128(0x200000UL));
+ EXPECT_EQ(4u, SizeUleb128(0x8000000UL));
+ EXPECT_EQ(4u, SizeUleb128(0xfffffffUL));
+
+ EXPECT_EQ(5u, SizeUleb128(0x10000000UL));
+ EXPECT_EQ(5u, SizeUleb128(0x40000000UL));
+ EXPECT_EQ(5u, SizeUleb128(0x7fffffffUL));
+
+ EXPECT_EQ(5u, SizeUleb128(UINT32_MAX));
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/main.cpp b/wpiutil/src/test/native/cpp/main.cpp
new file mode 100644
index 0000000..1e5ecf0
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/main.cpp
@@ -0,0 +1,14 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "gtest/gtest.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ return ret;
+}
diff --git a/wpiutil/src/test/native/cpp/priority_mutex_test.cpp b/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
new file mode 100644
index 0000000..c5c86b6
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
@@ -0,0 +1,271 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include <wpi/priority_mutex.h> // NOLINT(build/include_order)
+
+#include <atomic>
+#include <condition_variable>
+#include <thread>
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+#ifdef WPI_HAVE_PRIORITY_MUTEX
+
+using std::chrono::system_clock;
+
+// Threading primitive used to notify many threads that a condition is now true.
+// The condition can not be cleared.
+class Notification {
+ public:
+ // Efficiently waits until the Notification has been notified once.
+ void Wait() {
+ std::unique_lock<priority_mutex> lock(m_mutex);
+ while (!m_set) {
+ m_condition.wait(lock);
+ }
+ }
+ // Sets the condition to true, and wakes all waiting threads.
+ void Notify() {
+ std::lock_guard<priority_mutex> lock(m_mutex);
+ m_set = true;
+ m_condition.notify_all();
+ }
+
+ private:
+ // priority_mutex used for the notification and to protect the bit.
+ priority_mutex m_mutex;
+ // Condition for threads to sleep on.
+ std::condition_variable_any m_condition;
+ // Bool which is true when the notification has been notified.
+ bool m_set = false;
+};
+
+void SetProcessorAffinity(int32_t core_id) {
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ CPU_SET(core_id, &cpuset);
+
+ pthread_t current_thread = pthread_self();
+ ASSERT_EQ(pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset),
+ 0);
+}
+
+void SetThreadRealtimePriorityOrDie(int32_t priority) {
+ struct sched_param param;
+ // Set realtime priority for this thread
+ param.sched_priority = priority + sched_get_priority_min(SCHED_RR);
+ ASSERT_EQ(pthread_setschedparam(pthread_self(), SCHED_RR, ¶m), 0)
+ << ": Failed to set scheduler priority.";
+}
+
+// This thread holds the mutex and spins until signaled to release it and stop.
+template <typename MutexType>
+class LowPriorityThread {
+ public:
+ explicit LowPriorityThread(MutexType* mutex)
+ : m_mutex(mutex), m_hold_mutex(1), m_success(0) {}
+
+ void operator()() {
+ SetProcessorAffinity(0);
+ SetThreadRealtimePriorityOrDie(20);
+ m_mutex->lock();
+ m_started.Notify();
+ while (m_hold_mutex.test_and_set()) {
+ }
+ m_mutex->unlock();
+ m_success.store(1);
+ }
+
+ void WaitForStartup() { m_started.Wait(); }
+ void release_mutex() { m_hold_mutex.clear(); }
+ bool success() { return m_success.load(); }
+
+ private:
+ // priority_mutex to grab and release.
+ MutexType* m_mutex;
+ Notification m_started;
+ // Atomic type used to signal when the thread should unlock the mutex.
+ // Using a mutex to protect something could cause other issues, and a delay
+ // between setting and reading isn't a problem as long as the set is atomic.
+ std::atomic_flag m_hold_mutex;
+ std::atomic<int> m_success;
+};
+
+// This thread spins forever until signaled to stop.
+class BusyWaitingThread {
+ public:
+ BusyWaitingThread() : m_run(1), m_success(0) {}
+
+ void operator()() {
+ SetProcessorAffinity(0);
+ SetThreadRealtimePriorityOrDie(21);
+ system_clock::time_point start_time = system_clock::now();
+ m_started.Notify();
+ while (m_run.test_and_set()) {
+ // Have the busy waiting thread time out after a while. If it times out,
+ // the test failed.
+ if (system_clock::now() - start_time > std::chrono::milliseconds(50)) {
+ return;
+ }
+ }
+ m_success.store(1);
+ }
+
+ void quit() { m_run.clear(); }
+ void WaitForStartup() { m_started.Wait(); }
+ bool success() { return m_success.load(); }
+
+ private:
+ // Use an atomic type to signal if the thread should be running or not. A
+ // mutex could affect the scheduler, which isn't worth it. A delay between
+ // setting and reading the new value is fine.
+ std::atomic_flag m_run;
+
+ Notification m_started;
+
+ std::atomic<int> m_success;
+};
+
+// This thread starts up, grabs the mutex, and then exits.
+template <typename MutexType>
+class HighPriorityThread {
+ public:
+ explicit HighPriorityThread(MutexType* mutex) : m_mutex(mutex) {}
+
+ void operator()() {
+ SetProcessorAffinity(0);
+ SetThreadRealtimePriorityOrDie(22);
+ m_started.Notify();
+ m_mutex->lock();
+ m_success.store(1);
+ }
+
+ void WaitForStartup() { m_started.Wait(); }
+ bool success() { return m_success.load(); }
+
+ private:
+ Notification m_started;
+ MutexType* m_mutex;
+ std::atomic<int> m_success{0};
+};
+
+// Class to test a MutexType to see if it solves the priority inheritance
+// problem.
+//
+// To run the test, we need 3 threads, and then 1 thread to kick the test off.
+// The threads must all run on the same core, otherwise they wouldn't starve
+// eachother. The threads and their roles are as follows:
+//
+// Low priority thread:
+// Holds a lock that the high priority thread needs, and releases it upon
+// request.
+// Medium priority thread:
+// Hogs the processor so that the low priority thread will never run if it's
+// priority doesn't get bumped.
+// High priority thread:
+// Starts up and then goes to grab the lock that the low priority thread has.
+//
+// Control thread:
+// Sets the deadlock up so that it will happen 100% of the time by making sure
+// that each thread in order gets to the point that it needs to be at to cause
+// the deadlock.
+template <typename MutexType>
+class InversionTestRunner {
+ public:
+ void operator()() {
+ // This thread must run at the highest priority or it can't coordinate the
+ // inversion. This means that it can't busy wait or everything could
+ // starve.
+ SetThreadRealtimePriorityOrDie(23);
+
+ MutexType m;
+
+ // Start the lowest priority thread and wait until it holds the lock.
+ LowPriorityThread<MutexType> low(&m);
+ std::thread low_thread(std::ref(low));
+ low.WaitForStartup();
+
+ // Start the busy waiting thread and let it get to the loop.
+ BusyWaitingThread busy;
+ std::thread busy_thread(std::ref(busy));
+ busy.WaitForStartup();
+
+ // Start the high priority thread and let it become blocked on the lock.
+ HighPriorityThread<MutexType> high(&m);
+ std::thread high_thread(std::ref(high));
+ high.WaitForStartup();
+ // Startup and locking the mutex in the high priority thread aren't atomic,
+ // but pretty close. Wait a bit to let it happen.
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
+ // Release the mutex in the low priority thread. If done right, everything
+ // should finish now.
+ low.release_mutex();
+
+ // Wait for everything to finish and compute success.
+ high_thread.join();
+ busy.quit();
+ busy_thread.join();
+ low_thread.join();
+ m_success = low.success() && busy.success() && high.success();
+ }
+
+ bool success() { return m_success; }
+
+ private:
+ bool m_success = false;
+};
+
+// TODO: Fix roborio permissions to run as root.
+
+// Priority inversion test.
+TEST(MutexTest, DISABLED_PriorityInversionTest) {
+ InversionTestRunner<priority_mutex> runner;
+ std::thread runner_thread(std::ref(runner));
+ runner_thread.join();
+ EXPECT_TRUE(runner.success());
+}
+
+// Verify that the non-priority inversion mutex doesn't pass the test.
+TEST(MutexTest, DISABLED_StdMutexPriorityInversionTest) {
+ InversionTestRunner<std::mutex> runner;
+ std::thread runner_thread(std::ref(runner));
+ runner_thread.join();
+ EXPECT_FALSE(runner.success());
+}
+
+// Smoke test to make sure that mutexes lock and unlock.
+TEST(MutexTest, TryLock) {
+ priority_mutex m;
+ m.lock();
+ EXPECT_FALSE(m.try_lock());
+ m.unlock();
+ EXPECT_TRUE(m.try_lock());
+}
+
+// Priority inversion test.
+TEST(MutexTest, DISABLED_ReentrantPriorityInversionTest) {
+ InversionTestRunner<priority_recursive_mutex> runner;
+ std::thread runner_thread(std::ref(runner));
+ runner_thread.join();
+ EXPECT_TRUE(runner.success());
+}
+
+// Smoke test to make sure that mutexes lock and unlock.
+TEST(MutexTest, ReentrantTryLock) {
+ priority_recursive_mutex m;
+ m.lock();
+ EXPECT_TRUE(m.try_lock());
+ m.unlock();
+ EXPECT_TRUE(m.try_lock());
+}
+
+#endif // WPI_HAVE_PRIORITY_MUTEX
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/raw_uv_stream_test.cpp b/wpiutil/src/test/native/cpp/raw_uv_stream_test.cpp
new file mode 100644
index 0000000..d1a0f6c
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/raw_uv_stream_test.cpp
@@ -0,0 +1,58 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/raw_uv_ostream.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+TEST(RawUvStreamTest, BasicWrite) {
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os(bufs, 1024);
+ os << "12";
+ os << "34";
+ ASSERT_EQ(bufs.size(), 1u);
+ ASSERT_EQ(bufs[0].len, 4u);
+ ASSERT_EQ(bufs[0].base[0], '1');
+ ASSERT_EQ(bufs[0].base[1], '2');
+ ASSERT_EQ(bufs[0].base[2], '3');
+ ASSERT_EQ(bufs[0].base[3], '4');
+}
+
+TEST(RawUvStreamTest, BoundaryWrite) {
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os(bufs, 4);
+ ASSERT_EQ(bufs.size(), 0u);
+ os << "12";
+ ASSERT_EQ(bufs.size(), 1u);
+ os << "34";
+ ASSERT_EQ(bufs.size(), 1u);
+ os << "56";
+ ASSERT_EQ(bufs.size(), 2u);
+}
+
+TEST(RawUvStreamTest, LargeWrite) {
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os(bufs, 4);
+ os << "123456";
+ ASSERT_EQ(bufs.size(), 2u);
+ ASSERT_EQ(bufs[1].len, 2u);
+ ASSERT_EQ(bufs[1].base[0], '5');
+}
+
+TEST(RawUvStreamTest, PrevDataWrite) {
+ SmallVector<uv::Buffer, 4> bufs;
+ bufs.emplace_back(uv::Buffer::Allocate(1024));
+ raw_uv_ostream os(bufs, 1024);
+ os << "1234";
+ ASSERT_EQ(bufs.size(), 2u);
+ ASSERT_EQ(bufs[0].len, 1024u);
+ ASSERT_EQ(bufs[1].len, 4u);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sha1Test.cpp b/wpiutil/src/test/native/cpp/sha1Test.cpp
new file mode 100644
index 0000000..08d85fe
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/sha1Test.cpp
@@ -0,0 +1,92 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/*
+ test_sha1.cpp - test program of
+
+ ============
+ SHA-1 in C++
+ ============
+
+ 100% Public Domain.
+
+ Original C Code
+ -- Steve Reid <steve@edmweb.com>
+ Small changes to fit into bglibs
+ -- Bruce Guenter <bruce@untroubled.org>
+ Translation to simpler C++ Code
+ -- Volker Grabsch <vog@notjusthosting.com>
+*/
+
+#include <string>
+
+#include "gtest/gtest.h"
+#include "wpi/sha1.h"
+
+namespace wpi {
+
+/*
+ * The 3 test vectors from FIPS PUB 180-1
+ */
+
+TEST(SHA1Test, Standard1) {
+ SHA1 checksum;
+ checksum.Update("abc");
+ ASSERT_EQ(checksum.Final(), "a9993e364706816aba3e25717850c26c9cd0d89d");
+}
+
+TEST(SHA1Test, Standard2) {
+ SHA1 checksum;
+ checksum.Update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ ASSERT_EQ(checksum.Final(), "84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+}
+
+TEST(SHA1Test, Standard3) {
+ SHA1 checksum;
+ // A million repetitions of 'a'
+ for (int i = 0; i < 1000000 / 200; ++i) {
+ checksum.Update(
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+ }
+ ASSERT_EQ(checksum.Final(), "34aa973cd4c4daa4f61eeb2bdbad27316534016f");
+}
+
+/*
+ * Other tests
+ */
+
+TEST(SHA1Test, OtherNoString) {
+ SHA1 checksum;
+ ASSERT_EQ(checksum.Final(), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
+}
+
+TEST(SHA1Test, OtherEmptyString) {
+ SHA1 checksum;
+ checksum.Update("");
+ ASSERT_EQ(checksum.Final(), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
+}
+
+TEST(SHA1Test, OtherABCDE) {
+ SHA1 checksum;
+ checksum.Update("abcde");
+ ASSERT_EQ(checksum.Final(), "03de6c570bfe24bfc328ccd7ca46b76eadaf4334");
+}
+
+TEST(SHA1Test, Concurrent) {
+ // Two concurrent checksum calculations
+ SHA1 checksum1, checksum2;
+ checksum1.Update("abc");
+ ASSERT_EQ(checksum2.Final(),
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709"); /* "" */
+ ASSERT_EQ(checksum1.Final(),
+ "a9993e364706816aba3e25717850c26c9cd0d89d"); /* "abc" */
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp b/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
new file mode 100644
index 0000000..240e688
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
@@ -0,0 +1,135 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/*
+
+Sigslot, a signal-slot library
+
+https://github.com/palacaze/sigslot
+
+MIT License
+
+Copyright (c) 2017 Pierre-Antoine Lacaze
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+#include "wpi/Signal.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <type_traits>
+
+using namespace wpi::sig::trait;
+
+namespace {
+
+void f1(int, char, float) {}
+void f2(int, char, float) noexcept {}
+
+struct oo {
+ void operator()(int) {}
+ void operator()(int, char, float) {}
+};
+
+struct s {
+ static void s1(int, char, float) {}
+ static void s2(int, char, float) noexcept {}
+
+ void f1(int, char, float) {}
+ void f2(int, char, float) const {}
+ void f3(int, char, float) volatile {}
+ void f4(int, char, float) const volatile {}
+ void f5(int, char, float) noexcept {}
+ void f6(int, char, float) const noexcept {}
+ void f7(int, char, float) volatile noexcept {}
+ void f8(int, char, float) const volatile noexcept {}
+};
+
+struct o1 {
+ void operator()(int, char, float) {}
+};
+struct o2 {
+ void operator()(int, char, float) const {}
+};
+struct o3 {
+ void operator()(int, char, float) volatile {}
+};
+struct o4 {
+ void operator()(int, char, float) const volatile {}
+};
+struct o5 {
+ void operator()(int, char, float) noexcept {}
+};
+struct o6 {
+ void operator()(int, char, float) const noexcept {}
+};
+struct o7 {
+ void operator()(int, char, float) volatile noexcept {}
+};
+struct o8 {
+ void operator()(int, char, float) const volatile noexcept {}
+};
+
+using t = typelist<int, char, float>;
+
+static_assert(is_callable_v<t, decltype(f1)>, "");
+static_assert(is_callable_v<t, decltype(f2)>, "");
+static_assert(is_callable_v<t, decltype(&s::s1)>, "");
+static_assert(is_callable_v<t, decltype(&s::s2)>, "");
+static_assert(is_callable_v<t, oo>, "");
+static_assert(is_callable_v<t, decltype(&s::f1), s*>, "");
+static_assert(is_callable_v<t, decltype(&s::f2), s*>, "");
+static_assert(is_callable_v<t, decltype(&s::f3), s*>, "");
+static_assert(is_callable_v<t, decltype(&s::f4), s*>, "");
+static_assert(is_callable_v<t, decltype(&s::f5), s*>, "");
+static_assert(is_callable_v<t, decltype(&s::f6), s*>, "");
+static_assert(is_callable_v<t, decltype(&s::f7), s*>, "");
+static_assert(is_callable_v<t, decltype(&s::f8), s*>, "");
+static_assert(is_callable_v<t, o1>, "");
+static_assert(is_callable_v<t, o2>, "");
+static_assert(is_callable_v<t, o3>, "");
+static_assert(is_callable_v<t, o4>, "");
+static_assert(is_callable_v<t, o5>, "");
+static_assert(is_callable_v<t, o6>, "");
+static_assert(is_callable_v<t, o7>, "");
+static_assert(is_callable_v<t, o8>, "");
+
+} // namespace
+
+namespace wpi {
+
+TEST(Signal, FunctionTraits) {
+ auto l1 = [](int, char, float) {};
+ auto l2 = [&](int, char, float) mutable {};
+ auto l3 = [&](auto...) mutable {};
+
+ static_assert(is_callable_v<t, decltype(l1)>, "");
+ static_assert(is_callable_v<t, decltype(l2)>, "");
+ static_assert(is_callable_v<t, decltype(l3)>, "");
+
+ f1(0, '0', 0.0);
+ f2(0, '0', 0.0);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sigslot/recursive.cpp b/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
new file mode 100644
index 0000000..85ae9a9
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
@@ -0,0 +1,97 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/*
+
+Sigslot, a signal-slot library
+
+https://github.com/palacaze/sigslot
+
+MIT License
+
+Copyright (c) 2017 Pierre-Antoine Lacaze
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+#include "wpi/Signal.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace {
+
+template <typename T>
+struct object {
+ object(T i) : v{i} {} // NOLINT(runtime/explicit)
+
+ void inc_val(const T& i) {
+ if (i != v) {
+ v++;
+ sig(v);
+ }
+ }
+
+ void dec_val(const T& i) {
+ if (i != v) {
+ v--;
+ sig(v);
+ }
+ }
+
+ T v;
+ wpi::sig::Signal_r<T> sig;
+};
+
+} // namespace
+
+namespace wpi {
+
+TEST(Signal, Recursive) {
+ object<int> i1(-1);
+ object<int> i2(10);
+
+ i1.sig.connect(&object<int>::dec_val, &i2);
+ i2.sig.connect(&object<int>::inc_val, &i1);
+
+ i1.inc_val(0);
+
+ ASSERT_EQ(i1.v, i2.v);
+}
+
+TEST(Signal, SelfRecursive) {
+ int i = 0;
+
+ wpi::sig::Signal_r<int> s;
+ s.connect([&](int v) {
+ if (i < 10) {
+ i++;
+ s(v + 1);
+ }
+ });
+
+ s(0);
+
+ ASSERT_EQ(i, 10);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp b/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
new file mode 100644
index 0000000..1ebbc8e
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
@@ -0,0 +1,140 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/*
+
+Sigslot, a signal-slot library
+
+https://github.com/palacaze/sigslot
+
+MIT License
+
+Copyright (c) 2017 Pierre-Antoine Lacaze
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+#include "wpi/Signal.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+using namespace wpi::sig;
+
+namespace {
+
+int sum = 0;
+
+void f(Connection& c, int i) {
+ sum += i;
+ c.disconnect();
+}
+
+struct s {
+ static void sf(Connection& c, int i) {
+ sum += i;
+ c.disconnect();
+ }
+ void f(Connection& c, int i) {
+ sum += i;
+ c.disconnect();
+ }
+};
+
+struct o {
+ void operator()(Connection& c, int i) {
+ sum += i;
+ c.disconnect();
+ }
+};
+
+} // namespace
+
+namespace wpi {
+
+TEST(SignalExtended, FreeConnection) {
+ sum = 0;
+ Signal<int> sig;
+ sig.connect_extended(f);
+
+ sig(1);
+ ASSERT_EQ(sum, 1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+}
+
+TEST(SignalExtended, StaticConnection) {
+ sum = 0;
+ Signal<int> sig;
+ sig.connect_extended(&s::sf);
+
+ sig(1);
+ ASSERT_EQ(sum, 1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+}
+
+TEST(SignalExtended, PmfConnection) {
+ sum = 0;
+ Signal<int> sig;
+ s p;
+ sig.connect_extended(&s::f, &p);
+
+ sig(1);
+ ASSERT_EQ(sum, 1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+}
+
+TEST(SignalExtended, FunctionObjectConnection) {
+ sum = 0;
+ Signal<int> sig;
+ sig.connect_extended(o{});
+
+ sig(1);
+ ASSERT_EQ(sum, 1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+}
+
+TEST(SignalExtended, LambdaConnection) {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect_extended([&](Connection& c, int i) {
+ sum += i;
+ c.disconnect();
+ });
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ sig.connect_extended([&](Connection& c, int i) mutable {
+ sum += 2 * i;
+ c.disconnect();
+ });
+ sig(1);
+ ASSERT_EQ(sum, 3);
+ sig(1);
+ ASSERT_EQ(sum, 3);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp b/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
new file mode 100644
index 0000000..c4f7cdb
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
@@ -0,0 +1,93 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/*
+
+Sigslot, a signal-slot library
+
+https://github.com/palacaze/sigslot
+
+MIT License
+
+Copyright (c) 2017 Pierre-Antoine Lacaze
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+#include "wpi/Signal.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <array>
+#include <atomic>
+#include <thread>
+
+using namespace wpi::sig;
+
+namespace {
+
+std::atomic<int> sum{0};
+
+void f(int i) { sum += i; }
+
+void emit_many(Signal_mt<int>& sig) {
+ for (int i = 0; i < 10000; ++i) sig(1);
+}
+
+void connect_emit(Signal_mt<int>& sig) {
+ for (int i = 0; i < 100; ++i) {
+ auto s = sig.connect_scoped(f);
+ for (int j = 0; j < 100; ++j) sig(1);
+ }
+}
+
+} // namespace
+
+namespace wpi {
+
+TEST(Signal, ThreadedMix) {
+ sum = 0;
+
+ Signal_mt<int> sig;
+
+ std::array<std::thread, 10> threads;
+ for (auto& t : threads) t = std::thread(connect_emit, std::ref(sig));
+
+ for (auto& t : threads) t.join();
+}
+
+TEST(Signal, ThreadedEmission) {
+ sum = 0;
+
+ Signal_mt<int> sig;
+ sig.connect(f);
+
+ std::array<std::thread, 10> threads;
+ for (auto& t : threads) t = std::thread(emit_many, std::ref(sig));
+
+ for (auto& t : threads) t.join();
+
+ ASSERT_EQ(sum, 100000);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp b/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
new file mode 100644
index 0000000..89ffd36
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
@@ -0,0 +1,181 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/*
+
+Sigslot, a signal-slot library
+
+https://github.com/palacaze/sigslot
+
+MIT License
+
+Copyright (c) 2017 Pierre-Antoine Lacaze
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+#include "wpi/Signal.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <cmath>
+#include <sstream>
+#include <string>
+
+using namespace wpi::sig;
+
+namespace {
+
+int sum = 0;
+
+void f1(int i) { sum += i; }
+struct o1 {
+ void operator()(int i) { sum += 2 * i; }
+};
+
+struct s {
+ void f1(int i) { sum += i; }
+ void f2(int i) const { sum += 2 * i; }
+};
+
+struct oo {
+ void operator()(int i) { sum += i; }
+ void operator()(double i) { sum += std::round(4 * i); }
+};
+
+struct dummy {};
+
+static_assert(trait::is_callable_v<trait::typelist<int>, decltype(&s::f1),
+ std::shared_ptr<s>>,
+ "");
+
+} // namespace
+
+namespace wpi {
+
+TEST(Signal, TrackShared) {
+ sum = 0;
+ Signal<int> sig;
+
+ auto s1 = std::make_shared<s>();
+ sig.connect(&s::f1, s1);
+
+ auto s2 = std::make_shared<s>();
+ std::weak_ptr<s> w2 = s2;
+ sig.connect(&s::f2, w2);
+
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ s1.reset();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+
+ s2.reset();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+}
+
+TEST(Signal, TrackOther) {
+ sum = 0;
+ Signal<int> sig;
+
+ auto d1 = std::make_shared<dummy>();
+ sig.connect(f1, d1);
+
+ auto d2 = std::make_shared<dummy>();
+ std::weak_ptr<dummy> w2 = d2;
+ sig.connect(o1(), w2);
+
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ d1.reset();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+
+ d2.reset();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+}
+
+TEST(Signal, TrackOverloadedFunctionObject) {
+ sum = 0;
+ Signal<int> sig;
+ Signal<double> sig1;
+
+ auto d1 = std::make_shared<dummy>();
+ sig.connect(oo{}, d1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ d1.reset();
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ auto d2 = std::make_shared<dummy>();
+ std::weak_ptr<dummy> w2 = d2;
+ sig1.connect(oo{}, w2);
+ sig1(1);
+ ASSERT_EQ(sum, 5);
+
+ d2.reset();
+ sig1(1);
+ ASSERT_EQ(sum, 5);
+}
+
+TEST(Signal, TrackGenericLambda) {
+ std::stringstream s;
+
+ auto f = [&](auto a, auto... args) {
+ using result_t = int[];
+ s << a;
+ result_t r{
+ 1,
+ ((void)(s << args), 1)...,
+ };
+ (void)r;
+ };
+
+ Signal<int> sig1;
+ Signal<std::string> sig2;
+ Signal<double> sig3;
+
+ auto d1 = std::make_shared<dummy>();
+ sig1.connect(f, d1);
+ sig2.connect(f, d1);
+ sig3.connect(f, d1);
+
+ sig1(1);
+ sig2("foo");
+ sig3(4.1);
+ ASSERT_EQ(s.str(), "1foo4.1");
+
+ d1.reset();
+ sig1(2);
+ sig2("bar");
+ sig3(3.0);
+ ASSERT_EQ(s.str(), "1foo4.1");
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal.cpp b/wpiutil/src/test/native/cpp/sigslot/signal.cpp
new file mode 100644
index 0000000..a4f9208
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/sigslot/signal.cpp
@@ -0,0 +1,540 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/*
+
+Sigslot, a signal-slot library
+
+https://github.com/palacaze/sigslot
+
+MIT License
+
+Copyright (c) 2017 Pierre-Antoine Lacaze
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+#include "wpi/Signal.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <cmath>
+#include <sstream>
+#include <string>
+
+using namespace wpi::sig;
+
+namespace {
+
+int sum = 0;
+
+void f1(int i) { sum += i; }
+void f2(int i) /*noexcept*/ { sum += 2 * i; }
+
+struct s {
+ static void s1(int i) { sum += i; }
+ static void s2(int i) noexcept { sum += 2 * i; }
+
+ void f1(int i) { sum += i; }
+ void f2(int i) const { sum += i; }
+ void f3(int i) volatile { sum += i; }
+ void f4(int i) const volatile { sum += i; }
+ void f5(int i) noexcept { sum += i; }
+ void f6(int i) const noexcept { sum += i; }
+ void f7(int i) volatile noexcept { sum += i; }
+ void f8(int i) const volatile noexcept { sum += i; }
+};
+
+struct oo {
+ void operator()(int i) { sum += i; }
+ void operator()(double i) { sum += std::round(4 * i); }
+};
+
+struct o1 {
+ void operator()(int i) { sum += i; }
+};
+struct o2 {
+ void operator()(int i) const { sum += i; }
+};
+struct o3 {
+ void operator()(int i) volatile { sum += i; }
+};
+struct o4 {
+ void operator()(int i) const volatile { sum += i; }
+};
+struct o5 {
+ void operator()(int i) noexcept { sum += i; }
+};
+struct o6 {
+ void operator()(int i) const noexcept { sum += i; }
+};
+struct o7 {
+ void operator()(int i) volatile noexcept { sum += i; }
+};
+struct o8 {
+ void operator()(int i) const volatile noexcept { sum += i; }
+};
+
+} // namespace
+
+namespace wpi {
+
+TEST(Signal, FreeConnection) {
+ sum = 0;
+ Signal<int> sig;
+
+ auto c1 = sig.connect_connection(f1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ sig.connect(f2);
+ sig(1);
+ ASSERT_EQ(sum, 4);
+}
+
+TEST(Signal, StaticConnection) {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect(&s::s1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ sig.connect(&s::s2);
+ sig(1);
+ ASSERT_EQ(sum, 4);
+}
+
+TEST(Signal, PmfConnection) {
+ sum = 0;
+ Signal<int> sig;
+ s p;
+
+ sig.connect(&s::f1, &p);
+ sig.connect(&s::f2, &p);
+ sig.connect(&s::f3, &p);
+ sig.connect(&s::f4, &p);
+ sig.connect(&s::f5, &p);
+ sig.connect(&s::f6, &p);
+ sig.connect(&s::f7, &p);
+ sig.connect(&s::f8, &p);
+
+ sig(1);
+ ASSERT_EQ(sum, 8);
+}
+
+TEST(Signal, ConstPmfConnection) {
+ sum = 0;
+ Signal<int> sig;
+ const s p;
+
+ sig.connect(&s::f2, &p);
+ sig.connect(&s::f4, &p);
+ sig.connect(&s::f6, &p);
+ sig.connect(&s::f8, &p);
+
+ sig(1);
+ ASSERT_EQ(sum, 4);
+}
+
+TEST(Signal, FunctionObjectConnection) {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect(o1{});
+ sig.connect(o2{});
+ sig.connect(o3{});
+ sig.connect(o4{});
+ sig.connect(o5{});
+ sig.connect(o6{});
+ sig.connect(o7{});
+ sig.connect(o8{});
+
+ sig(1);
+ ASSERT_EQ(sum, 8);
+}
+
+TEST(Signal, OverloadedFunctionObjectConnection) {
+ sum = 0;
+ Signal<int> sig;
+ Signal<double> sig1;
+
+ sig.connect(oo{});
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ sig1.connect(oo{});
+ sig1(1);
+ ASSERT_EQ(sum, 5);
+}
+
+TEST(Signal, LambdaConnection) {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect([&](int i) { sum += i; });
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ sig.connect([&](int i) mutable { sum += 2 * i; });
+ sig(1);
+ ASSERT_EQ(sum, 4);
+}
+
+TEST(Signal, GenericLambdaConnection) {
+ std::stringstream s;
+
+ auto f = [&](auto a, auto... args) {
+ using result_t = int[];
+ s << a;
+ result_t r{
+ 1,
+ ((void)(s << args), 1)...,
+ };
+ (void)r;
+ };
+
+ Signal<int> sig1;
+ Signal<std::string> sig2;
+ Signal<double> sig3;
+
+ sig1.connect(f);
+ sig2.connect(f);
+ sig3.connect(f);
+ sig1(1);
+ sig2("foo");
+ sig3(4.1);
+
+ ASSERT_EQ(s.str(), "1foo4.1");
+}
+
+TEST(Signal, LvalueEmission) {
+ sum = 0;
+ Signal<int> sig;
+
+ auto c1 = sig.connect_connection(f1);
+ int v = 1;
+ sig(v);
+ ASSERT_EQ(sum, 1);
+
+ sig.connect(f2);
+ sig(v);
+ ASSERT_EQ(sum, 4);
+}
+
+TEST(Signal, Mutation) {
+ int res = 0;
+ Signal<int&> sig;
+
+ sig.connect([](int& r) { r += 1; });
+ sig(res);
+ ASSERT_EQ(res, 1);
+
+ sig.connect([](int& r) mutable { r += 2; });
+ sig(res);
+ ASSERT_EQ(res, 4);
+}
+
+TEST(Signal, CompatibleArgs) {
+ long ll = 0; // NOLINT(runtime/int)
+ std::string ss;
+ short ii = 0; // NOLINT(runtime/int)
+
+ auto f = [&](long l, const std::string& s, short i) { // NOLINT(runtime/int)
+ ll = l;
+ ss = s;
+ ii = i;
+ };
+
+ Signal<int, std::string, bool> sig;
+ sig.connect(f);
+ sig('0', "foo", true);
+
+ ASSERT_EQ(ll, 48);
+ ASSERT_EQ(ss, "foo");
+ ASSERT_EQ(ii, 1);
+}
+
+TEST(Signal, Disconnection) {
+ // test removing only connected
+ {
+ sum = 0;
+ Signal<int> sig;
+
+ auto sc = sig.connect_connection(f1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ sc.disconnect();
+ sig(1);
+ ASSERT_EQ(sum, 1);
+ }
+
+ // test removing first connected
+ {
+ sum = 0;
+ Signal<int> sig;
+
+ auto sc = sig.connect_connection(f1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ sig.connect(f2);
+ sig(1);
+ ASSERT_EQ(sum, 4);
+
+ sc.disconnect();
+ sig(1);
+ ASSERT_EQ(sum, 6);
+ }
+
+ // test removing last connected
+ {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect(f1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ auto sc = sig.connect_connection(f2);
+ sig(1);
+ ASSERT_EQ(sum, 4);
+
+ sc.disconnect();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+ }
+}
+
+TEST(Signal, ScopedConnection) {
+ sum = 0;
+ Signal<int> sig;
+
+ {
+ auto sc1 = sig.connect_scoped(f1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ auto sc2 = sig.connect_scoped(f2);
+ sig(1);
+ ASSERT_EQ(sum, 4);
+ }
+
+ sig(1);
+ ASSERT_EQ(sum, 4);
+
+ sum = 0;
+
+ {
+ ScopedConnection sc1 = sig.connect_connection(f1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ ScopedConnection sc2 = sig.connect_connection(f2);
+ sig(1);
+ ASSERT_EQ(sum, 4);
+ }
+
+ sig(1);
+ ASSERT_EQ(sum, 4);
+}
+
+TEST(Signal, ConnectionBlocking) {
+ sum = 0;
+ Signal<int> sig;
+
+ auto c1 = sig.connect_connection(f1);
+ sig.connect(f2);
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ c1.block();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+
+ c1.unblock();
+ sig(1);
+ ASSERT_EQ(sum, 8);
+}
+
+TEST(Signal, ConnectionBlocker) {
+ sum = 0;
+ Signal<int> sig;
+
+ auto c1 = sig.connect_connection(f1);
+ sig.connect(f2);
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ {
+ auto cb = c1.blocker();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+ }
+
+ sig(1);
+ ASSERT_EQ(sum, 8);
+}
+
+TEST(Signal, SignalBlocking) {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect(f1);
+ sig.connect(f2);
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ sig.block();
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ sig.unblock();
+ sig(1);
+ ASSERT_EQ(sum, 6);
+}
+
+TEST(Signal, AllDisconnection) {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect(f1);
+ sig.connect(f2);
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ sig.disconnect_all();
+ sig(1);
+ ASSERT_EQ(sum, 3);
+}
+
+TEST(Signal, ConnectionCopyingMoving) {
+ sum = 0;
+ Signal<int> sig;
+
+ auto sc1 = sig.connect_connection(f1);
+ auto sc2 = sig.connect_connection(f2);
+
+ auto sc3 = sc1;
+ auto sc4{sc2};
+
+ auto sc5 = std::move(sc3);
+ auto sc6{std::move(sc4)};
+
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ sc5.block();
+ sig(1);
+ ASSERT_EQ(sum, 5);
+
+ sc1.unblock();
+ sig(1);
+ ASSERT_EQ(sum, 8);
+
+ sc6.disconnect();
+ sig(1);
+ ASSERT_EQ(sum, 9);
+}
+
+TEST(Signal, ScopedConnectionMoving) {
+ sum = 0;
+ Signal<int> sig;
+
+ {
+ auto sc1 = sig.connect_scoped(f1);
+ sig(1);
+ ASSERT_EQ(sum, 1);
+
+ auto sc2 = sig.connect_scoped(f2);
+ sig(1);
+ ASSERT_EQ(sum, 4);
+
+ auto sc3 = std::move(sc1);
+ sig(1);
+ ASSERT_EQ(sum, 7);
+
+ auto sc4{std::move(sc2)};
+ sig(1);
+ ASSERT_EQ(sum, 10);
+ }
+
+ sig(1);
+ ASSERT_EQ(sum, 10);
+}
+
+TEST(Signal, SignalMoving) {
+ sum = 0;
+ Signal<int> sig;
+
+ sig.connect(f1);
+ sig.connect(f2);
+
+ sig(1);
+ ASSERT_EQ(sum, 3);
+
+ auto sig2 = std::move(sig);
+ sig2(1);
+ ASSERT_EQ(sum, 6);
+
+ auto sig3 = std::move(sig2);
+ sig3(1);
+ ASSERT_EQ(sum, 9);
+}
+
+template <typename T>
+struct object {
+ object();
+ object(T i) : v{i} {} // NOLINT(runtime/explicit)
+
+ const T& val() const { return v; }
+ T& val() { return v; }
+ void set_val(const T& i) {
+ if (i != v) {
+ v = i;
+ s(i);
+ }
+ }
+
+ Signal<T>& sig() { return s; }
+
+ private:
+ T v;
+ Signal<T> s;
+};
+
+TEST(Signal, Loop) {
+ object<int> i1(0);
+ object<int> i2(3);
+
+ i1.sig().connect(&object<int>::set_val, &i2);
+ i2.sig().connect(&object<int>::set_val, &i1);
+
+ i1.set_val(1);
+
+ ASSERT_EQ(i1.val(), 1);
+ ASSERT_EQ(i2.val(), 1);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/spinlock_bench.cpp b/wpiutil/src/test/native/cpp/spinlock_bench.cpp
new file mode 100644
index 0000000..2280a44
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/spinlock_bench.cpp
@@ -0,0 +1,166 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/spinlock.h" // NOLINT(build/include_order)
+
+#include <chrono>
+#include <iostream>
+#include <mutex>
+#include <thread>
+
+#include "gtest/gtest.h"
+#include "wpi/mutex.h"
+
+static std::mutex std_mutex;
+static std::recursive_mutex std_recursive_mutex;
+static wpi::mutex wpi_mutex;
+static wpi::recursive_mutex wpi_recursive_mutex;
+static wpi::spinlock spinlock;
+static wpi::recursive_spinlock1 recursive_spinlock1;
+static wpi::recursive_spinlock2 recursive_spinlock2;
+static wpi::recursive_spinlock recursive_spinlock;
+
+TEST(SpinlockTest, Benchmark) {
+ using std::chrono::duration_cast;
+ using std::chrono::high_resolution_clock;
+ using std::chrono::microseconds;
+
+ // warmup
+ std::thread thr([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 10000000; i++) {
+ std::lock_guard<std::mutex> lock(std_mutex);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ (void)start;
+ (void)stop;
+ });
+ thr.join();
+
+ std::thread thrb([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<std::mutex> lock(std_mutex);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "std::mutex sizeof: " << sizeof(std_mutex)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thrb.join();
+
+ std::thread thrb2([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<std::recursive_mutex> lock(std_recursive_mutex);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "std::recursive_mutex sizeof: " << sizeof(std_recursive_mutex)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thrb2.join();
+
+ std::thread thr2([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<wpi::mutex> lock(wpi_mutex);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "wpi::mutex sizeof: " << sizeof(wpi_mutex)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thr2.join();
+
+ std::thread thr2b([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<wpi::recursive_mutex> lock(wpi_recursive_mutex);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "wpi::recursive_mutex sizeof: " << sizeof(wpi_recursive_mutex)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thr2b.join();
+
+ std::thread thr3([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<wpi::spinlock> lock(spinlock);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "spinlock sizeof: " << sizeof(spinlock)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thr3.join();
+
+ std::thread thr4([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<wpi::recursive_spinlock1> lock(recursive_spinlock1);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "recursive_spinlock1 sizeof: " << sizeof(recursive_spinlock1)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thr4.join();
+
+ std::thread thr4b([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<wpi::recursive_spinlock2> lock(recursive_spinlock2);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "recursive_spinlock2 sizeof: " << sizeof(recursive_spinlock2)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thr4b.join();
+
+ std::thread thr4c([]() {
+ int value = 0;
+
+ auto start = high_resolution_clock::now();
+ for (int i = 0; i < 1000000; i++) {
+ std::lock_guard<wpi::recursive_spinlock> lock(recursive_spinlock);
+ ++value;
+ }
+ auto stop = high_resolution_clock::now();
+ std::cout << "recursive_spinlock sizeof: " << sizeof(recursive_spinlock)
+ << " time: " << duration_cast<microseconds>(stop - start).count()
+ << " value: " << value << "\n";
+ });
+ thr4c.join();
+}
diff --git a/wpiutil/src/test/native/cpp/test_optional.cpp b/wpiutil/src/test/native/cpp/test_optional.cpp
new file mode 100644
index 0000000..e64f0c2
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/test_optional.cpp
@@ -0,0 +1,1523 @@
+// Copyright (C) 2011 - 2017 Andrzej Krzemienski.
+//
+// Use, modification, and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// The idea and interface is based on Boost.Optional library
+// authored by Fernando Luis Cacciola Carballal
+
+# include "wpi/optional.h"
+
+#include "gtest/gtest.h"
+
+# include <vector>
+# include <iostream>
+# include <functional>
+# include <complex>
+
+
+
+enum State
+{
+ sDefaultConstructed,
+ sValueCopyConstructed,
+ sValueMoveConstructed,
+ sCopyConstructed,
+ sMoveConstructed,
+ sMoveAssigned,
+ sCopyAssigned,
+ sValueCopyAssigned,
+ sValueMoveAssigned,
+ sMovedFrom,
+ sValueConstructed
+};
+
+struct OracleVal
+{
+ State s;
+ int i;
+ OracleVal(int i = 0) : s(sValueConstructed), i(i) {}
+};
+
+struct Oracle
+{
+ State s;
+ OracleVal val;
+
+ Oracle() : s(sDefaultConstructed) {}
+ Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {}
+ Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;}
+ Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {}
+ Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;}
+
+ Oracle& operator=(const OracleVal& v) { s = sValueCopyConstructed; val = v; return *this; }
+ Oracle& operator=(OracleVal&& v) { s = sValueMoveConstructed; val = std::move(v); v.s = sMovedFrom; return *this; }
+ Oracle& operator=(const Oracle& o) { s = sCopyConstructed; val = o.val; return *this; }
+ Oracle& operator=(Oracle&& o) { s = sMoveConstructed; val = std::move(o.val); o.s = sMovedFrom; return *this; }
+};
+
+struct Guard
+{
+ std::string val;
+ Guard() : val{} {}
+ explicit Guard(std::string s, int = 0) : val(s) {}
+ Guard(const Guard&) = delete;
+ Guard(Guard&&) = delete;
+ void operator=(const Guard&) = delete;
+ void operator=(Guard&&) = delete;
+};
+
+struct ExplicitStr
+{
+ std::string s;
+ explicit ExplicitStr(const char* chp) : s(chp) {};
+};
+
+struct Date
+{
+ int i;
+ Date() = delete;
+ Date(int i) : i{i} {};
+ Date(Date&& d) : i(d.i) { d.i = 0; }
+ Date(const Date&) = delete;
+ Date& operator=(const Date&) = delete;
+ Date& operator=(Date&& d) { i = d.i; d.i = 0; return *this;};
+};
+
+bool operator==( Oracle const& a, Oracle const& b ) { return a.val.i == b.val.i; }
+bool operator!=( Oracle const& a, Oracle const& b ) { return a.val.i != b.val.i; }
+
+
+namespace tr2 = wpi;
+
+
+TEST(Optional, disengaged_ctor)
+{
+ tr2::optional<int> o1;
+ assert (!o1);
+
+ tr2::optional<int> o2 = tr2::nullopt;
+ assert (!o2);
+
+ tr2::optional<int> o3 = o2;
+ assert (!o3);
+
+ assert (o1 == tr2::nullopt);
+ assert (o1 == tr2::optional<int>{});
+ assert (!o1);
+ assert (bool(o1) == false);
+
+ assert (o2 == tr2::nullopt);
+ assert (o2 == tr2::optional<int>{});
+ assert (!o2);
+ assert (bool(o2) == false);
+
+ assert (o3 == tr2::nullopt);
+ assert (o3 == tr2::optional<int>{});
+ assert (!o3);
+ assert (bool(o3) == false);
+
+ assert (o1 == o2);
+ assert (o2 == o1);
+ assert (o1 == o3);
+ assert (o3 == o1);
+ assert (o2 == o3);
+ assert (o3 == o2);
+}
+
+
+TEST(Optional, value_ctor)
+{
+ OracleVal v;
+ tr2::optional<Oracle> oo1(v);
+ assert (oo1 != tr2::nullopt);
+ assert (oo1 != tr2::optional<Oracle>{});
+ assert (oo1 == tr2::optional<Oracle>{v});
+ assert (!!oo1);
+ assert (bool(oo1));
+ // NA: assert (oo1->s == sValueCopyConstructed);
+ assert (oo1->s == sMoveConstructed);
+ assert (v.s == sValueConstructed);
+
+ tr2::optional<Oracle> oo2(std::move(v));
+ assert (oo2 != tr2::nullopt);
+ assert (oo2 != tr2::optional<Oracle>{});
+ assert (oo2 == oo1);
+ assert (!!oo2);
+ assert (bool(oo2));
+ // NA: assert (oo2->s == sValueMoveConstructed);
+ assert (oo2->s == sMoveConstructed);
+ assert (v.s == sMovedFrom);
+
+ {
+ OracleVal v;
+ tr2::optional<Oracle> oo1{tr2::in_place, v};
+ assert (oo1 != tr2::nullopt);
+ assert (oo1 != tr2::optional<Oracle>{});
+ assert (oo1 == tr2::optional<Oracle>{v});
+ assert (!!oo1);
+ assert (bool(oo1));
+ assert (oo1->s == sValueCopyConstructed);
+ assert (v.s == sValueConstructed);
+
+ tr2::optional<Oracle> oo2{tr2::in_place, std::move(v)};
+ assert (oo2 != tr2::nullopt);
+ assert (oo2 != tr2::optional<Oracle>{});
+ assert (oo2 == oo1);
+ assert (!!oo2);
+ assert (bool(oo2));
+ assert (oo2->s == sValueMoveConstructed);
+ assert (v.s == sMovedFrom);
+ }
+}
+
+
+TEST(Optional, assignment)
+{
+ tr2::optional<int> oi;
+ oi = tr2::optional<int>{1};
+ assert (*oi == 1);
+
+ oi = tr2::nullopt;
+ assert (!oi);
+
+ oi = 2;
+ assert (*oi == 2);
+
+ oi = {};
+ assert (!oi);
+}
+
+
+template <class T>
+struct MoveAware
+{
+ T val;
+ bool moved;
+ MoveAware(T val) : val(val), moved(false) {}
+ MoveAware(MoveAware const&) = delete;
+ MoveAware(MoveAware&& rhs) : val(rhs.val), moved(rhs.moved) {
+ rhs.moved = true;
+ }
+ MoveAware& operator=(MoveAware const&) = delete;
+ MoveAware& operator=(MoveAware&& rhs) {
+ val = (rhs.val);
+ moved = (rhs.moved);
+ rhs.moved = true;
+ return *this;
+ }
+};
+
+TEST(Optional, moved_from_state)
+{
+ // first, test mock:
+ MoveAware<int> i{1}, j{2};
+ assert (i.val == 1);
+ assert (!i.moved);
+ assert (j.val == 2);
+ assert (!j.moved);
+
+ MoveAware<int> k = std::move(i);
+ assert (k.val == 1);
+ assert (!k.moved);
+ assert (i.val == 1);
+ assert (i.moved);
+
+ k = std::move(j);
+ assert (k.val == 2);
+ assert (!k.moved);
+ assert (j.val == 2);
+ assert (j.moved);
+
+ // now, test optional
+ tr2::optional<MoveAware<int>> oi{1}, oj{2};
+ assert (oi);
+ assert (!oi->moved);
+ assert (oj);
+ assert (!oj->moved);
+
+ tr2::optional<MoveAware<int>> ok = std::move(oi);
+ assert (ok);
+ assert (!ok->moved);
+ assert (oi);
+ assert (oi->moved);
+
+ ok = std::move(oj);
+ assert (ok);
+ assert (!ok->moved);
+ assert (oj);
+ assert (oj->moved);
+}
+
+
+TEST(Optional, copy_move_ctor_optional_int)
+{
+ tr2::optional<int> oi;
+ tr2::optional<int> oj = oi;
+
+ assert (!oj);
+ assert (oj == oi);
+ assert (oj == tr2::nullopt);
+ assert (!bool(oj));
+
+ oi = 1;
+ tr2::optional<int> ok = oi;
+ assert (!!ok);
+ assert (bool(ok));
+ assert (ok == oi);
+ assert (ok != oj);
+ assert (*ok == 1);
+
+ tr2::optional<int> ol = std::move(oi);
+ assert (!!ol);
+ assert (bool(ol));
+ assert (ol == oi);
+ assert (ol != oj);
+ assert (*ol == 1);
+}
+
+
+TEST(Optional, optional_optional)
+{
+ tr2::optional<tr2::optional<int>> oi1 = tr2::nullopt;
+ assert (oi1 == tr2::nullopt);
+ assert (!oi1);
+
+ {
+ tr2::optional<tr2::optional<int>> oi2 {tr2::in_place};
+ assert (oi2 != tr2::nullopt);
+ assert (bool(oi2));
+ assert (*oi2 == tr2::nullopt);
+ //assert (!(*oi2));
+ //std::cout << typeid(**oi2).name() << std::endl;
+ }
+
+ {
+ tr2::optional<tr2::optional<int>> oi2 {tr2::in_place, tr2::nullopt};
+ assert (oi2 != tr2::nullopt);
+ assert (bool(oi2));
+ assert (*oi2 == tr2::nullopt);
+ assert (!*oi2);
+ }
+
+ {
+ tr2::optional<tr2::optional<int>> oi2 {tr2::optional<int>{}};
+ assert (oi2 != tr2::nullopt);
+ assert (bool(oi2));
+ assert (*oi2 == tr2::nullopt);
+ assert (!*oi2);
+ }
+
+ tr2::optional<int> oi;
+ auto ooi = tr2::make_optional(oi);
+ static_assert( std::is_same<tr2::optional<tr2::optional<int>>, decltype(ooi)>::value, "");
+
+}
+
+TEST(Optional, example_guard)
+{
+ using namespace tr2;
+ //FAILS: optional<Guard> ogx(Guard("res1"));
+ //FAILS: optional<Guard> ogx = "res1";
+ //FAILS: optional<Guard> ogx("res1");
+ optional<Guard> oga; // Guard is non-copyable (and non-moveable)
+ optional<Guard> ogb(in_place, "res1"); // initialzes the contained value with "res1"
+ assert (bool(ogb));
+ assert (ogb->val == "res1");
+
+ optional<Guard> ogc(in_place); // default-constructs the contained value
+ assert (bool(ogc));
+ assert (ogc->val == "");
+
+ oga.emplace("res1"); // initialzes the contained value with "res1"
+ assert (bool(oga));
+ assert (oga->val == "res1");
+
+ oga.emplace(); // destroys the contained value and
+ // default-constructs the new one
+ assert (bool(oga));
+ assert (oga->val == "");
+
+ oga = nullopt; // OK: make disengaged the optional Guard
+ assert (!(oga));
+ //FAILS: ogb = {}; // ERROR: Guard is not Moveable
+}
+
+
+void process(){}
+void process(int ){}
+void processNil(){}
+
+
+TEST(Optional, example1)
+{
+ using namespace tr2;
+ optional<int> oi; // create disengaged object
+ optional<int> oj = nullopt; // alternative syntax
+ oi = oj; // assign disengaged object
+ optional<int> ok = oj; // ok is disengaged
+
+ if (oi) assert(false); // 'if oi is engaged...'
+ if (!oi) assert(true); // 'if oi is disengaged...'
+
+ if (oi != nullopt) assert(false); // 'if oi is engaged...'
+ if (oi == nullopt) assert(true); // 'if oi is disengaged...'
+
+ assert(oi == ok); // two disengaged optionals compare equal
+
+ ///////////////////////////////////////////////////////////////////////////
+ optional<int> ol{1}; // ol is engaged; its contained value is 1
+ ok = 2; // ok becomes engaged; its contained value is 2
+ oj = ol; // oj becomes engaged; its contained value is 1
+
+ assert(oi != ol); // disengaged != engaged
+ assert(ok != ol); // different contained values
+ assert(oj == ol); // same contained value
+ assert(oi < ol); // disengaged < engaged
+ assert(ol < ok); // less by contained value
+
+ /////////////////////////////////////////////////////////////////////////////
+ optional<int> om{1}; // om is engaged; its contained value is 1
+ optional<int> on = om; // on is engaged; its contained value is 1
+ om = 2; // om is engaged; its contained value is 2
+ assert (on != om); // on still contains 3. They are not pointers
+
+ /////////////////////////////////////////////////////////////////////////////
+ int i = *ol; // i obtains the value contained in ol
+ assert (i == 1);
+ *ol = 9; // the object contained in ol becomes 9
+ assert(*ol == 9);
+ assert(ol == make_optional(9));
+
+ ///////////////////////////////////
+ int p = 1;
+ optional<int> op = p;
+ assert(*op == 1);
+ p = 2;
+ assert(*op == 1); // value contained in op is separated from p
+
+ ////////////////////////////////
+ if (ol)
+ process(*ol); // use contained value if present
+ else
+ process(); // proceed without contained value
+
+ if (!om)
+ processNil();
+ else
+ process(*om);
+
+ /////////////////////////////////////////
+ process(ol.value_or(0)); // use 0 if ol is disengaged
+
+ ////////////////////////////////////////////
+ ok = nullopt; // if ok was engaged calls T's dtor
+ oj = {}; // assigns a temporary disengaged optional
+}
+
+
+TEST(Optional, example_guard2)
+{
+ using wpi::optional;
+ const optional<int> c = 4;
+ int i = *c; // i becomes 4
+ assert (i == 4);
+ // FAILS: *c = i; // ERROR: cannot assign to const int&
+}
+
+
+TEST(Optional, example_ref)
+{
+ using namespace wpi;
+ int i = 1;
+ int j = 2;
+ optional<int&> ora; // disengaged optional reference to int
+ optional<int&> orb = i; // contained reference refers to object i
+
+ *orb = 3; // i becomes 3
+ // FAILS: ora = j; // ERROR: optional refs do not have assignment from T
+ // FAILS: ora = {j}; // ERROR: optional refs do not have copy/move assignment
+ // FAILS: ora = orb; // ERROR: no copy/move assignment
+ ora.emplace(j); // OK: contained reference refers to object j
+ ora.emplace(i); // OK: contained reference now refers to object i
+
+ ora = nullopt; // OK: ora becomes disengaged
+}
+
+
+template <typename T>
+T getValue( tr2::optional<T> newVal = tr2::nullopt, tr2::optional<T&> storeHere = tr2::nullopt )
+{
+ T cached{};
+
+ if (newVal) {
+ cached = *newVal;
+
+ if (storeHere) {
+ *storeHere = *newVal; // LEGAL: assigning T to T
+ }
+ }
+ return cached;
+}
+
+TEST(Optional, example_optional_arg)
+{
+ int iii = 0;
+ iii = getValue<int>(iii, iii);
+ iii = getValue<int>(iii);
+ iii = getValue<int>();
+
+ {
+ using namespace wpi;
+ optional<Guard> grd1{in_place, "res1", 1}; // guard 1 initialized
+ optional<Guard> grd2;
+
+ grd2.emplace("res2", 2); // guard 2 initialized
+ grd1 = nullopt; // guard 1 released
+
+ } // guard 2 released (in dtor)
+}
+
+
+std::tuple<Date, Date, Date> getStartMidEnd() { return std::tuple<Date, Date, Date>{Date{1}, Date{2}, Date{3}}; }
+void run(Date const&, Date const&, Date const&) {}
+
+TEST(Optional, example_date)
+{
+ using namespace wpi;
+ optional<Date> start, mid, end; // Date doesn't have default ctor (no good default date)
+
+ std::tie(start, mid, end) = getStartMidEnd();
+ run(*start, *mid, *end);
+}
+
+
+wpi::optional<char> readNextChar(){ return{}; }
+
+void run(wpi::optional<std::string>) {}
+void run(std::complex<double>) {}
+
+
+template <class T>
+void assign_norebind(tr2::optional<T&>& optref, T& obj)
+{
+ if (optref) *optref = obj;
+ else optref.emplace(obj);
+}
+
+template <typename T> void unused(T&&) {}
+
+TEST(Optional, example_conceptual_model)
+{
+ using namespace wpi;
+
+ optional<int> oi = 0;
+ optional<int> oj = 1;
+ optional<int> ok = nullopt;
+
+ oi = 1;
+ oj = nullopt;
+ ok = 0;
+
+ unused(oi == nullopt);
+ unused(oj == 0);
+ unused(ok == 1);
+}
+
+TEST(Optional, example_rationale)
+{
+ using namespace wpi;
+ if (optional<char> ch = readNextChar()) {
+ unused(ch);
+ // ...
+ }
+
+ //////////////////////////////////
+ optional<int> opt1 = nullopt;
+ optional<int> opt2 = {};
+
+ opt1 = nullopt;
+ opt2 = {};
+
+ if (opt1 == nullopt) {}
+ if (!opt2) {}
+ if (opt2 == optional<int>{}) {}
+
+
+
+ ////////////////////////////////
+
+ run(nullopt); // pick the second overload
+ // FAILS: run({}); // ambiguous
+
+ if (opt1 == nullopt) {} // fine
+ // FAILS: if (opt2 == {}) {} // ilegal
+
+ ////////////////////////////////
+ assert (optional<unsigned>{} < optional<unsigned>{0});
+ assert (optional<unsigned>{0} < optional<unsigned>{1});
+ assert (!(optional<unsigned>{} < optional<unsigned>{}) );
+ assert (!(optional<unsigned>{1} < optional<unsigned>{1}));
+
+ assert (optional<unsigned>{} != optional<unsigned>{0});
+ assert (optional<unsigned>{0} != optional<unsigned>{1});
+ assert (optional<unsigned>{} == optional<unsigned>{} );
+ assert (optional<unsigned>{0} == optional<unsigned>{0});
+
+ /////////////////////////////////
+ optional<int> o;
+ o = make_optional(1); // copy/move assignment
+ o = 1; // assignment from T
+ o.emplace(1); // emplacement
+
+ ////////////////////////////////////
+ int isas = 0, i = 9;
+ optional<int&> asas = i;
+ assign_norebind(asas, isas);
+
+ /////////////////////////////////////
+ ////tr2::optional<std::vector<int>> ov2 = {2, 3};
+ ////assert (bool(ov2));
+ ////assert ((*ov2)[1] == 3);
+ ////
+ ////////////////////////////////
+ ////std::vector<int> v = {1, 2, 4, 8};
+ ////optional<std::vector<int>> ov = {1, 2, 4, 8};
+
+ ////assert (v == *ov);
+ ////
+ ////ov = {1, 2, 4, 8};
+
+ ////std::allocator<int> a;
+ ////optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a };
+
+ ////assert (ou == ov);
+
+ //////////////////////////////
+ // inconvenient syntax:
+ {
+
+ tr2::optional<std::vector<int>> ov2{tr2::in_place, {2, 3}};
+
+ assert (bool(ov2));
+ assert ((*ov2)[1] == 3);
+
+ ////////////////////////////
+
+ std::vector<int> v = {1, 2, 4, 8};
+ optional<std::vector<int>> ov{tr2::in_place, {1, 2, 4, 8}};
+
+ assert (v == *ov);
+
+ ov.emplace({1, 2, 4, 8});
+/*
+ std::allocator<int> a;
+ optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a };
+
+ assert (ou == ov);
+*/
+ }
+
+ /////////////////////////////////
+ {
+ typedef int T;
+ optional<optional<T>> ot {in_place};
+ optional<optional<T>> ou {in_place, nullopt};
+ optional<optional<T>> ov {optional<T>{}};
+
+ (void) ot;
+ (void) ou;
+
+ optional<int> oi;
+ auto ooi = make_optional(oi);
+ static_assert( std::is_same<optional<optional<int>>, decltype(ooi)>::value, "");
+ }
+}
+
+
+bool fun(std::string , wpi::optional<int> oi = wpi::nullopt)
+{
+ return bool(oi);
+}
+
+TEST(Optional, example_converting_ctor)
+{
+ using namespace wpi;
+
+ assert (true == fun("dog", 2));
+ assert (false == fun("dog"));
+ assert (false == fun("dog", nullopt)); // just to be explicit
+}
+
+
+TEST(Optional, bad_comparison)
+{
+ tr2::optional<int> oi, oj;
+ int i;
+ bool b = (oi == oj);
+ b = (oi >= i);
+ b = (oi == i);
+ unused(b);
+}
+
+
+//// NOT APPLICABLE ANYMORE
+////TEST(Optional, perfect_ctor)
+////{
+//// //tr2::optional<std::string> ois = "OS";
+//// assert (*ois == "OS");
+////
+//// // FAILS: tr2::optional<ExplicitStr> oes = "OS";
+//// tr2::optional<ExplicitStr> oes{"OS"};
+//// assert (oes->s == "OS");
+////}
+
+TEST(Optional, value_or)
+{
+ tr2::optional<int> oi = 1;
+ int i = oi.value_or(0);
+ assert (i == 1);
+
+ oi = tr2::nullopt;
+ assert (oi.value_or(3) == 3);
+
+ tr2::optional<std::string> os{"AAA"};
+ assert (os.value_or("BBB") == "AAA");
+ os = {};
+ assert (os.value_or("BBB") == "BBB");
+}
+
+TEST(Optional, reset)
+{
+ using namespace wpi;
+ optional<int> oi {1};
+ oi.reset();
+ assert (!oi);
+
+ int i = 1;
+ optional<const int&> oir {i};
+ oir.reset();
+ assert (!oir);
+}
+
+TEST(Optional, mixed_order)
+{
+ using namespace wpi;
+
+ optional<int> oN {nullopt};
+ optional<int> o0 {0};
+ optional<int> o1 {1};
+
+ assert ( (oN < 0));
+ assert ( (oN < 1));
+ assert (!(o0 < 0));
+ assert ( (o0 < 1));
+ assert (!(o1 < 0));
+ assert (!(o1 < 1));
+
+ assert (!(oN >= 0));
+ assert (!(oN >= 1));
+ assert ( (o0 >= 0));
+ assert (!(o0 >= 1));
+ assert ( (o1 >= 0));
+ assert ( (o1 >= 1));
+
+ assert (!(oN > 0));
+ assert (!(oN > 1));
+ assert (!(o0 > 0));
+ assert (!(o0 > 1));
+ assert ( (o1 > 0));
+ assert (!(o1 > 1));
+
+ assert ( (oN <= 0));
+ assert ( (oN <= 1));
+ assert ( (o0 <= 0));
+ assert ( (o0 <= 1));
+ assert (!(o1 <= 0));
+ assert ( (o1 <= 1));
+
+ assert ( (0 > oN));
+ assert ( (1 > oN));
+ assert (!(0 > o0));
+ assert ( (1 > o0));
+ assert (!(0 > o1));
+ assert (!(1 > o1));
+
+ assert (!(0 <= oN));
+ assert (!(1 <= oN));
+ assert ( (0 <= o0));
+ assert (!(1 <= o0));
+ assert ( (0 <= o1));
+ assert ( (1 <= o1));
+
+ assert (!(0 < oN));
+ assert (!(1 < oN));
+ assert (!(0 < o0));
+ assert (!(1 < o0));
+ assert ( (0 < o1));
+ assert (!(1 < o1));
+
+ assert ( (0 >= oN));
+ assert ( (1 >= oN));
+ assert ( (0 >= o0));
+ assert ( (1 >= o0));
+ assert (!(0 >= o1));
+ assert ( (1 >= o1));
+}
+
+struct BadRelops
+{
+ int i;
+};
+
+constexpr bool operator<(BadRelops a, BadRelops b) { return a.i < b.i; }
+constexpr bool operator>(BadRelops a, BadRelops b) { return a.i < b.i; } // intentional error!
+
+TEST(Optional, bad_relops)
+{
+ using namespace wpi;
+ BadRelops a{1}, b{2};
+ assert (a < b);
+ assert (a > b);
+
+ optional<BadRelops> oa = a, ob = b;
+ assert (oa < ob);
+ assert (!(oa > ob));
+
+ assert (oa < b);
+ assert (oa > b);
+
+ optional<BadRelops&> ra = a, rb = b;
+ assert (ra < rb);
+ assert (!(ra > rb));
+
+ assert (ra < b);
+ assert (ra > b);
+}
+
+
+TEST(Optional, mixed_equality)
+{
+ using namespace wpi;
+
+ assert (make_optional(0) == 0);
+ assert (make_optional(1) == 1);
+ assert (make_optional(0) != 1);
+ assert (make_optional(1) != 0);
+
+ optional<int> oN {nullopt};
+ optional<int> o0 {0};
+ optional<int> o1 {1};
+
+ assert (o0 == 0);
+ assert ( 0 == o0);
+ assert (o1 == 1);
+ assert ( 1 == o1);
+ assert (o1 != 0);
+ assert ( 0 != o1);
+ assert (o0 != 1);
+ assert ( 1 != o0);
+
+ assert ( 1 != oN);
+ assert ( 0 != oN);
+ assert (oN != 1);
+ assert (oN != 0);
+ assert (!( 1 == oN));
+ assert (!( 0 == oN));
+ assert (!(oN == 1));
+ assert (!(oN == 0));
+
+ std::string cat{"cat"}, dog{"dog"};
+ optional<std::string> oNil{}, oDog{"dog"}, oCat{"cat"};
+
+ assert (oCat == cat);
+ assert ( cat == oCat);
+ assert (oDog == dog);
+ assert ( dog == oDog);
+ assert (oDog != cat);
+ assert ( cat != oDog);
+ assert (oCat != dog);
+ assert ( dog != oCat);
+
+ assert ( dog != oNil);
+ assert ( cat != oNil);
+ assert (oNil != dog);
+ assert (oNil != cat);
+ assert (!( dog == oNil));
+ assert (!( cat == oNil));
+ assert (!(oNil == dog));
+ assert (!(oNil == cat));
+}
+
+TEST(Optional, const_propagation)
+{
+ using namespace wpi;
+
+ optional<int> mmi{0};
+ static_assert(std::is_same<decltype(*mmi), int&>::value, "WTF");
+
+ const optional<int> cmi{0};
+ static_assert(std::is_same<decltype(*cmi), const int&>::value, "WTF");
+
+ optional<const int> mci{0};
+ static_assert(std::is_same<decltype(*mci), const int&>::value, "WTF");
+
+ optional<const int> cci{0};
+ static_assert(std::is_same<decltype(*cci), const int&>::value, "WTF");
+}
+
+
+static_assert(std::is_base_of<std::logic_error, wpi::bad_optional_access>::value, "");
+
+TEST(Optional, safe_value)
+{
+ using namespace wpi;
+
+ try {
+ optional<int> ovN{}, ov1{1};
+
+ int& r1 = ov1.value();
+ assert (r1 == 1);
+
+ try {
+ ovN.value();
+ assert (false);
+ }
+ catch (bad_optional_access const&) {
+ }
+
+ { // ref variant
+ int i1 = 1;
+ optional<int&> orN{}, or1{i1};
+
+ int& r2 = or1.value();
+ assert (r2 == 1);
+
+ try {
+ orN.value();
+ assert (false);
+ }
+ catch (bad_optional_access const&) {
+ }
+ }
+ }
+ catch(...) {
+ assert (false);
+ }
+}
+
+TEST(Optional, optional_ref)
+{
+ using namespace tr2;
+ // FAILS: optional<int&&> orr;
+ // FAILS: optional<nullopt_t&> on;
+ int i = 8;
+ optional<int&> ori;
+ assert (!ori);
+ ori.emplace(i);
+ assert (bool(ori));
+ assert (*ori == 8);
+ assert (&*ori == &i);
+ *ori = 9;
+ assert (i == 9);
+
+ // FAILS: int& ir = ori.value_or(i);
+ int ii = ori.value_or(i);
+ assert (ii == 9);
+ ii = 7;
+ assert (*ori == 9);
+
+ int j = 22;
+ auto&& oj = make_optional(std::ref(j));
+ *oj = 23;
+ assert (&*oj == &j);
+ assert (j == 23);
+}
+
+TEST(Optional, optional_ref_const_propagation)
+{
+ using namespace wpi;
+
+ int i = 9;
+ const optional<int&> mi = i;
+ int& r = *mi;
+ optional<const int&> ci = i;
+ static_assert(std::is_same<decltype(*mi), int&>::value, "WTF");
+ static_assert(std::is_same<decltype(*ci), const int&>::value, "WTF");
+
+ unused(r);
+}
+
+TEST(Optional, optional_ref_assign)
+{
+ using namespace wpi;
+
+ int i = 9;
+ optional<int&> ori = i;
+
+ int j = 1;
+ ori = optional<int&>{j};
+ ori = {j};
+ // FAILS: ori = j;
+
+ optional<int&> orx = ori;
+ ori = orx;
+
+ optional<int&> orj = j;
+
+ assert (ori);
+ assert (*ori == 1);
+ assert (ori == orj);
+ assert (i == 9);
+
+ *ori = 2;
+ assert (*ori == 2);
+ assert (ori == 2);
+ assert (2 == ori);
+ assert (ori != 3);
+
+ assert (ori == orj);
+ assert (j == 2);
+ assert (i == 9);
+
+ ori = {};
+ assert (!ori);
+ assert (ori != orj);
+ assert (j == 2);
+ assert (i == 9);
+}
+
+TEST(Optional, optional_swap)
+{
+ namespace tr2 = wpi;
+ tr2::optional<int> oi {1}, oj {};
+ swap(oi, oj);
+ assert (oj);
+ assert (*oj == 1);
+ assert (!oi);
+ static_assert(noexcept(swap(oi, oj)), "swap() is not noexcept");
+}
+
+
+TEST(Optional, optional_ref_swap)
+{
+ using namespace wpi;
+ int i = 0;
+ int j = 1;
+ optional<int&> oi = i;
+ optional<int&> oj = j;
+
+ assert (&*oi == &i);
+ assert (&*oj == &j);
+
+ swap(oi, oj);
+ assert (&*oi == &j);
+ assert (&*oj == &i);
+}
+
+TEST(Optional, optional_initialization)
+{
+ using namespace tr2;
+ using std::string;
+ string s = "STR";
+
+ optional<string> os{s};
+ optional<string> ot = s;
+ optional<string> ou{"STR"};
+ optional<string> ov = string{"STR"};
+
+}
+
+#include <unordered_set>
+
+TEST(Optional, optional_hashing)
+{
+ using namespace tr2;
+ using std::string;
+
+ std::hash<int> hi;
+ std::hash<optional<int>> hoi;
+ std::hash<string> hs;
+ std::hash<optional<string>> hos;
+
+ assert (hi(0) == hoi(optional<int>{0}));
+ assert (hi(1) == hoi(optional<int>{1}));
+ assert (hi(3198) == hoi(optional<int>{3198}));
+
+ assert (hs("") == hos(optional<string>{""}));
+ assert (hs("0") == hos(optional<string>{"0"}));
+ assert (hs("Qa1#") == hos(optional<string>{"Qa1#"}));
+
+ std::unordered_set<optional<string>> set;
+ assert(set.find({"Qa1#"}) == set.end());
+
+ set.insert({"0"});
+ assert(set.find({"Qa1#"}) == set.end());
+
+ set.insert({"Qa1#"});
+ assert(set.find({"Qa1#"}) != set.end());
+}
+
+
+// optional_ref_emulation
+template <class T>
+struct generic
+{
+ typedef T type;
+};
+
+template <class U>
+struct generic<U&>
+{
+ typedef std::reference_wrapper<U> type;
+};
+
+template <class T>
+using Generic = typename generic<T>::type;
+
+template <class X>
+bool generic_fun()
+{
+ wpi::optional<Generic<X>> op;
+ return bool(op);
+}
+
+TEST(Optional, optional_ref_emulation)
+{
+ using namespace wpi;
+ optional<Generic<int>> oi = 1;
+ assert (*oi == 1);
+
+ int i = 8;
+ int j = 4;
+ optional<Generic<int&>> ori {i};
+ assert (*ori == 8);
+ assert ((void*)&*ori != (void*)&i); // !DIFFERENT THAN optional<T&>
+
+ *ori = j;
+ assert (*ori == 4);
+}
+
+
+TEST(Optional, moved_on_value_or)
+{
+ using namespace tr2;
+ optional<Oracle> oo{in_place};
+
+ assert (oo);
+ assert (oo->s == sDefaultConstructed);
+
+ Oracle o = std::move(oo).value_or( Oracle{OracleVal{}} );
+ assert (oo);
+ assert (oo->s == sMovedFrom);
+ assert (o.s == sMoveConstructed);
+
+ optional<MoveAware<int>> om {in_place, 1};
+ assert (om);
+ assert (om->moved == false);
+
+ /*MoveAware<int> m =*/ std::move(om).value_or( MoveAware<int>{1} );
+ assert (om);
+ assert (om->moved == true);
+
+# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
+ {
+ Date d = optional<Date>{in_place, 1}.value();
+ assert (d.i); // to silence compiler warning
+
+ Date d2 = *optional<Date>{in_place, 1};
+ assert (d2.i); // to silence compiler warning
+ }
+# endif
+}
+
+
+TEST(Optional, optional_ref_hashing)
+{
+ using namespace tr2;
+ using std::string;
+
+ std::hash<int> hi;
+ std::hash<optional<int&>> hoi;
+ std::hash<string> hs;
+ std::hash<optional<string&>> hos;
+
+ int i0 = 0;
+ int i1 = 1;
+ assert (hi(0) == hoi(optional<int&>{i0}));
+ assert (hi(1) == hoi(optional<int&>{i1}));
+
+ string s{""};
+ string s0{"0"};
+ string sCAT{"CAT"};
+ assert (hs("") == hos(optional<string&>{s}));
+ assert (hs("0") == hos(optional<string&>{s0}));
+ assert (hs("CAT") == hos(optional<string&>{sCAT}));
+
+ std::unordered_set<optional<string&>> set;
+ assert(set.find({sCAT}) == set.end());
+
+ set.insert({s0});
+ assert(set.find({sCAT}) == set.end());
+
+ set.insert({sCAT});
+ assert(set.find({sCAT}) != set.end());
+}
+
+struct Combined
+{
+ int m = 0;
+ int n = 1;
+
+ constexpr Combined() : m{5}, n{6} {}
+ constexpr Combined(int m, int n) : m{m}, n{n} {}
+};
+
+struct Nasty
+{
+ int m = 0;
+ int n = 1;
+
+ constexpr Nasty() : m{5}, n{6} {}
+ constexpr Nasty(int m, int n) : m{m}, n{n} {}
+
+ int operator&() { return n; }
+ int operator&() const { return n; }
+};
+
+TEST(Optional, arrow_operator)
+{
+ using namespace wpi;
+
+ optional<Combined> oc1{in_place, 1, 2};
+ assert (oc1);
+ assert (oc1->m == 1);
+ assert (oc1->n == 2);
+
+ optional<Nasty> on{in_place, 1, 2};
+ assert (on);
+ assert (on->m == 1);
+ assert (on->n == 2);
+}
+
+TEST(Optional, arrow_wit_optional_ref)
+{
+ using namespace wpi;
+
+ Combined c{1, 2};
+ optional<Combined&> oc = c;
+ assert (oc);
+ assert (oc->m == 1);
+ assert (oc->n == 2);
+
+ Nasty n{1, 2};
+ Nasty m{3, 4};
+ Nasty p{5, 6};
+
+ optional<Nasty&> on{n};
+ assert (on);
+ assert (on->m == 1);
+ assert (on->n == 2);
+
+ on = {m};
+ assert (on);
+ assert (on->m == 3);
+ assert (on->n == 4);
+
+ on.emplace(p);
+ assert (on);
+ assert (on->m == 5);
+ assert (on->n == 6);
+
+ optional<Nasty&> om{in_place, n};
+ assert (om);
+ assert (om->m == 1);
+ assert (om->n == 2);
+}
+
+TEST(Optional, no_dangling_reference_in_value)
+{
+ // this mostly tests compiler warnings
+ using namespace wpi;
+ optional<int> oi {2};
+ unused (oi.value());
+ const optional<int> coi {3};
+ unused (coi.value());
+}
+
+struct CountedObject
+{
+ static int _counter;
+ bool _throw;
+ CountedObject(bool b) : _throw(b) { ++_counter; }
+ CountedObject(CountedObject const& rhs) : _throw(rhs._throw) { if (_throw) throw int(); }
+ ~CountedObject() { --_counter; }
+};
+
+int CountedObject::_counter = 0;
+
+TEST(Optional, exception_safety)
+{
+ using namespace wpi;
+ try {
+ optional<CountedObject> oo(in_place, true); // throw
+ optional<CountedObject> o1(oo);
+ }
+ catch(...)
+ {
+ //
+ }
+ assert(CountedObject::_counter == 0);
+
+ try {
+ optional<CountedObject> oo(in_place, true); // throw
+ optional<CountedObject> o1(std::move(oo)); // now move
+ }
+ catch(...)
+ {
+ //
+ }
+ assert(CountedObject::_counter == 0);
+}
+
+TEST(Optional, nested_optional)
+{
+ using namespace wpi;
+
+ optional<optional<optional<int>>> o1 {nullopt};
+ assert (!o1);
+
+ optional<optional<optional<int>>> o2 {in_place, nullopt};
+ assert (o2);
+ assert (!*o2);
+
+ optional<optional<optional<int>>> o3 (in_place, in_place, nullopt);
+ assert (o3);
+ assert (*o3);
+ assert (!**o3);
+}
+
+TEST(Optional, three_ways_of_having_value)
+{
+ using namespace wpi;
+ optional<int> oN, o1 (1);
+
+ assert (!oN);
+ assert (!oN.has_value());
+ assert (oN == nullopt);
+
+ assert (o1);
+ assert (o1.has_value());
+ assert (o1 != nullopt);
+
+ assert (bool(oN) == oN.has_value());
+ assert (bool(o1) == o1.has_value());
+
+ int i = 1;
+ optional<int&> rN, r1 (i);
+
+ assert (!rN);
+ assert (!rN.has_value());
+ assert (rN == nullopt);
+
+ assert (r1);
+ assert (r1.has_value());
+ assert (r1 != nullopt);
+
+ assert (bool(rN) == rN.has_value());
+ assert (bool(r1) == r1.has_value());
+}
+
+//// constexpr tests
+
+// these 4 classes have different noexcept signatures in move operations
+struct NothrowBoth {
+ NothrowBoth(NothrowBoth&&) noexcept(true) {};
+ void operator=(NothrowBoth&&) noexcept(true) {};
+};
+struct NothrowCtor {
+ NothrowCtor(NothrowCtor&&) noexcept(true) {};
+ void operator=(NothrowCtor&&) noexcept(false) {};
+};
+struct NothrowAssign {
+ NothrowAssign(NothrowAssign&&) noexcept(false) {};
+ void operator=(NothrowAssign&&) noexcept(true) {};
+};
+struct NothrowNone {
+ NothrowNone(NothrowNone&&) noexcept(false) {};
+ void operator=(NothrowNone&&) noexcept(false) {};
+};
+
+void test_noexcept()
+{
+ {
+ tr2::optional<NothrowBoth> b1, b2;
+ static_assert(noexcept(tr2::optional<NothrowBoth>{tr2::constexpr_move(b1)}), "bad noexcept!");
+ static_assert(noexcept(b1 = tr2::constexpr_move(b2)), "bad noexcept!");
+ }
+ {
+ tr2::optional<NothrowCtor> c1, c2;
+ static_assert(noexcept(tr2::optional<NothrowCtor>{tr2::constexpr_move(c1)}), "bad noexcept!");
+ static_assert(!noexcept(c1 = tr2::constexpr_move(c2)), "bad noexcept!");
+ }
+ {
+ tr2::optional<NothrowAssign> a1, a2;
+ static_assert(!noexcept(tr2::optional<NothrowAssign>{tr2::constexpr_move(a1)}), "bad noexcept!");
+ static_assert(!noexcept(a1 = tr2::constexpr_move(a2)), "bad noexcept!");
+ }
+ {
+ tr2::optional<NothrowNone> n1, n2;
+ static_assert(!noexcept(tr2::optional<NothrowNone>{tr2::constexpr_move(n1)}), "bad noexcept!");
+ static_assert(!noexcept(n1 = tr2::constexpr_move(n2)), "bad noexcept!");
+ }
+}
+
+
+void constexpr_test_disengaged()
+{
+ constexpr tr2::optional<int> g0{};
+ constexpr tr2::optional<int> g1{tr2::nullopt};
+ static_assert( !g0, "initialized!" );
+ static_assert( !g1, "initialized!" );
+
+ static_assert( bool(g1) == bool(g0), "ne!" );
+
+ static_assert( g1 == g0, "ne!" );
+ static_assert( !(g1 != g0), "ne!" );
+ static_assert( g1 >= g0, "ne!" );
+ static_assert( !(g1 > g0), "ne!" );
+ static_assert( g1 <= g0, "ne!" );
+ static_assert( !(g1 <g0), "ne!" );
+
+ static_assert( g1 == tr2::nullopt, "!" );
+ static_assert( !(g1 != tr2::nullopt), "!" );
+ static_assert( g1 <= tr2::nullopt, "!" );
+ static_assert( !(g1 < tr2::nullopt), "!" );
+ static_assert( g1 >= tr2::nullopt, "!" );
+ static_assert( !(g1 > tr2::nullopt), "!" );
+
+ static_assert( (tr2::nullopt == g0), "!" );
+ static_assert( !(tr2::nullopt != g0), "!" );
+ static_assert( (tr2::nullopt >= g0), "!" );
+ static_assert( !(tr2::nullopt > g0), "!" );
+ static_assert( (tr2::nullopt <= g0), "!" );
+ static_assert( !(tr2::nullopt < g0), "!" );
+
+ static_assert( (g1 != tr2::optional<int>(1)), "!" );
+ static_assert( !(g1 == tr2::optional<int>(1)), "!" );
+ static_assert( (g1 < tr2::optional<int>(1)), "!" );
+ static_assert( (g1 <= tr2::optional<int>(1)), "!" );
+ static_assert( !(g1 > tr2::optional<int>(1)), "!" );
+ static_assert( !(g1 > tr2::optional<int>(1)), "!" );
+}
+
+
+constexpr tr2::optional<int> g0{};
+constexpr tr2::optional<int> g2{2};
+static_assert( g2, "not initialized!" );
+static_assert( *g2 == 2, "not 2!" );
+static_assert( g2 == tr2::optional<int>(2), "not 2!" );
+static_assert( g2 != g0, "eq!" );
+
+# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
+static_assert( *tr2::optional<int>{3} == 3, "WTF!" );
+static_assert( tr2::optional<int>{3}.value() == 3, "WTF!" );
+static_assert( tr2::optional<int>{3}.value_or(1) == 3, "WTF!" );
+static_assert( tr2::optional<int>{}.value_or(4) == 4, "WTF!" );
+# endif
+
+constexpr tr2::optional<Combined> gc0{tr2::in_place};
+static_assert(gc0->n == 6, "WTF!");
+
+// optional refs
+int gi = 0;
+constexpr tr2::optional<int&> gori = gi;
+constexpr tr2::optional<int&> gorn{};
+constexpr int& gri = *gori;
+static_assert(gori, "WTF");
+static_assert(!gorn, "WTF");
+static_assert(gori != tr2::nullopt, "WTF");
+static_assert(gorn == tr2::nullopt, "WTF");
+static_assert(&gri == &*gori, "WTF");
+
+constexpr int gci = 1;
+constexpr tr2::optional<int const&> gorci = gci;
+constexpr tr2::optional<int const&> gorcn{};
+
+static_assert(gorcn < gorci, "WTF");
+static_assert(gorcn <= gorci, "WTF");
+static_assert(gorci == gorci, "WTF");
+static_assert(*gorci == 1, "WTF");
+static_assert(gorci == gci, "WTF");
+
+namespace constexpr_optional_ref_and_arrow
+{
+ using namespace wpi;
+ constexpr Combined c{1, 2};
+ constexpr optional<Combined const&> oc = c;
+ static_assert(oc, "WTF!");
+ static_assert(oc->m == 1, "WTF!");
+ static_assert(oc->n == 2, "WTF!");
+}
+
+#if OPTIONAL_HAS_CONSTEXPR_INIT_LIST
+
+namespace InitList
+{
+ using namespace wpi;
+
+ struct ConstInitLister
+ {
+ template <typename T>
+ constexpr ConstInitLister(std::initializer_list<T> il) : len (il.size()) {}
+ size_t len;
+ };
+
+ constexpr ConstInitLister CIL {2, 3, 4};
+ static_assert(CIL.len == 3, "WTF!");
+
+ constexpr optional<ConstInitLister> oil {in_place, {4, 5, 6, 7}};
+ static_assert(oil, "WTF!");
+ static_assert(oil->len == 4, "WTF!");
+}
+
+#endif // OPTIONAL_HAS_CONSTEXPR_INIT_LIST
+
+// end constexpr tests
+
+
+#include <string>
+
+
+struct VEC
+{
+ std::vector<int> v;
+ template <typename... X>
+ VEC( X&&...x) : v(std::forward<X>(x)...) {}
+
+ template <typename U, typename... X>
+ VEC(std::initializer_list<U> il, X&&...x) : v(il, std::forward<X>(x)...) {}
+};
+
+
+
+TEST(Optional, Vector) {
+ tr2::optional<int> oi = 1;
+ assert (bool(oi));
+ oi.operator=({});
+ assert (!oi);
+
+ VEC v = {5, 6};
+
+ if (OPTIONAL_HAS_CONSTEXPR_INIT_LIST)
+ std::cout << "Optional has constexpr initializer_list" << std::endl;
+ else
+ std::cout << "Optional doesn't have constexpr initializer_list" << std::endl;
+
+ if (OPTIONAL_HAS_MOVE_ACCESSORS)
+ std::cout << "Optional has constexpr move accessors" << std::endl;
+ else
+ std::cout << "Optional doesn't have constexpr move accessors" << std::endl;
+}
+
diff --git a/wpiutil/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp b/wpiutil/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
new file mode 100644
index 0000000..132adea
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
@@ -0,0 +1,241 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/AsyncFunction.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <thread>
+
+#include "wpi/uv/Loop.h"
+#include "wpi/uv/Prepare.h"
+
+namespace wpi {
+namespace uv {
+
+TEST(UvAsyncFunction, Test) {
+ int prepare_cb_called = 0;
+ int async_cb_called[2] = {0, 0};
+ int close_cb_called = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = AsyncFunction<int(int)>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ loop->error.connect([](Error) { FAIL(); });
+
+ prepare->error.connect([](Error) { FAIL(); });
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] {
+ auto call0 = async->Call(0);
+ auto call1 = async->Call(1);
+ ASSERT_EQ(call0.get(), 1);
+ ASSERT_EQ(call1.get(), 2);
+ });
+ });
+ prepare->Start();
+
+ async->error.connect([](Error) { FAIL(); });
+ async->closed.connect([&] { close_cb_called++; });
+ async->wakeup = [&](promise<int> out, int v) {
+ ++async_cb_called[v];
+ if (v == 1) {
+ async->Close();
+ prepare->Close();
+ }
+ out.set_value(v + 1);
+ };
+
+ loop->Run();
+
+ ASSERT_EQ(async_cb_called[0], 1);
+ ASSERT_EQ(async_cb_called[1], 1);
+ ASSERT_EQ(close_cb_called, 1);
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsyncFunction, Ref) {
+ int prepare_cb_called = 0;
+ int val = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = AsyncFunction<int(int, int&)>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] { ASSERT_EQ(async->Call(1, val).get(), 2); });
+ });
+ prepare->Start();
+
+ async->wakeup = [&](promise<int> out, int v, int& r) {
+ r = v;
+ async->Close();
+ prepare->Close();
+ out.set_value(v + 1);
+ };
+
+ loop->Run();
+
+ ASSERT_EQ(val, 1);
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsyncFunction, Movable) {
+ int prepare_cb_called = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async =
+ AsyncFunction<std::unique_ptr<int>(std::unique_ptr<int>)>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] {
+ auto val = std::make_unique<int>(1);
+ auto val2 = async->Call(std::move(val)).get();
+ ASSERT_NE(val2, nullptr);
+ ASSERT_EQ(*val2, 1);
+ });
+ });
+ prepare->Start();
+
+ async->wakeup = [&](promise<std::unique_ptr<int>> out,
+ std::unique_ptr<int> v) {
+ async->Close();
+ prepare->Close();
+ out.set_value(std::move(v));
+ };
+
+ loop->Run();
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsyncFunction, CallIgnoreResult) {
+ int prepare_cb_called = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async =
+ AsyncFunction<std::unique_ptr<int>(std::unique_ptr<int>)>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] { async->Call(std::make_unique<int>(1)); });
+ });
+ prepare->Start();
+
+ async->wakeup = [&](promise<std::unique_ptr<int>> out,
+ std::unique_ptr<int> v) {
+ async->Close();
+ prepare->Close();
+ out.set_value(std::move(v));
+ };
+
+ loop->Run();
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsyncFunction, VoidCall) {
+ int prepare_cb_called = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = AsyncFunction<void()>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] { async->Call(); });
+ });
+ prepare->Start();
+
+ async->wakeup = [&](promise<void> out) {
+ async->Close();
+ prepare->Close();
+ out.set_value();
+ };
+
+ loop->Run();
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsyncFunction, WaitFor) {
+ int prepare_cb_called = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = AsyncFunction<int()>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] {
+ ASSERT_FALSE(async->Call().wait_for(std::chrono::milliseconds(10)));
+ });
+ });
+ prepare->Start();
+
+ async->wakeup = [&](promise<int> out) {
+ async->Close();
+ prepare->Close();
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ out.set_value(1);
+ };
+
+ loop->Run();
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsyncFunction, VoidWaitFor) {
+ int prepare_cb_called = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = AsyncFunction<void()>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] {
+ ASSERT_FALSE(async->Call().wait_for(std::chrono::milliseconds(10)));
+ });
+ });
+ prepare->Start();
+
+ async->wakeup = [&](promise<void> out) {
+ async->Close();
+ prepare->Close();
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ out.set_value();
+ };
+
+ loop->Run();
+
+ if (theThread.joinable()) theThread.join();
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/uv/UvAsyncTest.cpp b/wpiutil/src/test/native/cpp/uv/UvAsyncTest.cpp
new file mode 100644
index 0000000..e18f972
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvAsyncTest.cpp
@@ -0,0 +1,178 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpi/uv/Async.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include <atomic>
+#include <thread>
+
+#include "wpi/mutex.h"
+#include "wpi/uv/Loop.h"
+#include "wpi/uv/Prepare.h"
+
+namespace wpi {
+namespace uv {
+
+TEST(UvAsync, Test) {
+ std::atomic_int async_cb_called{0};
+ int prepare_cb_called = 0;
+ int close_cb_called = 0;
+
+ wpi::mutex mutex;
+ mutex.lock();
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = Async<>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ loop->error.connect([](Error) { FAIL(); });
+
+ prepare->error.connect([](Error) { FAIL(); });
+ prepare->closed.connect([&] { close_cb_called++; });
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] {
+ for (;;) {
+ mutex.lock();
+ int n = async_cb_called;
+ mutex.unlock();
+
+ if (n == 3) {
+ break;
+ }
+
+ async->Send();
+
+ std::this_thread::yield();
+ }
+ });
+ mutex.unlock();
+ });
+ prepare->Start();
+
+ async->error.connect([](Error) { FAIL(); });
+ async->closed.connect([&] { close_cb_called++; });
+ async->wakeup.connect([&] {
+ mutex.lock();
+ int n = ++async_cb_called;
+ mutex.unlock();
+
+ if (n == 3) {
+ async->Close();
+ prepare->Close();
+ }
+ });
+
+ loop->Run();
+
+ ASSERT_GT(prepare_cb_called, 0);
+ ASSERT_EQ(async_cb_called, 3);
+ ASSERT_EQ(close_cb_called, 2);
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsync, Data) {
+ int prepare_cb_called = 0;
+ int async_cb_called[2] = {0, 0};
+ int close_cb_called = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = Async<int, std::function<void(int)>>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ loop->error.connect([](Error) { FAIL(); });
+
+ prepare->error.connect([](Error) { FAIL(); });
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] {
+ async->Send(0, [&](int v) {
+ ASSERT_EQ(v, 0);
+ ++async_cb_called[0];
+ });
+ async->Send(1, [&](int v) {
+ ASSERT_EQ(v, 1);
+ ++async_cb_called[1];
+ async->Close();
+ prepare->Close();
+ });
+ });
+ });
+ prepare->Start();
+
+ async->error.connect([](Error) { FAIL(); });
+ async->closed.connect([&] { close_cb_called++; });
+ async->wakeup.connect([&](int v, std::function<void(int)> f) { f(v); });
+
+ loop->Run();
+
+ ASSERT_EQ(async_cb_called[0], 1);
+ ASSERT_EQ(async_cb_called[1], 1);
+ ASSERT_EQ(close_cb_called, 1);
+
+ if (theThread.joinable()) theThread.join();
+}
+
+TEST(UvAsync, DataRef) {
+ int prepare_cb_called = 0;
+ int val = 0;
+
+ std::thread theThread;
+
+ auto loop = Loop::Create();
+ auto async = Async<int, int&>::Create(loop);
+ auto prepare = Prepare::Create(loop);
+
+ prepare->prepare.connect([&] {
+ if (prepare_cb_called++) return;
+ theThread = std::thread([&] { async->Send(1, val); });
+ });
+ prepare->Start();
+
+ async->wakeup.connect([&](int v, int& r) {
+ r = v;
+ async->Close();
+ prepare->Close();
+ });
+
+ loop->Run();
+
+ ASSERT_EQ(val, 1);
+
+ if (theThread.joinable()) theThread.join();
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/uv/UvBufferTest.cpp b/wpiutil/src/test/native/cpp/uv/UvBufferTest.cpp
new file mode 100644
index 0000000..e837ca9
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvBufferTest.cpp
@@ -0,0 +1,50 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Buffer.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+namespace wpi {
+namespace uv {
+
+TEST(UvSimpleBufferPool, ConstructDefault) {
+ SimpleBufferPool<> pool;
+ auto buf1 = pool.Allocate();
+ ASSERT_EQ(buf1.len, 4096u);
+}
+
+TEST(UvSimpleBufferPool, ConstructSize) {
+ SimpleBufferPool<4> pool{8192};
+ auto buf1 = pool.Allocate();
+ ASSERT_EQ(buf1.len, 8192u);
+}
+
+TEST(UvSimpleBufferPool, ReleaseReuse) {
+ SimpleBufferPool<4> pool;
+ auto buf1 = pool.Allocate();
+ auto buf1copy = buf1;
+ auto origSize = buf1.len;
+ buf1.len = 8;
+ pool.Release(buf1);
+ ASSERT_EQ(buf1.base, nullptr);
+ auto buf2 = pool.Allocate();
+ ASSERT_EQ(buf1copy.base, buf2.base);
+ ASSERT_EQ(buf2.len, origSize);
+}
+
+TEST(UvSimpleBufferPool, ClearRemaining) {
+ SimpleBufferPool<4> pool;
+ auto buf1 = pool.Allocate();
+ pool.Release(buf1);
+ ASSERT_EQ(pool.Remaining(), 1u);
+ pool.Clear();
+ ASSERT_EQ(pool.Remaining(), 0u);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp b/wpiutil/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
new file mode 100644
index 0000000..11ac426
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
@@ -0,0 +1,110 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpi/uv/GetAddrInfo.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include "wpi/uv/Loop.h"
+
+#define CONCURRENT_COUNT 10
+
+namespace wpi {
+namespace uv {
+
+TEST(UvGetAddrInfo, BothNull) {
+ int fail_cb_called = 0;
+
+ auto loop = Loop::Create();
+ loop->error.connect([&](Error err) {
+ ASSERT_EQ(err.code(), UV_EINVAL);
+ fail_cb_called++;
+ });
+
+ GetAddrInfo(loop, [](const addrinfo&) { FAIL(); }, Twine::createNull());
+ loop->Run();
+ ASSERT_EQ(fail_cb_called, 1);
+}
+
+TEST(UvGetAddrInfo, FailedLookup) {
+ int fail_cb_called = 0;
+
+ auto loop = Loop::Create();
+ loop->error.connect([&](Error err) {
+ ASSERT_EQ(fail_cb_called, 0);
+ ASSERT_LT(err.code(), 0);
+ fail_cb_called++;
+ });
+
+ // Use a FQDN by ending in a period
+ GetAddrInfo(loop, [](const addrinfo&) { FAIL(); }, "xyzzy.xyzzy.xyzzy.");
+ loop->Run();
+ ASSERT_EQ(fail_cb_called, 1);
+}
+
+TEST(UvGetAddrInfo, Basic) {
+ int getaddrinfo_cbs = 0;
+
+ auto loop = Loop::Create();
+ loop->error.connect([](Error) { FAIL(); });
+
+ GetAddrInfo(loop, [&](const addrinfo&) { getaddrinfo_cbs++; }, "localhost");
+
+ loop->Run();
+
+ ASSERT_EQ(getaddrinfo_cbs, 1);
+}
+
+#ifndef _WIN32
+TEST(UvGetAddrInfo, Concurrent) {
+ int getaddrinfo_cbs = 0;
+ int callback_counts[CONCURRENT_COUNT];
+
+ auto loop = Loop::Create();
+ loop->error.connect([](Error) { FAIL(); });
+
+ for (int i = 0; i < CONCURRENT_COUNT; i++) {
+ callback_counts[i] = 0;
+ GetAddrInfo(loop,
+ [i, &callback_counts, &getaddrinfo_cbs](const addrinfo&) {
+ callback_counts[i]++;
+ getaddrinfo_cbs++;
+ },
+ "localhost");
+ }
+
+ loop->Run();
+
+ for (int i = 0; i < CONCURRENT_COUNT; i++) {
+ ASSERT_EQ(callback_counts[i], 1);
+ }
+}
+#endif
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/uv/UvGetNameInfoTest.cpp b/wpiutil/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
new file mode 100644
index 0000000..16de329
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
@@ -0,0 +1,77 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpi/uv/GetNameInfo.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+TEST(UvGetNameInfo, BasicIp4) {
+ int getnameinfo_cbs = 0;
+
+ auto loop = Loop::Create();
+ loop->error.connect([](Error) { FAIL(); });
+
+ GetNameInfo4(loop,
+ [&](const char* hostname, const char* service) {
+ ASSERT_NE(hostname, nullptr);
+ ASSERT_NE(service, nullptr);
+ getnameinfo_cbs++;
+ },
+ "127.0.0.1", 80);
+
+ loop->Run();
+
+ ASSERT_EQ(getnameinfo_cbs, 1);
+}
+
+TEST(UvGetNameInfo, BasicIp6) {
+ int getnameinfo_cbs = 0;
+
+ auto loop = Loop::Create();
+ loop->error.connect([](Error) { FAIL(); });
+
+ GetNameInfo6(loop,
+ [&](const char* hostname, const char* service) {
+ ASSERT_NE(hostname, nullptr);
+ ASSERT_NE(service, nullptr);
+ getnameinfo_cbs++;
+ },
+ "::1", 80);
+
+ loop->Run();
+
+ ASSERT_EQ(getnameinfo_cbs, 1);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp b/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
new file mode 100644
index 0000000..78abf69
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
@@ -0,0 +1,70 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpi/uv/Loop.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h" // NOLINT(build/include_order)
+
+#include "wpi/uv/Timer.h"
+
+namespace wpi {
+namespace uv {
+
+TEST(UvLoop, Walk) {
+ int seen_timer_handle = 0;
+
+ auto loop = Loop::Create();
+ auto timer = Timer::Create(loop);
+
+ loop->error.connect([](Error) { FAIL(); });
+
+ timer->error.connect([](Error) { FAIL(); });
+
+ timer->timeout.connect([&, theTimer = timer.get() ] {
+ theTimer->GetLoopRef().Walk([&](Handle& it) {
+ if (&it == timer.get()) seen_timer_handle++;
+ });
+ theTimer->Close();
+ });
+ timer->Start(Timer::Time{1});
+
+ // Start event loop, expect to see the timer handle
+ ASSERT_EQ(seen_timer_handle, 0);
+ loop->Run();
+ ASSERT_EQ(seen_timer_handle, 1);
+
+ // Loop is finished, should not see our timer handle
+ seen_timer_handle = 0;
+ loop->Walk([&](Handle& it) {
+ if (&it == timer.get()) seen_timer_handle++;
+ });
+ ASSERT_EQ(seen_timer_handle, 0);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp b/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
new file mode 100644
index 0000000..706e1eb
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
@@ -0,0 +1,74 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Timer.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+namespace uv {
+
+TEST(UvTimer, StartAndStop) {
+ auto loop = Loop::Create();
+ auto handleNoRepeat = Timer::Create(loop);
+ auto handleRepeat = Timer::Create(loop);
+
+ bool checkTimerNoRepeatEvent = false;
+ bool checkTimerRepeatEvent = false;
+
+ handleNoRepeat->error.connect([](Error) { FAIL(); });
+ handleRepeat->error.connect([](Error) { FAIL(); });
+
+ handleNoRepeat->timeout.connect(
+ [&checkTimerNoRepeatEvent, handle = handleNoRepeat.get() ] {
+ ASSERT_FALSE(checkTimerNoRepeatEvent);
+ checkTimerNoRepeatEvent = true;
+ handle->Stop();
+ handle->Close();
+ ASSERT_TRUE(handle->IsClosing());
+ });
+
+ handleRepeat->timeout.connect(
+ [&checkTimerRepeatEvent, handle = handleRepeat.get() ] {
+ if (checkTimerRepeatEvent) {
+ handle->Stop();
+ handle->Close();
+ ASSERT_TRUE(handle->IsClosing());
+ } else {
+ checkTimerRepeatEvent = true;
+ ASSERT_FALSE(handle->IsClosing());
+ }
+ });
+
+ handleNoRepeat->Start(Timer::Time{0}, Timer::Time{0});
+ handleRepeat->Start(Timer::Time{0}, Timer::Time{1});
+
+ ASSERT_TRUE(handleNoRepeat->IsActive());
+ ASSERT_FALSE(handleNoRepeat->IsClosing());
+
+ ASSERT_TRUE(handleRepeat->IsActive());
+ ASSERT_FALSE(handleRepeat->IsClosing());
+
+ loop->Run();
+
+ ASSERT_TRUE(checkTimerNoRepeatEvent);
+ ASSERT_TRUE(checkTimerRepeatEvent);
+}
+
+TEST(UvTimer, Repeat) {
+ auto loop = Loop::Create();
+ auto handle = Timer::Create(loop);
+
+ handle->SetRepeat(Timer::Time{42});
+ ASSERT_EQ(handle->GetRepeat(), Timer::Time{42});
+ handle->Close();
+
+ loop->Run(); // forces close callback to run
+}
+
+} // namespace uv
+} // namespace wpi