Rename our allwpilib (which is now 2020) to not have 2019 in the name
Change-Id: I3c07f85ed32ab8b97db765a9b43f2a6ce7da964a
diff --git a/wpiutil/src/test/java/edu/wpi/first/wpiutil/CircularBufferTest.java b/wpiutil/src/test/java/edu/wpi/first/wpiutil/CircularBufferTest.java
new file mode 100644
index 0000000..f995098
--- /dev/null
+++ b/wpiutil/src/test/java/edu/wpi/first/wpiutil/CircularBufferTest.java
@@ -0,0 +1,214 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2019 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. */
+/*----------------------------------------------------------------------------*/
+
+package edu.wpi.first.wpiutil;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class CircularBufferTest {
+ private final double[] m_values = {751.848, 766.366, 342.657, 234.252, 716.126,
+ 132.344, 445.697, 22.727, 421.125, 799.913};
+ private final double[] m_addFirstOut = {799.913, 421.125, 22.727, 445.697, 132.344,
+ 716.126, 234.252, 342.657};
+ private final double[] m_addLastOut = {342.657, 234.252, 716.126, 132.344, 445.697,
+ 22.727, 421.125, 799.913};
+
+ @Test
+ void addFirstTest() {
+ CircularBuffer queue = new CircularBuffer(8);
+
+ for (double value : m_values) {
+ queue.addFirst(value);
+ }
+
+ for (int i = 0; i < m_addFirstOut.length; i++) {
+ assertEquals(m_addFirstOut[i], queue.get(i), 0.00005);
+ }
+ }
+
+ @Test
+ void addLastTest() {
+ CircularBuffer queue = new CircularBuffer(8);
+
+ for (double value : m_values) {
+ queue.addLast(value);
+ }
+
+ for (int i = 0; i < m_addLastOut.length; i++) {
+ assertEquals(m_addLastOut[i], queue.get(i), 0.00005);
+ }
+ }
+
+ @Test
+ void pushPopTest() {
+ CircularBuffer queue = new CircularBuffer(3);
+
+ // Insert three elements into the buffer
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+ assertEquals(3.0, queue.get(2), 0.00005);
+
+ /*
+ * The buffer is full now, so pushing subsequent elements will overwrite the
+ * front-most elements.
+ */
+
+ queue.addLast(4.0); // Overwrite 1 with 4
+
+ // The buffer now contains 2, 3, and 4
+ assertEquals(2.0, queue.get(0), 0.00005);
+ assertEquals(3.0, queue.get(1), 0.00005);
+ assertEquals(4.0, queue.get(2), 0.00005);
+
+ queue.addLast(5.0); // Overwrite 2 with 5
+
+ // The buffer now contains 3, 4, and 5
+ assertEquals(3.0, queue.get(0), 0.00005);
+ assertEquals(4.0, queue.get(1), 0.00005);
+ assertEquals(5.0, queue.get(2), 0.00005);
+
+ assertEquals(5.0, queue.removeLast(), 0.00005); // 5 is removed
+
+ // The buffer now contains 3 and 4
+ assertEquals(3.0, queue.get(0), 0.00005);
+ assertEquals(4.0, queue.get(1), 0.00005);
+
+ assertEquals(3.0, queue.removeFirst(), 0.00005); // 3 is removed
+
+ // Leaving only one element with value == 4
+ assertEquals(4.0, queue.get(0), 0.00005);
+ }
+
+ @Test
+ void resetTest() {
+ CircularBuffer queue = new CircularBuffer(5);
+
+ for (int i = 0; i < 6; i++) {
+ queue.addLast(i);
+ }
+
+ queue.clear();
+
+ for (int i = 0; i < 5; i++) {
+ assertEquals(0.0, queue.get(i), 0.00005);
+ }
+ }
+
+ @Test
+ @SuppressWarnings("PMD.ExcessiveMethodLength")
+ void resizeTest() {
+ CircularBuffer queue = new CircularBuffer(5);
+
+ /* Buffer contains {1, 2, 3, _, _}
+ * ^ front
+ */
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {_, 1, 2, 3, _}
+ * ^ front
+ */
+ queue.addLast(0.0);
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+ queue.removeFirst();
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {_, _, 1, 2, 3}
+ * ^ front
+ */
+ queue.addLast(0.0);
+ queue.addLast(0.0);
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+ queue.removeFirst();
+ queue.removeFirst();
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {3, _, _, 1, 2}
+ * ^ front
+ */
+ queue.addLast(3.0);
+ queue.addFirst(2.0);
+ queue.addFirst(1.0);
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {2, 3, _, _, 1}
+ * ^ front
+ */
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+ queue.addFirst(1.0);
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ // Test addLast() after resize
+ queue.addLast(3.0);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+ assertEquals(3.0, queue.get(2), 0.00005);
+
+ // Test addFirst() after resize
+ queue.addFirst(4.0);
+ assertEquals(4.0, queue.get(0), 0.00005);
+ assertEquals(1.0, queue.get(1), 0.00005);
+ assertEquals(2.0, queue.get(2), 0.00005);
+ assertEquals(3.0, queue.get(3), 0.00005);
+ }
+}
diff --git a/wpiutil/src/test/java/edu/wpi/first/wpiutil/math/MatrixTest.java b/wpiutil/src/test/java/edu/wpi/first/wpiutil/math/MatrixTest.java
new file mode 100644
index 0000000..4d6697d
--- /dev/null
+++ b/wpiutil/src/test/java/edu/wpi/first/wpiutil/math/MatrixTest.java
@@ -0,0 +1,210 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 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. */
+/*----------------------------------------------------------------------------*/
+
+package edu.wpi.first.wpiutil.math;
+
+import org.ejml.data.SingularMatrixException;
+import org.ejml.dense.row.MatrixFeatures_DDRM;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.jupiter.api.Test;
+
+import edu.wpi.first.wpiutil.math.numbers.N1;
+import edu.wpi.first.wpiutil.math.numbers.N2;
+import edu.wpi.first.wpiutil.math.numbers.N3;
+import edu.wpi.first.wpiutil.math.numbers.N4;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class MatrixTest {
+ @Test
+ void testMatrixMultiplication() {
+ var mat1 = MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(2.0, 1.0,
+ 0.0, 1.0);
+ var mat2 = MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(3.0, 0.0,
+ 0.0, 2.5);
+
+ Matrix<N2, N2> result = mat1.times(mat2);
+
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(6.0, 2.5,
+ 0.0, 2.5).getStorage().getDDRM(),
+ result.getStorage().getDDRM()
+ ));
+
+ var mat3 = MatrixUtils.mat(Nat.N2(), Nat.N3())
+ .fill(1.0, 3.0, 0.5,
+ 2.0, 4.3, 1.2);
+ var mat4 = MatrixUtils.mat(Nat.N3(), Nat.N4())
+ .fill(3.0, 1.5, 2.0, 4.5,
+ 2.3, 1.0, 1.6, 3.1,
+ 5.2, 2.1, 2.0, 1.0);
+
+ Matrix<N2, N4> result2 = mat3.times(mat4);
+
+ assertTrue(MatrixFeatures_DDRM.isIdentical(
+ MatrixUtils.mat(Nat.N2(), Nat.N4())
+ .fill(12.5, 5.55, 7.8, 14.3,
+ 22.13, 9.82, 13.28, 23.53).getStorage().getDDRM(),
+ result2.getStorage().getDDRM(),
+ 1E-9
+ ));
+ }
+
+ @Test
+ void testMatrixVectorMultiplication() {
+ var mat = MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(1.0, 1.0,
+ 0.0, 1.0);
+
+ var vec = MatrixUtils.vec(Nat.N2())
+ .fill(3.0,
+ 2.0);
+
+ Matrix<N2, N1> result = mat.times(vec);
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.vec(Nat.N2())
+ .fill(5.0,
+ 2.0).getStorage().getDDRM(),
+ result.getStorage().getDDRM()
+ ));
+ }
+
+ @Test
+ void testTranspose() {
+ Matrix<N3, N1> vec = MatrixUtils.vec(Nat.N3())
+ .fill(1.0,
+ 2.0,
+ 3.0);
+
+ Matrix<N1, N3> transpose = vec.transpose();
+
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.mat(Nat.N1(), Nat.N3()).fill(1.0, 2.0, 3.0).getStorage()
+ .getDDRM(),
+ transpose.getStorage().getDDRM()
+ ));
+ }
+
+ @Test
+ void testInverse() {
+ var mat = MatrixUtils.mat(Nat.N3(), Nat.N3())
+ .fill(1.0, 3.0, 2.0,
+ 5.0, 2.0, 1.5,
+ 0.0, 1.3, 2.5);
+
+ var inv = mat.inv();
+
+ assertTrue(MatrixFeatures_DDRM.isIdentical(
+ MatrixUtils.eye(Nat.N3()).getStorage().getDDRM(),
+ mat.times(inv).getStorage().getDDRM(),
+ 1E-9
+ ));
+
+ assertTrue(MatrixFeatures_DDRM.isIdentical(
+ MatrixUtils.eye(Nat.N3()).getStorage().getDDRM(),
+ inv.times(mat).getStorage().getDDRM(),
+ 1E-9
+ ));
+ }
+
+ @Test
+ void testUninvertableMatrix() {
+ var singularMatrix = MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(2.0, 1.0,
+ 2.0, 1.0);
+
+ assertThrows(SingularMatrixException.class, singularMatrix::inv);
+ }
+
+ @Test
+ void testMatrixScalarArithmetic() {
+ var mat = MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(1.0, 2.0,
+ 3.0, 4.0);
+
+
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(3.0, 4.0,
+ 5.0, 6.0).getStorage().getDDRM(),
+ mat.plus(2.0).getStorage().getDDRM()
+ ));
+
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(0.0, 1.0,
+ 2.0, 3.0).getStorage().getDDRM(),
+ mat.minus(1.0).getStorage().getDDRM()
+ ));
+
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(2.0, 4.0,
+ 6.0, 8.0).getStorage().getDDRM(),
+ mat.times(2.0).getStorage().getDDRM()
+ ));
+
+ assertTrue(MatrixFeatures_DDRM.isIdentical(
+ MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(0.5, 1.0,
+ 1.5, 2.0).getStorage().getDDRM(),
+ mat.div(2.0).getStorage().getDDRM(),
+ 1E-3
+ ));
+ }
+
+ @Test
+ void testMatrixMatrixArithmetic() {
+ var mat1 = MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(1.0, 2.0,
+ 3.0, 4.0);
+
+ var mat2 = MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(5.0, 6.0,
+ 7.0, 8.0);
+
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(-4.0, -4.0,
+ -4.0, -4.0).getStorage().getDDRM(),
+ mat1.minus(mat2).getStorage().getDDRM()
+ ));
+
+ assertTrue(MatrixFeatures_DDRM.isEquals(
+ MatrixUtils.mat(Nat.N2(), Nat.N2())
+ .fill(6.0, 8.0,
+ 10.0, 12.0).getStorage().getDDRM(),
+ mat1.plus(mat2).getStorage().getDDRM()
+ ));
+ }
+
+ @Test
+ void testMatrixExponential() {
+ SimpleMatrix matrix = MatrixUtils.eye(Nat.N2()).getStorage();
+ var result = SimpleMatrixUtils.expm(matrix);
+
+ assertTrue(MatrixFeatures_DDRM.isIdentical(
+ result.getDDRM(),
+ new SimpleMatrix(2, 2, true, new double[]{Math.E, 0, 0, Math.E}).getDDRM(),
+ 1E-9
+ ));
+
+ matrix = new SimpleMatrix(2, 2, true, new double[]{1, 2, 3, 4});
+ result = SimpleMatrixUtils.expm(matrix.scale(0.01));
+
+ assertTrue(MatrixFeatures_DDRM.isIdentical(
+ result.getDDRM(),
+ new SimpleMatrix(2, 2, true, new double[]{1.01035625, 0.02050912,
+ 0.03076368, 1.04111993}).getDDRM(),
+ 1E-8
+ ));
+ }
+}
diff --git a/wpiutil/src/test/native/ManagedStaticTest.cpp b/wpiutil/src/test/native/ManagedStaticTest.cpp
new file mode 100644
index 0000000..81a4b9c
--- /dev/null
+++ b/wpiutil/src/test/native/ManagedStaticTest.cpp
@@ -0,0 +1,60 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 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/ManagedStatic.h" // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+static int refCount = 0;
+
+struct StaticTestClass {
+ StaticTestClass() { refCount++; }
+ ~StaticTestClass() { refCount--; }
+
+ void Func() {}
+};
+
+namespace wpi {
+TEST(ManagedStaticTest, LazyDoesNotInitialize) {
+ {
+ refCount = 0;
+ wpi::ManagedStatic<StaticTestClass> managedStatic;
+ ASSERT_EQ(refCount, 0);
+ }
+ ASSERT_EQ(refCount, 0);
+ wpi_shutdown();
+}
+
+TEST(ManagedStaticTest, LazyInitDoesntDestruct) {
+ {
+ refCount = 0;
+ wpi::ManagedStatic<StaticTestClass> managedStatic;
+ ASSERT_EQ(refCount, 0);
+ managedStatic->Func();
+ ASSERT_EQ(refCount, 1);
+ }
+ ASSERT_EQ(refCount, 1);
+ wpi_shutdown();
+ ASSERT_EQ(refCount, 0);
+}
+
+TEST(ManagedStaticTest, EagerInit) {
+ {
+ refCount = 0;
+ StaticTestClass* test = new StaticTestClass{};
+ ASSERT_EQ(refCount, 1);
+ wpi::ManagedStatic<StaticTestClass> managedStatic(
+ test, [](void* val) { delete static_cast<StaticTestClass*>(val); });
+ ASSERT_EQ(refCount, 1);
+ managedStatic->Func();
+ ASSERT_EQ(refCount, 1);
+ }
+ ASSERT_EQ(refCount, 1);
+ wpi_shutdown();
+ ASSERT_EQ(refCount, 0);
+}
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/Base64Test.cpp b/wpiutil/src/test/native/cpp/Base64Test.cpp
new file mode 100644
index 0000000..caa35aa
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/Base64Test.cpp
@@ -0,0 +1,104 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2019 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_SUITE_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_SUITE_P(Base64Standard, Base64Test,
+ ::testing::ValuesIn(standard));
+
+} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/CircularBufferTest.cpp b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
new file mode 100644
index 0000000..7fe9e03
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
@@ -0,0 +1,209 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2019 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/circular_buffer.h" // NOLINT(build/include_order)
+
+#include <array>
+
+#include "gtest/gtest.h"
+
+static const std::array<double, 10> values = {
+ {751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727,
+ 421.125, 799.913}};
+
+static const std::array<double, 8> pushFrontOut = {
+ {799.913, 421.125, 22.727, 445.697, 132.344, 716.126, 234.252, 342.657}};
+
+static const std::array<double, 8> pushBackOut = {
+ {342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913}};
+
+TEST(CircularBufferTest, PushFrontTest) {
+ wpi::circular_buffer<double> queue(8);
+
+ for (auto& value : values) {
+ queue.push_front(value);
+ }
+
+ for (size_t i = 0; i < pushFrontOut.size(); i++) {
+ EXPECT_EQ(pushFrontOut[i], queue[i]);
+ }
+}
+
+TEST(CircularBufferTest, PushBackTest) {
+ wpi::circular_buffer<double> queue(8);
+
+ for (auto& value : values) {
+ queue.push_back(value);
+ }
+
+ for (size_t i = 0; i < pushBackOut.size(); i++) {
+ EXPECT_EQ(pushBackOut[i], queue[i]);
+ }
+}
+
+TEST(CircularBufferTest, PushPopTest) {
+ wpi::circular_buffer<double> queue(3);
+
+ // Insert three elements into the buffer
+ queue.push_back(1.0);
+ queue.push_back(2.0);
+ queue.push_back(3.0);
+
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+ EXPECT_EQ(3.0, queue[2]);
+
+ /*
+ * The buffer is full now, so pushing subsequent elements will overwrite the
+ * front-most elements.
+ */
+
+ queue.push_back(4.0); // Overwrite 1 with 4
+
+ // The buffer now contains 2, 3 and 4
+ EXPECT_EQ(2.0, queue[0]);
+ EXPECT_EQ(3.0, queue[1]);
+ EXPECT_EQ(4.0, queue[2]);
+
+ queue.push_back(5.0); // Overwrite 2 with 5
+
+ // The buffer now contains 3, 4 and 5
+ EXPECT_EQ(3.0, queue[0]);
+ EXPECT_EQ(4.0, queue[1]);
+ EXPECT_EQ(5.0, queue[2]);
+
+ EXPECT_EQ(5.0, queue.pop_back()); // 5 is removed
+
+ // The buffer now contains 3 and 4
+ EXPECT_EQ(3.0, queue[0]);
+ EXPECT_EQ(4.0, queue[1]);
+
+ EXPECT_EQ(3.0, queue.pop_front()); // 3 is removed
+
+ // Leaving only one element with value == 4
+ EXPECT_EQ(4.0, queue[0]);
+}
+
+TEST(CircularBufferTest, ResetTest) {
+ wpi::circular_buffer<double> queue(5);
+
+ for (size_t i = 1; i < 6; i++) {
+ queue.push_back(i);
+ }
+
+ queue.reset();
+
+ for (size_t i = 0; i < 5; i++) {
+ EXPECT_EQ(0.0, queue[i]);
+ }
+}
+
+TEST(CircularBufferTest, ResizeTest) {
+ wpi::circular_buffer<double> queue(5);
+
+ /* Buffer contains {1, 2, 3, _, _}
+ * ^ front
+ */
+ queue.push_back(1.0);
+ queue.push_back(2.0);
+ queue.push_back(3.0);
+
+ queue.resize(2);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.resize(5);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.reset();
+
+ /* Buffer contains {_, 1, 2, 3, _}
+ * ^ front
+ */
+ queue.push_back(0.0);
+ queue.push_back(1.0);
+ queue.push_back(2.0);
+ queue.push_back(3.0);
+ queue.pop_front();
+
+ queue.resize(2);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.resize(5);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.reset();
+
+ /* Buffer contains {_, _, 1, 2, 3}
+ * ^ front
+ */
+ queue.push_back(0.0);
+ queue.push_back(0.0);
+ queue.push_back(1.0);
+ queue.push_back(2.0);
+ queue.push_back(3.0);
+ queue.pop_front();
+ queue.pop_front();
+
+ queue.resize(2);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.resize(5);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.reset();
+
+ /* Buffer contains {3, _, _, 1, 2}
+ * ^ front
+ */
+ queue.push_back(3.0);
+ queue.push_front(2.0);
+ queue.push_front(1.0);
+
+ queue.resize(2);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.resize(5);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.reset();
+
+ /* Buffer contains {2, 3, _, _, 1}
+ * ^ front
+ */
+ queue.push_back(2.0);
+ queue.push_back(3.0);
+ queue.push_front(1.0);
+
+ queue.resize(2);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ queue.resize(5);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+
+ // Test push_back() after resize
+ queue.push_back(3.0);
+ EXPECT_EQ(1.0, queue[0]);
+ EXPECT_EQ(2.0, queue[1]);
+ EXPECT_EQ(3.0, queue[2]);
+
+ // Test push_front() after resize
+ queue.push_front(4.0);
+ EXPECT_EQ(4.0, queue[0]);
+ EXPECT_EQ(1.0, queue[1]);
+ EXPECT_EQ(2.0, queue[2]);
+ EXPECT_EQ(3.0, queue[3]);
+}
diff --git a/wpiutil/src/test/native/cpp/EigenTest.cpp b/wpiutil/src/test/native/cpp/EigenTest.cpp
new file mode 100644
index 0000000..04a0bc2
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/EigenTest.cpp
@@ -0,0 +1,61 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 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 <Eigen/Core>
+#include <Eigen/LU>
+
+#include "gtest/gtest.h"
+
+TEST(EigenTest, MultiplicationTest) {
+ Eigen::Matrix<double, 2, 2> m1;
+ m1 << 2, 1, 0, 1;
+
+ Eigen::Matrix<double, 2, 2> m2;
+ m2 << 3, 0, 0, 2.5;
+
+ const auto result = m1 * m2;
+
+ Eigen::Matrix<double, 2, 2> expectedResult;
+ expectedResult << 6.0, 2.5, 0.0, 2.5;
+
+ EXPECT_TRUE(expectedResult.isApprox(result));
+
+ Eigen::Matrix<double, 2, 3> m3;
+ m3 << 1.0, 3.0, 0.5, 2.0, 4.3, 1.2;
+
+ Eigen::Matrix<double, 3, 4> m4;
+ m4 << 3.0, 1.5, 2.0, 4.5, 2.3, 1.0, 1.6, 3.1, 5.2, 2.1, 2.0, 1.0;
+
+ const auto result2 = m3 * m4;
+
+ Eigen::Matrix<double, 2, 4> expectedResult2;
+ expectedResult2 << 12.5, 5.55, 7.8, 14.3, 22.13, 9.82, 13.28, 23.53;
+
+ EXPECT_TRUE(expectedResult2.isApprox(result2));
+}
+
+TEST(EigenTest, TransposeTest) {
+ Eigen::Matrix<double, 3, 1> vec;
+ vec << 1, 2, 3;
+
+ const auto transpose = vec.transpose();
+
+ Eigen::Matrix<double, 1, 3> expectedTranspose;
+ expectedTranspose << 1, 2, 3;
+
+ EXPECT_TRUE(expectedTranspose.isApprox(transpose));
+}
+
+TEST(EigenTest, InverseTest) {
+ Eigen::Matrix<double, 3, 3> mat;
+ mat << 1.0, 3.0, 2.0, 5.0, 2.0, 1.5, 0.0, 1.3, 2.5;
+
+ const auto inverse = mat.inverse();
+ const auto identity = Eigen::MatrixXd::Identity(3, 3);
+
+ EXPECT_TRUE(identity.isApprox(mat * inverse));
+}
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/UnitsTest.cpp b/wpiutil/src/test/native/cpp/UnitsTest.cpp
new file mode 100644
index 0000000..8e0823a
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/UnitsTest.cpp
@@ -0,0 +1,3379 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 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 <array>
+#include <chrono>
+#include <string>
+#include <type_traits>
+
+#include "gtest/gtest.h"
+#include "units/units.h"
+
+using namespace units;
+using namespace units::dimensionless;
+using namespace units::length;
+using namespace units::mass;
+using namespace units::angle;
+using namespace units::time;
+using namespace units::frequency;
+using namespace units::area;
+using namespace units::velocity;
+using namespace units::angular_velocity;
+using namespace units::temperature;
+using namespace units::luminous_intensity;
+using namespace units::solid_angle;
+using namespace units::frequency;
+using namespace units::acceleration;
+using namespace units::pressure;
+using namespace units::charge;
+using namespace units::energy;
+using namespace units::power;
+using namespace units::voltage;
+using namespace units::capacitance;
+using namespace units::impedance;
+using namespace units::conductance;
+using namespace units::magnetic_flux;
+using namespace units::magnetic_field_strength;
+using namespace units::inductance;
+using namespace units::luminous_flux;
+using namespace units::illuminance;
+using namespace units::radiation;
+using namespace units::torque;
+using namespace units::volume;
+using namespace units::density;
+using namespace units::concentration;
+using namespace units::data;
+using namespace units::data_transfer_rate;
+using namespace units::math;
+
+#if !defined(_MSC_VER) || _MSC_VER > 1800
+using namespace units::literals;
+#endif
+
+namespace {
+
+class TypeTraits : public ::testing::Test {
+ protected:
+ TypeTraits() {}
+ virtual ~TypeTraits() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
+
+class UnitManipulators : public ::testing::Test {
+ protected:
+ UnitManipulators() {}
+ virtual ~UnitManipulators() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
+
+class UnitContainer : public ::testing::Test {
+ protected:
+ UnitContainer() {}
+ virtual ~UnitContainer() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
+
+class UnitConversion : public ::testing::Test {
+ protected:
+ UnitConversion() {}
+ virtual ~UnitConversion() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
+
+class UnitMath : public ::testing::Test {
+ protected:
+ UnitMath() {}
+ virtual ~UnitMath() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
+
+class CompileTimeArithmetic : public ::testing::Test {
+ protected:
+ CompileTimeArithmetic() {}
+ virtual ~CompileTimeArithmetic() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
+
+class Constexpr : public ::testing::Test {
+ protected:
+ Constexpr() {}
+ virtual ~Constexpr() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
+
+class CaseStudies : public ::testing::Test {
+ protected:
+ CaseStudies() {}
+ virtual ~CaseStudies() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+
+ struct RightTriangle {
+ using a = unit_value_t<meters, 3>;
+ using b = unit_value_t<meters, 4>;
+ using c = unit_value_sqrt<
+ unit_value_add<unit_value_power<a, 2>, unit_value_power<b, 2>>>;
+ };
+};
+} // namespace
+
+TEST_F(TypeTraits, isRatio) {
+ EXPECT_TRUE(traits::is_ratio<std::ratio<1>>::value);
+ EXPECT_FALSE(traits::is_ratio<double>::value);
+}
+
+TEST_F(TypeTraits, ratio_sqrt) {
+ using rt2 = ratio_sqrt<std::ratio<2>>;
+ EXPECT_LT(std::abs(std::sqrt(2 / static_cast<double>(1)) -
+ rt2::num / static_cast<double>(rt2::den)),
+ 5e-9);
+
+ using rt4 = ratio_sqrt<std::ratio<4>>;
+ EXPECT_LT(std::abs(std::sqrt(4 / static_cast<double>(1)) -
+ rt4::num / static_cast<double>(rt4::den)),
+ 5e-9);
+
+ using rt10 = ratio_sqrt<std::ratio<10>>;
+ EXPECT_LT(std::abs(std::sqrt(10 / static_cast<double>(1)) -
+ rt10::num / static_cast<double>(rt10::den)),
+ 5e-9);
+
+ using rt30 = ratio_sqrt<std::ratio<30>>;
+ EXPECT_LT(std::abs(std::sqrt(30 / static_cast<double>(1)) -
+ rt30::num / static_cast<double>(rt30::den)),
+ 5e-9);
+
+ using rt61 = ratio_sqrt<std::ratio<61>>;
+ EXPECT_LT(std::abs(std::sqrt(61 / static_cast<double>(1)) -
+ rt61::num / static_cast<double>(rt61::den)),
+ 5e-9);
+
+ using rt100 = ratio_sqrt<std::ratio<100>>;
+ EXPECT_LT(std::abs(std::sqrt(100 / static_cast<double>(1)) -
+ rt100::num / static_cast<double>(rt100::den)),
+ 5e-9);
+
+ using rt1000 = ratio_sqrt<std::ratio<1000>>;
+ EXPECT_LT(std::abs(std::sqrt(1000 / static_cast<double>(1)) -
+ rt1000::num / static_cast<double>(rt1000::den)),
+ 5e-9);
+
+ using rt10000 = ratio_sqrt<std::ratio<10000>>;
+ EXPECT_LT(std::abs(std::sqrt(10000 / static_cast<double>(1)) -
+ rt10000::num / static_cast<double>(rt10000::den)),
+ 5e-9);
+}
+
+TEST_F(TypeTraits, is_unit) {
+ EXPECT_FALSE(traits::is_unit<std::ratio<1>>::value);
+ EXPECT_FALSE(traits::is_unit<double>::value);
+ EXPECT_TRUE(traits::is_unit<meters>::value);
+ EXPECT_TRUE(traits::is_unit<feet>::value);
+ EXPECT_TRUE(traits::is_unit<degrees_squared>::value);
+ EXPECT_FALSE(traits::is_unit<meter_t>::value);
+}
+
+TEST_F(TypeTraits, is_unit_t) {
+ EXPECT_FALSE(traits::is_unit_t<std::ratio<1>>::value);
+ EXPECT_FALSE(traits::is_unit_t<double>::value);
+ EXPECT_FALSE(traits::is_unit_t<meters>::value);
+ EXPECT_FALSE(traits::is_unit_t<feet>::value);
+ EXPECT_FALSE(traits::is_unit_t<degrees_squared>::value);
+ EXPECT_TRUE(traits::is_unit_t<meter_t>::value);
+}
+
+TEST_F(TypeTraits, unit_traits) {
+ EXPECT_TRUE(
+ (std::is_same<void,
+ traits::unit_traits<double>::conversion_ratio>::value));
+ EXPECT_FALSE(
+ (std::is_same<void,
+ traits::unit_traits<meters>::conversion_ratio>::value));
+}
+
+TEST_F(TypeTraits, unit_t_traits) {
+ EXPECT_TRUE(
+ (std::is_same<void,
+ traits::unit_t_traits<double>::underlying_type>::value));
+ EXPECT_TRUE(
+ (std::is_same<UNIT_LIB_DEFAULT_TYPE,
+ traits::unit_t_traits<meter_t>::underlying_type>::value));
+ EXPECT_TRUE(
+ (std::is_same<void, traits::unit_t_traits<double>::value_type>::value));
+ EXPECT_TRUE(
+ (std::is_same<UNIT_LIB_DEFAULT_TYPE,
+ traits::unit_t_traits<meter_t>::value_type>::value));
+}
+
+TEST_F(TypeTraits, all_true) {
+ EXPECT_TRUE(all_true<true>::type::value);
+ EXPECT_TRUE((all_true<true, true>::type::value));
+ EXPECT_TRUE((all_true<true, true, true>::type::value));
+ EXPECT_FALSE(all_true<false>::type::value);
+ EXPECT_FALSE((all_true<true, false>::type::value));
+ EXPECT_FALSE((all_true<true, true, false>::type::value));
+ EXPECT_FALSE((all_true<false, false, false>::type::value));
+}
+
+TEST_F(TypeTraits, is_convertible_unit) {
+ EXPECT_TRUE((traits::is_convertible_unit<meters, meters>::value));
+ EXPECT_TRUE((traits::is_convertible_unit<meters, astronicalUnits>::value));
+ EXPECT_TRUE((traits::is_convertible_unit<meters, parsecs>::value));
+
+ EXPECT_TRUE((traits::is_convertible_unit<meters, meters>::value));
+ EXPECT_TRUE((traits::is_convertible_unit<astronicalUnits, meters>::value));
+ EXPECT_TRUE((traits::is_convertible_unit<parsecs, meters>::value));
+ EXPECT_TRUE((traits::is_convertible_unit<years, weeks>::value));
+
+ EXPECT_FALSE((traits::is_convertible_unit<meters, seconds>::value));
+ EXPECT_FALSE((traits::is_convertible_unit<seconds, meters>::value));
+ EXPECT_FALSE((traits::is_convertible_unit<years, meters>::value));
+}
+
+TEST_F(TypeTraits, inverse) {
+ double test;
+
+ using htz = inverse<seconds>;
+ bool shouldBeTrue = std::is_same<htz, hertz>::value;
+ EXPECT_TRUE(shouldBeTrue);
+
+ test = convert<inverse<celsius>, inverse<fahrenheit>>(1.0);
+ EXPECT_NEAR(5.0 / 9.0, test, 5.0e-5);
+
+ test = convert<inverse<kelvin>, inverse<fahrenheit>>(6.0);
+ EXPECT_NEAR(10.0 / 3.0, test, 5.0e-5);
+}
+
+TEST_F(TypeTraits, base_unit_of) {
+ using base = traits::base_unit_of<years>;
+ bool shouldBeTrue = std::is_same<base, category::time_unit>::value;
+
+ EXPECT_TRUE(shouldBeTrue);
+}
+
+TEST_F(TypeTraits, has_linear_scale) {
+ EXPECT_TRUE((traits::has_linear_scale<scalar_t>::value));
+ EXPECT_TRUE((traits::has_linear_scale<meter_t>::value));
+ EXPECT_TRUE((traits::has_linear_scale<foot_t>::value));
+ EXPECT_TRUE((traits::has_linear_scale<watt_t, scalar_t>::value));
+ EXPECT_TRUE((traits::has_linear_scale<scalar_t, meter_t>::value));
+ EXPECT_TRUE((traits::has_linear_scale<meters_per_second_t>::value));
+ EXPECT_FALSE((traits::has_linear_scale<dB_t>::value));
+ EXPECT_FALSE((traits::has_linear_scale<dB_t, meters_per_second_t>::value));
+}
+
+TEST_F(TypeTraits, has_decibel_scale) {
+ EXPECT_FALSE((traits::has_decibel_scale<scalar_t>::value));
+ EXPECT_FALSE((traits::has_decibel_scale<meter_t>::value));
+ EXPECT_FALSE((traits::has_decibel_scale<foot_t>::value));
+ EXPECT_TRUE((traits::has_decibel_scale<dB_t>::value));
+ EXPECT_TRUE((traits::has_decibel_scale<dBW_t>::value));
+
+ EXPECT_TRUE((traits::has_decibel_scale<dBW_t, dB_t>::value));
+ EXPECT_TRUE((traits::has_decibel_scale<dBW_t, dBm_t>::value));
+ EXPECT_TRUE((traits::has_decibel_scale<dB_t, dB_t>::value));
+ EXPECT_TRUE((traits::has_decibel_scale<dB_t, dB_t, dB_t>::value));
+ EXPECT_FALSE((traits::has_decibel_scale<dB_t, dB_t, meter_t>::value));
+ EXPECT_FALSE((traits::has_decibel_scale<meter_t, dB_t>::value));
+}
+
+TEST_F(TypeTraits, is_same_scale) {
+ EXPECT_TRUE((traits::is_same_scale<scalar_t, dimensionless_t>::value));
+ EXPECT_TRUE((traits::is_same_scale<dB_t, dBW_t>::value));
+ EXPECT_FALSE((traits::is_same_scale<dB_t, scalar_t>::value));
+}
+
+TEST_F(TypeTraits, is_dimensionless_unit) {
+ EXPECT_TRUE((traits::is_dimensionless_unit<scalar_t>::value));
+ EXPECT_TRUE((traits::is_dimensionless_unit<const scalar_t>::value));
+ EXPECT_TRUE((traits::is_dimensionless_unit<const scalar_t&>::value));
+ EXPECT_TRUE((traits::is_dimensionless_unit<dimensionless_t>::value));
+ EXPECT_TRUE((traits::is_dimensionless_unit<dB_t>::value));
+ EXPECT_TRUE((traits::is_dimensionless_unit<dB_t, scalar_t>::value));
+ EXPECT_TRUE((traits::is_dimensionless_unit<ppm_t>::value));
+ EXPECT_FALSE((traits::is_dimensionless_unit<meter_t>::value));
+ EXPECT_FALSE((traits::is_dimensionless_unit<dBW_t>::value));
+ EXPECT_FALSE((traits::is_dimensionless_unit<dBW_t, scalar_t>::value));
+}
+
+TEST_F(TypeTraits, is_length_unit) {
+ EXPECT_TRUE((traits::is_length_unit<meter>::value));
+ EXPECT_TRUE((traits::is_length_unit<cubit>::value));
+ EXPECT_FALSE((traits::is_length_unit<year>::value));
+ EXPECT_FALSE((traits::is_length_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_length_unit<meter_t>::value));
+ EXPECT_TRUE((traits::is_length_unit<const meter_t>::value));
+ EXPECT_TRUE((traits::is_length_unit<const meter_t&>::value));
+ EXPECT_TRUE((traits::is_length_unit<cubit_t>::value));
+ EXPECT_FALSE((traits::is_length_unit<year_t>::value));
+ EXPECT_TRUE((traits::is_length_unit<meter_t, cubit_t>::value));
+ EXPECT_FALSE((traits::is_length_unit<meter_t, year_t>::value));
+}
+
+TEST_F(TypeTraits, is_mass_unit) {
+ EXPECT_TRUE((traits::is_mass_unit<kilogram>::value));
+ EXPECT_TRUE((traits::is_mass_unit<stone>::value));
+ EXPECT_FALSE((traits::is_mass_unit<meter>::value));
+ EXPECT_FALSE((traits::is_mass_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_mass_unit<kilogram_t>::value));
+ EXPECT_TRUE((traits::is_mass_unit<const kilogram_t>::value));
+ EXPECT_TRUE((traits::is_mass_unit<const kilogram_t&>::value));
+ EXPECT_TRUE((traits::is_mass_unit<stone_t>::value));
+ EXPECT_FALSE((traits::is_mass_unit<meter_t>::value));
+ EXPECT_TRUE((traits::is_mass_unit<kilogram_t, stone_t>::value));
+ EXPECT_FALSE((traits::is_mass_unit<kilogram_t, meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_time_unit) {
+ EXPECT_TRUE((traits::is_time_unit<second>::value));
+ EXPECT_TRUE((traits::is_time_unit<year>::value));
+ EXPECT_FALSE((traits::is_time_unit<meter>::value));
+ EXPECT_FALSE((traits::is_time_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_time_unit<second_t>::value));
+ EXPECT_TRUE((traits::is_time_unit<const second_t>::value));
+ EXPECT_TRUE((traits::is_time_unit<const second_t&>::value));
+ EXPECT_TRUE((traits::is_time_unit<year_t>::value));
+ EXPECT_FALSE((traits::is_time_unit<meter_t>::value));
+ EXPECT_TRUE((traits::is_time_unit<second_t, year_t>::value));
+ EXPECT_FALSE((traits::is_time_unit<second_t, meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_angle_unit) {
+ EXPECT_TRUE((traits::is_angle_unit<angle::radian>::value));
+ EXPECT_TRUE((traits::is_angle_unit<angle::degree>::value));
+ EXPECT_FALSE((traits::is_angle_unit<watt>::value));
+ EXPECT_FALSE((traits::is_angle_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_angle_unit<angle::radian_t>::value));
+ EXPECT_TRUE((traits::is_angle_unit<const angle::radian_t>::value));
+ EXPECT_TRUE((traits::is_angle_unit<const angle::radian_t&>::value));
+ EXPECT_TRUE((traits::is_angle_unit<angle::degree_t>::value));
+ EXPECT_FALSE((traits::is_angle_unit<watt_t>::value));
+ EXPECT_TRUE((traits::is_angle_unit<angle::radian_t, angle::degree_t>::value));
+ EXPECT_FALSE((traits::is_angle_unit<angle::radian_t, watt_t>::value));
+}
+
+TEST_F(TypeTraits, is_current_unit) {
+ EXPECT_TRUE((traits::is_current_unit<current::ampere>::value));
+ EXPECT_FALSE((traits::is_current_unit<volt>::value));
+ EXPECT_FALSE((traits::is_current_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_current_unit<current::ampere_t>::value));
+ EXPECT_TRUE((traits::is_current_unit<const current::ampere_t>::value));
+ EXPECT_TRUE((traits::is_current_unit<const current::ampere_t&>::value));
+ EXPECT_FALSE((traits::is_current_unit<volt_t>::value));
+ EXPECT_TRUE((traits::is_current_unit<current::ampere_t,
+ current::milliampere_t>::value));
+ EXPECT_FALSE((traits::is_current_unit<current::ampere_t, volt_t>::value));
+}
+
+TEST_F(TypeTraits, is_temperature_unit) {
+ EXPECT_TRUE((traits::is_temperature_unit<fahrenheit>::value));
+ EXPECT_TRUE((traits::is_temperature_unit<kelvin>::value));
+ EXPECT_FALSE((traits::is_temperature_unit<cubit>::value));
+ EXPECT_FALSE((traits::is_temperature_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_temperature_unit<fahrenheit_t>::value));
+ EXPECT_TRUE((traits::is_temperature_unit<const fahrenheit_t>::value));
+ EXPECT_TRUE((traits::is_temperature_unit<const fahrenheit_t&>::value));
+ EXPECT_TRUE((traits::is_temperature_unit<kelvin_t>::value));
+ EXPECT_FALSE((traits::is_temperature_unit<cubit_t>::value));
+ EXPECT_TRUE((traits::is_temperature_unit<fahrenheit_t, kelvin_t>::value));
+ EXPECT_FALSE((traits::is_temperature_unit<cubit_t, fahrenheit_t>::value));
+}
+
+TEST_F(TypeTraits, is_substance_unit) {
+ EXPECT_TRUE((traits::is_substance_unit<substance::mol>::value));
+ EXPECT_FALSE((traits::is_substance_unit<year>::value));
+ EXPECT_FALSE((traits::is_substance_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_substance_unit<substance::mole_t>::value));
+ EXPECT_TRUE((traits::is_substance_unit<const substance::mole_t>::value));
+ EXPECT_TRUE((traits::is_substance_unit<const substance::mole_t&>::value));
+ EXPECT_FALSE((traits::is_substance_unit<year_t>::value));
+ EXPECT_TRUE(
+ (traits::is_substance_unit<substance::mole_t, substance::mole_t>::value));
+ EXPECT_FALSE((traits::is_substance_unit<year_t, substance::mole_t>::value));
+}
+
+TEST_F(TypeTraits, is_luminous_intensity_unit) {
+ EXPECT_TRUE((traits::is_luminous_intensity_unit<candela>::value));
+ EXPECT_FALSE(
+ (traits::is_luminous_intensity_unit<units::radiation::rad>::value));
+ EXPECT_FALSE((traits::is_luminous_intensity_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_luminous_intensity_unit<candela_t>::value));
+ EXPECT_TRUE((traits::is_luminous_intensity_unit<const candela_t>::value));
+ EXPECT_TRUE((traits::is_luminous_intensity_unit<const candela_t&>::value));
+ EXPECT_FALSE((traits::is_luminous_intensity_unit<rad_t>::value));
+ EXPECT_TRUE(
+ (traits::is_luminous_intensity_unit<candela_t, candela_t>::value));
+ EXPECT_FALSE((traits::is_luminous_intensity_unit<rad_t, candela_t>::value));
+}
+
+TEST_F(TypeTraits, is_solid_angle_unit) {
+ EXPECT_TRUE((traits::is_solid_angle_unit<steradian>::value));
+ EXPECT_TRUE((traits::is_solid_angle_unit<degree_squared>::value));
+ EXPECT_FALSE((traits::is_solid_angle_unit<angle::degree>::value));
+ EXPECT_FALSE((traits::is_solid_angle_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_solid_angle_unit<steradian_t>::value));
+ EXPECT_TRUE((traits::is_solid_angle_unit<const steradian_t>::value));
+ EXPECT_TRUE((traits::is_solid_angle_unit<const degree_squared_t&>::value));
+ EXPECT_FALSE((traits::is_solid_angle_unit<angle::degree_t>::value));
+ EXPECT_TRUE(
+ (traits::is_solid_angle_unit<degree_squared_t, steradian_t>::value));
+ EXPECT_FALSE(
+ (traits::is_solid_angle_unit<angle::degree_t, steradian_t>::value));
+}
+
+TEST_F(TypeTraits, is_frequency_unit) {
+ EXPECT_TRUE((traits::is_frequency_unit<hertz>::value));
+ EXPECT_FALSE((traits::is_frequency_unit<second>::value));
+ EXPECT_FALSE((traits::is_frequency_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_frequency_unit<hertz_t>::value));
+ EXPECT_TRUE((traits::is_frequency_unit<const hertz_t>::value));
+ EXPECT_TRUE((traits::is_frequency_unit<const hertz_t&>::value));
+ EXPECT_FALSE((traits::is_frequency_unit<second_t>::value));
+ EXPECT_TRUE((traits::is_frequency_unit<const hertz_t&, gigahertz_t>::value));
+ EXPECT_FALSE((traits::is_frequency_unit<second_t, hertz_t>::value));
+}
+
+TEST_F(TypeTraits, is_velocity_unit) {
+ EXPECT_TRUE((traits::is_velocity_unit<meters_per_second>::value));
+ EXPECT_TRUE((traits::is_velocity_unit<miles_per_hour>::value));
+ EXPECT_FALSE((traits::is_velocity_unit<meters_per_second_squared>::value));
+ EXPECT_FALSE((traits::is_velocity_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_velocity_unit<meters_per_second_t>::value));
+ EXPECT_TRUE((traits::is_velocity_unit<const meters_per_second_t>::value));
+ EXPECT_TRUE((traits::is_velocity_unit<const meters_per_second_t&>::value));
+ EXPECT_TRUE((traits::is_velocity_unit<miles_per_hour_t>::value));
+ EXPECT_FALSE((traits::is_velocity_unit<meters_per_second_squared_t>::value));
+ EXPECT_TRUE(
+ (traits::is_velocity_unit<miles_per_hour_t, meters_per_second_t>::value));
+ EXPECT_FALSE((traits::is_velocity_unit<meters_per_second_squared_t,
+ meters_per_second_t>::value));
+}
+
+TEST_F(TypeTraits, is_acceleration_unit) {
+ EXPECT_TRUE((traits::is_acceleration_unit<meters_per_second_squared>::value));
+ EXPECT_TRUE(
+ (traits::is_acceleration_unit<acceleration::standard_gravity>::value));
+ EXPECT_FALSE((traits::is_acceleration_unit<inch>::value));
+ EXPECT_FALSE((traits::is_acceleration_unit<double>::value));
+
+ EXPECT_TRUE(
+ (traits::is_acceleration_unit<meters_per_second_squared_t>::value));
+ EXPECT_TRUE(
+ (traits::is_acceleration_unit<const meters_per_second_squared_t>::value));
+ EXPECT_TRUE((
+ traits::is_acceleration_unit<const meters_per_second_squared_t&>::value));
+ EXPECT_TRUE((traits::is_acceleration_unit<standard_gravity_t>::value));
+ EXPECT_FALSE((traits::is_acceleration_unit<inch_t>::value));
+ EXPECT_TRUE(
+ (traits::is_acceleration_unit<standard_gravity_t,
+ meters_per_second_squared_t>::value));
+ EXPECT_FALSE(
+ (traits::is_acceleration_unit<inch_t,
+ meters_per_second_squared_t>::value));
+}
+
+TEST_F(TypeTraits, is_force_unit) {
+ EXPECT_TRUE((traits::is_force_unit<units::force::newton>::value));
+ EXPECT_TRUE((traits::is_force_unit<units::force::dynes>::value));
+ EXPECT_FALSE((traits::is_force_unit<meter>::value));
+ EXPECT_FALSE((traits::is_force_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_force_unit<units::force::newton_t>::value));
+ EXPECT_TRUE((traits::is_force_unit<const units::force::newton_t>::value));
+ EXPECT_TRUE((traits::is_force_unit<const units::force::newton_t&>::value));
+ EXPECT_TRUE((traits::is_force_unit<units::force::dyne_t>::value));
+ EXPECT_FALSE((traits::is_force_unit<watt_t>::value));
+ EXPECT_TRUE((traits::is_force_unit<units::force::dyne_t,
+ units::force::newton_t>::value));
+ EXPECT_FALSE((traits::is_force_unit<watt_t, units::force::newton_t>::value));
+}
+
+TEST_F(TypeTraits, is_pressure_unit) {
+ EXPECT_TRUE((traits::is_pressure_unit<pressure::pascals>::value));
+ EXPECT_TRUE((traits::is_pressure_unit<atmosphere>::value));
+ EXPECT_FALSE((traits::is_pressure_unit<year>::value));
+ EXPECT_FALSE((traits::is_pressure_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_pressure_unit<pascal_t>::value));
+ EXPECT_TRUE((traits::is_pressure_unit<const pascal_t>::value));
+ EXPECT_TRUE((traits::is_pressure_unit<const pascal_t&>::value));
+ EXPECT_TRUE((traits::is_pressure_unit<atmosphere_t>::value));
+ EXPECT_FALSE((traits::is_pressure_unit<year_t>::value));
+ EXPECT_TRUE(
+ (traits::is_pressure_unit<atmosphere_t, pressure::pascal_t>::value));
+ EXPECT_FALSE((traits::is_pressure_unit<year_t, pressure::pascal_t>::value));
+}
+
+TEST_F(TypeTraits, is_charge_unit) {
+ EXPECT_TRUE((traits::is_charge_unit<coulomb>::value));
+ EXPECT_FALSE((traits::is_charge_unit<watt>::value));
+ EXPECT_FALSE((traits::is_charge_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_charge_unit<coulomb_t>::value));
+ EXPECT_TRUE((traits::is_charge_unit<const coulomb_t>::value));
+ EXPECT_TRUE((traits::is_charge_unit<const coulomb_t&>::value));
+ EXPECT_FALSE((traits::is_charge_unit<watt_t>::value));
+ EXPECT_TRUE((traits::is_charge_unit<const coulomb_t&, coulomb_t>::value));
+ EXPECT_FALSE((traits::is_charge_unit<watt_t, coulomb_t>::value));
+}
+
+TEST_F(TypeTraits, is_energy_unit) {
+ EXPECT_TRUE((traits::is_energy_unit<joule>::value));
+ EXPECT_TRUE((traits::is_energy_unit<calorie>::value));
+ EXPECT_FALSE((traits::is_energy_unit<watt>::value));
+ EXPECT_FALSE((traits::is_energy_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_energy_unit<joule_t>::value));
+ EXPECT_TRUE((traits::is_energy_unit<const joule_t>::value));
+ EXPECT_TRUE((traits::is_energy_unit<const joule_t&>::value));
+ EXPECT_TRUE((traits::is_energy_unit<calorie_t>::value));
+ EXPECT_FALSE((traits::is_energy_unit<watt_t>::value));
+ EXPECT_TRUE((traits::is_energy_unit<calorie_t, joule_t>::value));
+ EXPECT_FALSE((traits::is_energy_unit<watt_t, joule_t>::value));
+}
+
+TEST_F(TypeTraits, is_power_unit) {
+ EXPECT_TRUE((traits::is_power_unit<watt>::value));
+ EXPECT_FALSE((traits::is_power_unit<henry>::value));
+ EXPECT_FALSE((traits::is_power_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_power_unit<watt_t>::value));
+ EXPECT_TRUE((traits::is_power_unit<const watt_t>::value));
+ EXPECT_TRUE((traits::is_power_unit<const watt_t&>::value));
+ EXPECT_FALSE((traits::is_power_unit<henry_t>::value));
+ EXPECT_TRUE((traits::is_power_unit<const watt_t&, watt_t>::value));
+ EXPECT_FALSE((traits::is_power_unit<henry_t, watt_t>::value));
+}
+
+TEST_F(TypeTraits, is_voltage_unit) {
+ EXPECT_TRUE((traits::is_voltage_unit<volt>::value));
+ EXPECT_FALSE((traits::is_voltage_unit<henry>::value));
+ EXPECT_FALSE((traits::is_voltage_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_voltage_unit<volt_t>::value));
+ EXPECT_TRUE((traits::is_voltage_unit<const volt_t>::value));
+ EXPECT_TRUE((traits::is_voltage_unit<const volt_t&>::value));
+ EXPECT_FALSE((traits::is_voltage_unit<henry_t>::value));
+ EXPECT_TRUE((traits::is_voltage_unit<const volt_t&, volt_t>::value));
+ EXPECT_FALSE((traits::is_voltage_unit<henry_t, volt_t>::value));
+}
+
+TEST_F(TypeTraits, is_capacitance_unit) {
+ EXPECT_TRUE((traits::is_capacitance_unit<farad>::value));
+ EXPECT_FALSE((traits::is_capacitance_unit<ohm>::value));
+ EXPECT_FALSE((traits::is_capacitance_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_capacitance_unit<farad_t>::value));
+ EXPECT_TRUE((traits::is_capacitance_unit<const farad_t>::value));
+ EXPECT_TRUE((traits::is_capacitance_unit<const farad_t&>::value));
+ EXPECT_FALSE((traits::is_capacitance_unit<ohm_t>::value));
+ EXPECT_TRUE(
+ (traits::is_capacitance_unit<const farad_t&, millifarad_t>::value));
+ EXPECT_FALSE((traits::is_capacitance_unit<ohm_t, farad_t>::value));
+}
+
+TEST_F(TypeTraits, is_impedance_unit) {
+ EXPECT_TRUE((traits::is_impedance_unit<ohm>::value));
+ EXPECT_FALSE((traits::is_impedance_unit<farad>::value));
+ EXPECT_FALSE((traits::is_impedance_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_impedance_unit<ohm_t>::value));
+ EXPECT_TRUE((traits::is_impedance_unit<const ohm_t>::value));
+ EXPECT_TRUE((traits::is_impedance_unit<const ohm_t&>::value));
+ EXPECT_FALSE((traits::is_impedance_unit<farad_t>::value));
+ EXPECT_TRUE((traits::is_impedance_unit<const ohm_t&, milliohm_t>::value));
+ EXPECT_FALSE((traits::is_impedance_unit<farad_t, ohm_t>::value));
+}
+
+TEST_F(TypeTraits, is_conductance_unit) {
+ EXPECT_TRUE((traits::is_conductance_unit<siemens>::value));
+ EXPECT_FALSE((traits::is_conductance_unit<volt>::value));
+ EXPECT_FALSE((traits::is_conductance_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_conductance_unit<siemens_t>::value));
+ EXPECT_TRUE((traits::is_conductance_unit<const siemens_t>::value));
+ EXPECT_TRUE((traits::is_conductance_unit<const siemens_t&>::value));
+ EXPECT_FALSE((traits::is_conductance_unit<volt_t>::value));
+ EXPECT_TRUE(
+ (traits::is_conductance_unit<const siemens_t&, millisiemens_t>::value));
+ EXPECT_FALSE((traits::is_conductance_unit<volt_t, siemens_t>::value));
+}
+
+TEST_F(TypeTraits, is_magnetic_flux_unit) {
+ EXPECT_TRUE((traits::is_magnetic_flux_unit<weber>::value));
+ EXPECT_TRUE((traits::is_magnetic_flux_unit<maxwell>::value));
+ EXPECT_FALSE((traits::is_magnetic_flux_unit<inch>::value));
+ EXPECT_FALSE((traits::is_magnetic_flux_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_magnetic_flux_unit<weber_t>::value));
+ EXPECT_TRUE((traits::is_magnetic_flux_unit<const weber_t>::value));
+ EXPECT_TRUE((traits::is_magnetic_flux_unit<const weber_t&>::value));
+ EXPECT_TRUE((traits::is_magnetic_flux_unit<maxwell_t>::value));
+ EXPECT_FALSE((traits::is_magnetic_flux_unit<inch_t>::value));
+ EXPECT_TRUE((traits::is_magnetic_flux_unit<maxwell_t, weber_t>::value));
+ EXPECT_FALSE((traits::is_magnetic_flux_unit<inch_t, weber_t>::value));
+}
+
+TEST_F(TypeTraits, is_magnetic_field_strength_unit) {
+ EXPECT_TRUE((traits::is_magnetic_field_strength_unit<
+ units::magnetic_field_strength::tesla>::value));
+ EXPECT_TRUE((traits::is_magnetic_field_strength_unit<gauss>::value));
+ EXPECT_FALSE((traits::is_magnetic_field_strength_unit<volt>::value));
+ EXPECT_FALSE((traits::is_magnetic_field_strength_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_magnetic_field_strength_unit<tesla_t>::value));
+ EXPECT_TRUE((traits::is_magnetic_field_strength_unit<const tesla_t>::value));
+ EXPECT_TRUE((traits::is_magnetic_field_strength_unit<const tesla_t&>::value));
+ EXPECT_TRUE((traits::is_magnetic_field_strength_unit<gauss_t>::value));
+ EXPECT_FALSE((traits::is_magnetic_field_strength_unit<volt_t>::value));
+ EXPECT_TRUE(
+ (traits::is_magnetic_field_strength_unit<gauss_t, tesla_t>::value));
+ EXPECT_FALSE(
+ (traits::is_magnetic_field_strength_unit<volt_t, tesla_t>::value));
+}
+
+TEST_F(TypeTraits, is_inductance_unit) {
+ EXPECT_TRUE((traits::is_inductance_unit<henry>::value));
+ EXPECT_FALSE((traits::is_inductance_unit<farad>::value));
+ EXPECT_FALSE((traits::is_inductance_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_inductance_unit<henry_t>::value));
+ EXPECT_TRUE((traits::is_inductance_unit<const henry_t>::value));
+ EXPECT_TRUE((traits::is_inductance_unit<const henry_t&>::value));
+ EXPECT_FALSE((traits::is_inductance_unit<farad_t>::value));
+ EXPECT_TRUE(
+ (traits::is_inductance_unit<const henry_t&, millihenry_t>::value));
+ EXPECT_FALSE((traits::is_inductance_unit<farad_t, henry_t>::value));
+}
+
+TEST_F(TypeTraits, is_luminous_flux_unit) {
+ EXPECT_TRUE((traits::is_luminous_flux_unit<lumen>::value));
+ EXPECT_FALSE((traits::is_luminous_flux_unit<pound>::value));
+ EXPECT_FALSE((traits::is_luminous_flux_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_luminous_flux_unit<lumen_t>::value));
+ EXPECT_TRUE((traits::is_luminous_flux_unit<const lumen_t>::value));
+ EXPECT_TRUE((traits::is_luminous_flux_unit<const lumen_t&>::value));
+ EXPECT_FALSE((traits::is_luminous_flux_unit<pound_t>::value));
+ EXPECT_TRUE(
+ (traits::is_luminous_flux_unit<const lumen_t&, millilumen_t>::value));
+ EXPECT_FALSE((traits::is_luminous_flux_unit<pound_t, lumen_t>::value));
+}
+
+TEST_F(TypeTraits, is_illuminance_unit) {
+ EXPECT_TRUE((traits::is_illuminance_unit<illuminance::footcandle>::value));
+ EXPECT_TRUE((traits::is_illuminance_unit<illuminance::lux>::value));
+ EXPECT_FALSE((traits::is_illuminance_unit<meter>::value));
+ EXPECT_FALSE((traits::is_illuminance_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_illuminance_unit<footcandle_t>::value));
+ EXPECT_TRUE((traits::is_illuminance_unit<const footcandle_t>::value));
+ EXPECT_TRUE((traits::is_illuminance_unit<const footcandle_t&>::value));
+ EXPECT_TRUE((traits::is_illuminance_unit<lux_t>::value));
+ EXPECT_FALSE((traits::is_illuminance_unit<meter_t>::value));
+ EXPECT_TRUE((traits::is_illuminance_unit<lux_t, footcandle_t>::value));
+ EXPECT_FALSE((traits::is_illuminance_unit<meter_t, footcandle_t>::value));
+}
+
+TEST_F(TypeTraits, is_radioactivity_unit) {
+ EXPECT_TRUE((traits::is_radioactivity_unit<becquerel>::value));
+ EXPECT_FALSE((traits::is_radioactivity_unit<year>::value));
+ EXPECT_FALSE((traits::is_radioactivity_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_radioactivity_unit<becquerel_t>::value));
+ EXPECT_TRUE((traits::is_radioactivity_unit<const becquerel_t>::value));
+ EXPECT_TRUE((traits::is_radioactivity_unit<const becquerel_t&>::value));
+ EXPECT_FALSE((traits::is_radioactivity_unit<year_t>::value));
+ EXPECT_TRUE((traits::is_radioactivity_unit<const becquerel_t&,
+ millibecquerel_t>::value));
+ EXPECT_FALSE((traits::is_radioactivity_unit<year_t, becquerel_t>::value));
+}
+
+TEST_F(TypeTraits, is_torque_unit) {
+ EXPECT_TRUE((traits::is_torque_unit<torque::newton_meter>::value));
+ EXPECT_TRUE((traits::is_torque_unit<torque::foot_pound>::value));
+ EXPECT_FALSE((traits::is_torque_unit<volume::cubic_meter>::value));
+ EXPECT_FALSE((traits::is_torque_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_torque_unit<torque::newton_meter_t>::value));
+ EXPECT_TRUE((traits::is_torque_unit<const torque::newton_meter_t>::value));
+ EXPECT_TRUE((traits::is_torque_unit<const torque::newton_meter_t&>::value));
+ EXPECT_TRUE((traits::is_torque_unit<torque::foot_pound_t>::value));
+ EXPECT_FALSE((traits::is_torque_unit<volume::cubic_meter_t>::value));
+ EXPECT_TRUE((traits::is_torque_unit<torque::foot_pound_t,
+ torque::newton_meter_t>::value));
+ EXPECT_FALSE((traits::is_torque_unit<volume::cubic_meter_t,
+ torque::newton_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_area_unit) {
+ EXPECT_TRUE((traits::is_area_unit<square_meter>::value));
+ EXPECT_TRUE((traits::is_area_unit<hectare>::value));
+ EXPECT_FALSE((traits::is_area_unit<astronicalUnit>::value));
+ EXPECT_FALSE((traits::is_area_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_area_unit<square_meter_t>::value));
+ EXPECT_TRUE((traits::is_area_unit<const square_meter_t>::value));
+ EXPECT_TRUE((traits::is_area_unit<const square_meter_t&>::value));
+ EXPECT_TRUE((traits::is_area_unit<hectare_t>::value));
+ EXPECT_FALSE((traits::is_area_unit<astronicalUnit_t>::value));
+ EXPECT_TRUE((traits::is_area_unit<hectare_t, square_meter_t>::value));
+ EXPECT_FALSE((traits::is_area_unit<astronicalUnit_t, square_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_volume_unit) {
+ EXPECT_TRUE((traits::is_volume_unit<cubic_meter>::value));
+ EXPECT_TRUE((traits::is_volume_unit<cubic_foot>::value));
+ EXPECT_FALSE((traits::is_volume_unit<square_feet>::value));
+ EXPECT_FALSE((traits::is_volume_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_volume_unit<cubic_meter_t>::value));
+ EXPECT_TRUE((traits::is_volume_unit<const cubic_meter_t>::value));
+ EXPECT_TRUE((traits::is_volume_unit<const cubic_meter_t&>::value));
+ EXPECT_TRUE((traits::is_volume_unit<cubic_inch_t>::value));
+ EXPECT_FALSE((traits::is_volume_unit<foot_t>::value));
+ EXPECT_TRUE((traits::is_volume_unit<cubic_inch_t, cubic_meter_t>::value));
+ EXPECT_FALSE((traits::is_volume_unit<foot_t, cubic_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_density_unit) {
+ EXPECT_TRUE((traits::is_density_unit<kilograms_per_cubic_meter>::value));
+ EXPECT_TRUE((traits::is_density_unit<ounces_per_cubic_foot>::value));
+ EXPECT_FALSE((traits::is_density_unit<year>::value));
+ EXPECT_FALSE((traits::is_density_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_density_unit<kilograms_per_cubic_meter_t>::value));
+ EXPECT_TRUE(
+ (traits::is_density_unit<const kilograms_per_cubic_meter_t>::value));
+ EXPECT_TRUE(
+ (traits::is_density_unit<const kilograms_per_cubic_meter_t&>::value));
+ EXPECT_TRUE((traits::is_density_unit<ounces_per_cubic_foot_t>::value));
+ EXPECT_FALSE((traits::is_density_unit<year_t>::value));
+ EXPECT_TRUE((traits::is_density_unit<ounces_per_cubic_foot_t,
+ kilograms_per_cubic_meter_t>::value));
+ EXPECT_FALSE(
+ (traits::is_density_unit<year_t, kilograms_per_cubic_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_data_unit) {
+ EXPECT_TRUE((traits::is_data_unit<bit>::value));
+ EXPECT_TRUE((traits::is_data_unit<byte>::value));
+ EXPECT_TRUE((traits::is_data_unit<exabit>::value));
+ EXPECT_TRUE((traits::is_data_unit<exabyte>::value));
+ EXPECT_FALSE((traits::is_data_unit<year>::value));
+ EXPECT_FALSE((traits::is_data_unit<double>::value));
+
+ EXPECT_TRUE((traits::is_data_unit<bit_t>::value));
+ EXPECT_TRUE((traits::is_data_unit<const bit_t>::value));
+ EXPECT_TRUE((traits::is_data_unit<const bit_t&>::value));
+ EXPECT_TRUE((traits::is_data_unit<byte_t>::value));
+ EXPECT_FALSE((traits::is_data_unit<year_t>::value));
+ EXPECT_TRUE((traits::is_data_unit<bit_t, byte_t>::value));
+ EXPECT_FALSE((traits::is_data_unit<year_t, byte_t>::value));
+}
+
+TEST_F(TypeTraits, is_data_transfer_rate_unit) {
+ EXPECT_TRUE((traits::is_data_transfer_rate_unit<Gbps>::value));
+ EXPECT_TRUE((traits::is_data_transfer_rate_unit<GBps>::value));
+ EXPECT_FALSE((traits::is_data_transfer_rate_unit<year>::value));
+ EXPECT_FALSE((traits::is_data_transfer_rate_unit<double>::value));
+
+ EXPECT_TRUE(
+ (traits::is_data_transfer_rate_unit<gigabits_per_second_t>::value));
+ EXPECT_TRUE((
+ traits::is_data_transfer_rate_unit<const gigabytes_per_second_t>::value));
+ EXPECT_TRUE((traits::is_data_transfer_rate_unit<
+ const gigabytes_per_second_t&>::value));
+ EXPECT_TRUE(
+ (traits::is_data_transfer_rate_unit<gigabytes_per_second_t>::value));
+ EXPECT_FALSE((traits::is_data_transfer_rate_unit<year_t>::value));
+ EXPECT_TRUE(
+ (traits::is_data_transfer_rate_unit<gigabits_per_second_t,
+ gigabytes_per_second_t>::value));
+ EXPECT_FALSE(
+ (traits::is_data_transfer_rate_unit<year_t,
+ gigabytes_per_second_t>::value));
+}
+
+TEST_F(UnitManipulators, squared) {
+ double test;
+
+ test = convert<squared<meters>, square_feet>(0.092903);
+ EXPECT_NEAR(0.99999956944, test, 5.0e-12);
+
+ using scalar_2 = squared<scalar>; // this is actually nonsensical, and should
+ // also result in a scalar.
+ bool isSame =
+ std::is_same<typename std::decay<scalar_t>::type,
+ typename std::decay<unit_t<scalar_2>>::type>::value;
+ EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitManipulators, cubed) {
+ double test;
+
+ test = convert<cubed<meters>, cubic_feet>(0.0283168);
+ EXPECT_NEAR(0.999998354619, test, 5.0e-13);
+}
+
+TEST_F(UnitManipulators, square_root) {
+ double test;
+
+ test = convert<square_root<square_kilometer>, meter>(1.0);
+ EXPECT_TRUE((traits::is_convertible_unit<
+ typename std::decay<square_root<square_kilometer>>::type,
+ kilometer>::value));
+ EXPECT_NEAR(1000.0, test, 5.0e-13);
+}
+
+TEST_F(UnitManipulators, compound_unit) {
+ using acceleration1 = unit<std::ratio<1>, category::acceleration_unit>;
+ using acceleration2 =
+ compound_unit<meters, inverse<seconds>, inverse<seconds>>;
+ using acceleration3 =
+ unit<std::ratio<1>,
+ base_unit<std::ratio<1>, std::ratio<0>, std::ratio<-2>>>;
+ using acceleration4 = compound_unit<meters, inverse<squared<seconds>>>;
+ using acceleration5 = compound_unit<meters, squared<inverse<seconds>>>;
+
+ bool areSame12 = std::is_same<acceleration1, acceleration2>::value;
+ bool areSame23 = std::is_same<acceleration2, acceleration3>::value;
+ bool areSame34 = std::is_same<acceleration3, acceleration4>::value;
+ bool areSame45 = std::is_same<acceleration4, acceleration5>::value;
+
+ EXPECT_TRUE(areSame12);
+ EXPECT_TRUE(areSame23);
+ EXPECT_TRUE(areSame34);
+ EXPECT_TRUE(areSame45);
+
+ // test that thing with translations still compile
+ using arbitrary1 = compound_unit<meters, inverse<celsius>>;
+ using arbitrary2 = compound_unit<meters, celsius>;
+ using arbitrary3 = compound_unit<arbitrary1, arbitrary2>;
+ EXPECT_TRUE((std::is_same<square_meters, arbitrary3>::value));
+}
+
+TEST_F(UnitManipulators, dimensionalAnalysis) {
+ // these look like 'compound units', but the dimensional analysis can be
+ // REALLY handy if the unit types aren't know (i.e. they themselves are
+ // template parameters), as you can get the resulting unit of the operation.
+
+ using velocity = units::detail::unit_divide<meters, second>;
+ bool shouldBeTrue = std::is_same<meters_per_second, velocity>::value;
+ EXPECT_TRUE(shouldBeTrue);
+
+ using acceleration1 = unit<std::ratio<1>, category::acceleration_unit>;
+ using acceleration2 = units::detail::unit_divide<
+ meters, units::detail::unit_multiply<seconds, seconds>>;
+ shouldBeTrue = std::is_same<acceleration1, acceleration2>::value;
+ EXPECT_TRUE(shouldBeTrue);
+}
+
+#ifdef _MSC_VER
+#if (_MSC_VER >= 1900)
+TEST_F(UnitContainer, trivial) {
+ EXPECT_TRUE((std::is_trivial<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_assignable<meter_t, meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_constructible<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_copy_assignable<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_copy_constructible<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_copyable<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_default_constructible<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_destructible<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_move_assignable<meter_t>::value));
+ EXPECT_TRUE((std::is_trivially_move_constructible<meter_t>::value));
+
+ EXPECT_TRUE((std::is_trivial<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_assignable<dB_t, dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_constructible<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_copy_assignable<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_copy_constructible<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_copyable<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_default_constructible<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_destructible<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_move_assignable<dB_t>::value));
+ EXPECT_TRUE((std::is_trivially_move_constructible<dB_t>::value));
+}
+#endif
+#endif
+
+TEST_F(UnitContainer, has_value_member) {
+ EXPECT_TRUE((traits::has_value_member<linear_scale<double>, double>::value));
+ EXPECT_FALSE((traits::has_value_member<meter, double>::value));
+}
+
+TEST_F(UnitContainer, make_unit) {
+ auto dist = units::make_unit<meter_t>(5);
+ EXPECT_EQ(meter_t(5), dist);
+}
+
+TEST_F(UnitContainer, unitTypeAddition) {
+ // units
+ meter_t a_m(1.0), c_m;
+ foot_t b_ft(3.28084);
+
+ double d = convert<feet, meters>(b_ft());
+ EXPECT_NEAR(1.0, d, 5.0e-5);
+
+ c_m = a_m + b_ft;
+ EXPECT_NEAR(2.0, c_m(), 5.0e-5);
+
+ c_m = b_ft + meter_t(3);
+ EXPECT_NEAR(4.0, c_m(), 5.0e-5);
+
+ auto e_ft = b_ft + meter_t(3);
+ EXPECT_NEAR(13.12336, e_ft(), 5.0e-6);
+
+ // scalar
+ scalar_t sresult = scalar_t(1.0) + scalar_t(1.0);
+ EXPECT_NEAR(2.0, sresult, 5.0e-6);
+
+ sresult = scalar_t(1.0) + 1.0;
+ EXPECT_NEAR(2.0, sresult, 5.0e-6);
+
+ sresult = 1.0 + scalar_t(1.0);
+ EXPECT_NEAR(2.0, sresult, 5.0e-6);
+
+ d = scalar_t(1.0) + scalar_t(1.0);
+ EXPECT_NEAR(2.0, d, 5.0e-6);
+
+ d = scalar_t(1.0) + 1.0;
+ EXPECT_NEAR(2.0, d, 5.0e-6);
+
+ d = 1.0 + scalar_t(1.0);
+ EXPECT_NEAR(2.0, d, 5.0e-6);
+}
+
+TEST_F(UnitContainer, unitTypeUnaryAddition) {
+ meter_t a_m(1.0);
+
+ EXPECT_EQ(++a_m, meter_t(2));
+ EXPECT_EQ(a_m++, meter_t(2));
+ EXPECT_EQ(a_m, meter_t(3));
+ EXPECT_EQ(+a_m, meter_t(3));
+ EXPECT_EQ(a_m, meter_t(3));
+
+ dBW_t b_dBW(1.0);
+
+ EXPECT_EQ(++b_dBW, dBW_t(2));
+ EXPECT_EQ(b_dBW++, dBW_t(2));
+ EXPECT_EQ(b_dBW, dBW_t(3));
+ EXPECT_EQ(+b_dBW, dBW_t(3));
+ EXPECT_EQ(b_dBW, dBW_t(3));
+}
+
+TEST_F(UnitContainer, unitTypeSubtraction) {
+ meter_t a_m(1.0), c_m;
+ foot_t b_ft(3.28084);
+
+ c_m = a_m - b_ft;
+ EXPECT_NEAR(0.0, c_m(), 5.0e-5);
+
+ c_m = b_ft - meter_t(1);
+ EXPECT_NEAR(0.0, c_m(), 5.0e-5);
+
+ auto e_ft = b_ft - meter_t(1);
+ EXPECT_NEAR(0.0, e_ft(), 5.0e-6);
+
+ scalar_t sresult = scalar_t(1.0) - scalar_t(1.0);
+ EXPECT_NEAR(0.0, sresult, 5.0e-6);
+
+ sresult = scalar_t(1.0) - 1.0;
+ EXPECT_NEAR(0.0, sresult, 5.0e-6);
+
+ sresult = 1.0 - scalar_t(1.0);
+ EXPECT_NEAR(0.0, sresult, 5.0e-6);
+
+ double d = scalar_t(1.0) - scalar_t(1.0);
+ EXPECT_NEAR(0.0, d, 5.0e-6);
+
+ d = scalar_t(1.0) - 1.0;
+ EXPECT_NEAR(0.0, d, 5.0e-6);
+
+ d = 1.0 - scalar_t(1.0);
+ EXPECT_NEAR(0.0, d, 5.0e-6);
+}
+
+TEST_F(UnitContainer, unitTypeUnarySubtraction) {
+ meter_t a_m(4.0);
+
+ EXPECT_EQ(--a_m, meter_t(3));
+ EXPECT_EQ(a_m--, meter_t(3));
+ EXPECT_EQ(a_m, meter_t(2));
+ EXPECT_EQ(-a_m, meter_t(-2));
+ EXPECT_EQ(a_m, meter_t(2));
+
+ dBW_t b_dBW(4.0);
+
+ EXPECT_EQ(--b_dBW, dBW_t(3));
+ EXPECT_EQ(b_dBW--, dBW_t(3));
+ EXPECT_EQ(b_dBW, dBW_t(2));
+ EXPECT_EQ(-b_dBW, dBW_t(-2));
+ EXPECT_EQ(b_dBW, dBW_t(2));
+}
+
+TEST_F(UnitContainer, unitTypeMultiplication) {
+ meter_t a_m(1.0), b_m(2.0);
+ foot_t a_ft(3.28084);
+
+ auto c_m2 = a_m * b_m;
+ EXPECT_NEAR(2.0, c_m2(), 5.0e-5);
+
+ c_m2 = b_m * meter_t(2);
+ EXPECT_NEAR(4.0, c_m2(), 5.0e-5);
+
+ c_m2 = b_m * a_ft;
+ EXPECT_NEAR(2.0, c_m2(), 5.0e-5);
+
+ auto c_m = b_m * 2.0;
+ EXPECT_NEAR(4.0, c_m(), 5.0e-5);
+
+ c_m = 2.0 * b_m;
+ EXPECT_NEAR(4.0, c_m(), 5.0e-5);
+
+ double convert = scalar_t(3.14);
+ EXPECT_NEAR(3.14, convert, 5.0e-5);
+
+ scalar_t sresult = scalar_t(5.0) * scalar_t(4.0);
+ EXPECT_NEAR(20.0, sresult(), 5.0e-5);
+
+ sresult = scalar_t(5.0) * 4.0;
+ EXPECT_NEAR(20.0, sresult(), 5.0e-5);
+
+ sresult = 4.0 * scalar_t(5.0);
+ EXPECT_NEAR(20.0, sresult(), 5.0e-5);
+
+ double result = scalar_t(5.0) * scalar_t(4.0);
+ EXPECT_NEAR(20.0, result, 5.0e-5);
+
+ result = scalar_t(5.0) * 4.0;
+ EXPECT_NEAR(20.0, result, 5.0e-5);
+
+ result = 4.0 * scalar_t(5.0);
+ EXPECT_NEAR(20.0, result, 5.0e-5);
+}
+
+TEST_F(UnitContainer, unitTypeMixedUnitMultiplication) {
+ meter_t a_m(1.0);
+ foot_t b_ft(3.28084);
+ unit_t<inverse<meter>> i_m(2.0);
+
+ // resultant unit is square of leftmost unit
+ auto c_m2 = a_m * b_ft;
+ EXPECT_NEAR(1.0, c_m2(), 5.0e-5);
+
+ auto c_ft2 = b_ft * a_m;
+ EXPECT_NEAR(10.7639111056, c_ft2(), 5.0e-7);
+
+ // you can get whatever (compatible) type you want if you ask explicitly
+ square_meter_t d_m2 = b_ft * a_m;
+ EXPECT_NEAR(1.0, d_m2(), 5.0e-5);
+
+ // a unit times a sclar ends up with the same units.
+ meter_t e_m = a_m * scalar_t(3.0);
+ EXPECT_NEAR(3.0, e_m(), 5.0e-5);
+
+ e_m = scalar_t(4.0) * a_m;
+ EXPECT_NEAR(4.0, e_m(), 5.0e-5);
+
+ // unit times its inverse results in a scalar
+ scalar_t s = a_m * i_m;
+ EXPECT_NEAR(2.0, s, 5.0e-5);
+
+ c_m2 = b_ft * meter_t(2);
+ EXPECT_NEAR(2.0, c_m2(), 5.0e-5);
+
+ auto e_ft2 = b_ft * meter_t(3);
+ EXPECT_NEAR(32.2917333168, e_ft2(), 5.0e-6);
+
+ auto mps = meter_t(10.0) * unit_t<inverse<seconds>>(1.0);
+ EXPECT_EQ(mps, meters_per_second_t(10));
+}
+
+TEST_F(UnitContainer, unitTypeScalarMultiplication) {
+ meter_t a_m(1.0);
+
+ auto result_m = scalar_t(3.0) * a_m;
+ EXPECT_NEAR(3.0, result_m(), 5.0e-5);
+
+ result_m = a_m * scalar_t(4.0);
+ EXPECT_NEAR(4.0, result_m(), 5.0e-5);
+
+ result_m = 3.0 * a_m;
+ EXPECT_NEAR(3.0, result_m(), 5.0e-5);
+
+ result_m = a_m * 4.0;
+ EXPECT_NEAR(4.0, result_m(), 5.0e-5);
+
+ bool isSame = std::is_same<decltype(result_m), meter_t>::value;
+ EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitContainer, unitTypeDivision) {
+ meter_t a_m(1.0), b_m(2.0);
+ foot_t a_ft(3.28084);
+ second_t a_sec(10.0);
+ bool isSame;
+
+ auto c = a_m / a_ft;
+ EXPECT_NEAR(1.0, c, 5.0e-5);
+ isSame = std::is_same<decltype(c), scalar_t>::value;
+ EXPECT_TRUE(isSame);
+
+ c = a_m / b_m;
+ EXPECT_NEAR(0.5, c, 5.0e-5);
+ isSame = std::is_same<decltype(c), scalar_t>::value;
+ EXPECT_TRUE(isSame);
+
+ c = a_ft / a_m;
+ EXPECT_NEAR(1.0, c, 5.0e-5);
+ isSame = std::is_same<decltype(c), scalar_t>::value;
+ EXPECT_TRUE(isSame);
+
+ c = scalar_t(1.0) / 2.0;
+ EXPECT_NEAR(0.5, c, 5.0e-5);
+ isSame = std::is_same<decltype(c), scalar_t>::value;
+ EXPECT_TRUE(isSame);
+
+ c = 1.0 / scalar_t(2.0);
+ EXPECT_NEAR(0.5, c, 5.0e-5);
+ isSame = std::is_same<decltype(c), scalar_t>::value;
+ EXPECT_TRUE(isSame);
+
+ double d = scalar_t(1.0) / 2.0;
+ EXPECT_NEAR(0.5, d, 5.0e-5);
+
+ auto e = a_m / a_sec;
+ EXPECT_NEAR(0.1, e(), 5.0e-5);
+ isSame = std::is_same<decltype(e), meters_per_second_t>::value;
+ EXPECT_TRUE(isSame);
+
+ auto f = a_m / 8.0;
+ EXPECT_NEAR(0.125, f(), 5.0e-5);
+ isSame = std::is_same<decltype(f), meter_t>::value;
+ EXPECT_TRUE(isSame);
+
+ auto g = 4.0 / b_m;
+ EXPECT_NEAR(2.0, g(), 5.0e-5);
+ isSame = std::is_same<decltype(g), unit_t<inverse<meters>>>::value;
+ EXPECT_TRUE(isSame);
+
+ auto mph = mile_t(60.0) / hour_t(1.0);
+ meters_per_second_t mps = mph;
+ EXPECT_NEAR(26.8224, mps(), 5.0e-5);
+}
+
+TEST_F(UnitContainer, compoundAssignmentAddition) {
+ // units
+ meter_t a(0.0);
+ a += meter_t(1.0);
+
+ EXPECT_EQ(meter_t(1.0), a);
+
+ a += foot_t(meter_t(1));
+
+ EXPECT_EQ(meter_t(2.0), a);
+
+ // scalars
+ scalar_t b(0);
+ b += scalar_t(1.0);
+
+ EXPECT_EQ(scalar_t(1.0), b);
+
+ b += 1;
+
+ EXPECT_EQ(scalar_t(2.0), b);
+}
+
+TEST_F(UnitContainer, compoundAssignmentSubtraction) {
+ // units
+ meter_t a(2.0);
+ a -= meter_t(1.0);
+
+ EXPECT_EQ(meter_t(1.0), a);
+
+ a -= foot_t(meter_t(1));
+
+ EXPECT_EQ(meter_t(0.0), a);
+
+ // scalars
+ scalar_t b(2);
+ b -= scalar_t(1.0);
+
+ EXPECT_EQ(scalar_t(1.0), b);
+
+ b -= 1;
+
+ EXPECT_EQ(scalar_t(0), b);
+}
+
+TEST_F(UnitContainer, compoundAssignmentMultiplication) {
+ // units
+ meter_t a(2.0);
+ a *= scalar_t(2.0);
+
+ EXPECT_EQ(meter_t(4.0), a);
+
+ a *= 2.0;
+
+ EXPECT_EQ(meter_t(8.0), a);
+
+ // scalars
+ scalar_t b(2);
+ b *= scalar_t(2.0);
+
+ EXPECT_EQ(scalar_t(4.0), b);
+
+ b *= 2;
+
+ EXPECT_EQ(scalar_t(8.0), b);
+}
+
+TEST_F(UnitContainer, compoundAssignmentDivision) {
+ // units
+ meter_t a(8.0);
+ a /= scalar_t(2.0);
+
+ EXPECT_EQ(meter_t(4.0), a);
+
+ a /= 2.0;
+
+ EXPECT_EQ(meter_t(2.0), a);
+
+ // scalars
+ scalar_t b(8);
+ b /= scalar_t(2.0);
+
+ EXPECT_EQ(scalar_t(4.0), b);
+
+ b /= 2;
+
+ EXPECT_EQ(scalar_t(2.0), b);
+}
+
+TEST_F(UnitContainer, scalarTypeImplicitConversion) {
+ double test = scalar_t(3.0);
+ EXPECT_DOUBLE_EQ(3.0, test);
+
+ scalar_t testS = 3.0;
+ EXPECT_DOUBLE_EQ(3.0, testS);
+
+ scalar_t test3(ppm_t(10));
+ EXPECT_DOUBLE_EQ(0.00001, test3);
+
+ scalar_t test4;
+ test4 = ppm_t(1);
+ EXPECT_DOUBLE_EQ(0.000001, test4);
+}
+
+TEST_F(UnitContainer, valueMethod) {
+ double test = meter_t(3.0).to<double>();
+ EXPECT_DOUBLE_EQ(3.0, test);
+
+ auto test2 = meter_t(4.0).value();
+ EXPECT_DOUBLE_EQ(4.0, test2);
+ EXPECT_TRUE((std::is_same<decltype(test2), double>::value));
+}
+
+TEST_F(UnitContainer, convertMethod) {
+ double test = meter_t(3.0).convert<feet>().to<double>();
+ EXPECT_NEAR(9.84252, test, 5.0e-6);
+}
+
+#ifndef UNIT_LIB_DISABLE_IOSTREAM
+TEST_F(UnitContainer, cout) {
+ testing::internal::CaptureStdout();
+ std::cout << degree_t(349.87);
+ std::string output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("349.87 deg", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << meter_t(1.0);
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("1 m", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << dB_t(31.0);
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("31 dB", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << volt_t(21.79);
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("21.79 V", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << dBW_t(12.0);
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("12 dBW", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << dBm_t(120.0);
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("120 dBm", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << miles_per_hour_t(72.1);
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("72.1 mph", output.c_str());
+
+ // undefined unit
+ testing::internal::CaptureStdout();
+ std::cout << units::math::cpow<4>(meter_t(2));
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("16 m^4", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << units::math::cpow<3>(foot_t(2));
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("8 cu_ft", output.c_str());
+
+ testing::internal::CaptureStdout();
+ std::cout << std::setprecision(9) << units::math::cpow<4>(foot_t(2));
+ output = testing::internal::GetCapturedStdout();
+ EXPECT_STREQ("0.138095597 m^4", output.c_str());
+
+ // constants
+ testing::internal::CaptureStdout();
+ std::cout << std::setprecision(8) << constants::k_B;
+ output = testing::internal::GetCapturedStdout();
+#if defined(_MSC_VER) && (_MSC_VER <= 1800)
+ EXPECT_STREQ("1.3806485e-023 m^2 kg s^-2 K^-1", output.c_str());
+#else
+ EXPECT_STREQ("1.3806485e-23 m^2 kg s^-2 K^-1", output.c_str());
+#endif
+
+ testing::internal::CaptureStdout();
+ std::cout << std::setprecision(9) << constants::mu_B;
+ output = testing::internal::GetCapturedStdout();
+#if defined(_MSC_VER) && (_MSC_VER <= 1800)
+ EXPECT_STREQ("9.27400999e-024 m^2 A", output.c_str());
+#else
+ EXPECT_STREQ("9.27400999e-24 m^2 A", output.c_str());
+#endif
+
+ testing::internal::CaptureStdout();
+ std::cout << std::setprecision(7) << constants::sigma;
+ output = testing::internal::GetCapturedStdout();
+#if defined(_MSC_VER) && (_MSC_VER <= 1800)
+ EXPECT_STREQ("5.670367e-008 kg s^-3 K^-4", output.c_str());
+#else
+ EXPECT_STREQ("5.670367e-08 kg s^-3 K^-4", output.c_str());
+#endif
+}
+
+TEST_F(UnitContainer, to_string) {
+ foot_t a(3.5);
+ EXPECT_STREQ("3.5 ft", units::length::to_string(a).c_str());
+
+ meter_t b(8);
+ EXPECT_STREQ("8 m", units::length::to_string(b).c_str());
+}
+
+TEST_F(UnitContainer, DISABLED_to_string_locale) {
+ struct lconv* lc;
+
+ // German locale
+#if defined(_MSC_VER)
+ setlocale(LC_ALL, "de-DE");
+#else
+ EXPECT_STREQ("de_DE.utf8", setlocale(LC_ALL, "de_DE.utf8"));
+#endif
+
+ lc = localeconv();
+ char point_de = *lc->decimal_point;
+ EXPECT_EQ(point_de, ',');
+
+ kilometer_t de = 2_km;
+ EXPECT_STREQ("2 km", units::length::to_string(de).c_str());
+
+ de = 2.5_km;
+ EXPECT_STREQ("2,5 km", units::length::to_string(de).c_str());
+
+ // US locale
+#if defined(_MSC_VER)
+ setlocale(LC_ALL, "en-US");
+#else
+ EXPECT_STREQ("en_US.utf8", setlocale(LC_ALL, "en_US.utf8"));
+#endif
+
+ lc = localeconv();
+ char point_us = *lc->decimal_point;
+ EXPECT_EQ(point_us, '.');
+
+ mile_t us = 2_mi;
+ EXPECT_STREQ("2 mi", units::length::to_string(us).c_str());
+
+ us = 2.5_mi;
+ EXPECT_STREQ("2.5 mi", units::length::to_string(us).c_str());
+}
+
+TEST_F(UnitContainer, nameAndAbbreviation) {
+ foot_t a(3.5);
+ EXPECT_STREQ("ft", units::abbreviation(a));
+ EXPECT_STREQ("ft", a.abbreviation());
+ EXPECT_STREQ("foot", a.name());
+
+ meter_t b(8);
+ EXPECT_STREQ("m", units::abbreviation(b));
+ EXPECT_STREQ("m", b.abbreviation());
+ EXPECT_STREQ("meter", b.name());
+}
+#endif
+
+TEST_F(UnitContainer, negative) {
+ meter_t a(5.3);
+ meter_t b(-5.3);
+ EXPECT_NEAR(a.to<double>(), -b.to<double>(), 5.0e-320);
+ EXPECT_NEAR(b.to<double>(), -a.to<double>(), 5.0e-320);
+
+ dB_t c(2.87);
+ dB_t d(-2.87);
+ EXPECT_NEAR(c.to<double>(), -d.to<double>(), 5.0e-320);
+ EXPECT_NEAR(d.to<double>(), -c.to<double>(), 5.0e-320);
+
+ ppm_t e = -1 * ppm_t(10);
+ EXPECT_EQ(e, -ppm_t(10));
+ EXPECT_NEAR(-0.00001, e, 5.0e-10);
+}
+
+TEST_F(UnitContainer, concentration) {
+ ppb_t a(ppm_t(1));
+ EXPECT_EQ(ppb_t(1000), a);
+ EXPECT_EQ(0.000001, a);
+ EXPECT_EQ(0.000001, a.to<double>());
+
+ scalar_t b(ppm_t(1));
+ EXPECT_EQ(0.000001, b);
+
+ scalar_t c = ppb_t(1);
+ EXPECT_EQ(0.000000001, c);
+}
+
+TEST_F(UnitContainer, dBConversion) {
+ dBW_t a_dbw(23.1);
+ watt_t a_w = a_dbw;
+ dBm_t a_dbm = a_dbw;
+
+ EXPECT_NEAR(204.173794, a_w(), 5.0e-7);
+ EXPECT_NEAR(53.1, a_dbm(), 5.0e-7);
+
+ milliwatt_t b_mw(100000.0);
+ watt_t b_w = b_mw;
+ dBm_t b_dbm = b_mw;
+ dBW_t b_dbw = b_mw;
+
+ EXPECT_NEAR(100.0, b_w(), 5.0e-7);
+ EXPECT_NEAR(50.0, b_dbm(), 5.0e-7);
+ EXPECT_NEAR(20.0, b_dbw(), 5.0e-7);
+}
+
+TEST_F(UnitContainer, dBAddition) {
+ bool isSame;
+
+ auto result_dbw = dBW_t(10.0) + dB_t(30.0);
+ EXPECT_NEAR(40.0, result_dbw(), 5.0e-5);
+ result_dbw = dB_t(12.0) + dBW_t(30.0);
+ EXPECT_NEAR(42.0, result_dbw(), 5.0e-5);
+ isSame = std::is_same<decltype(result_dbw), dBW_t>::value;
+ EXPECT_TRUE(isSame);
+
+ auto result_dbm = dB_t(30.0) + dBm_t(20.0);
+ EXPECT_NEAR(50.0, result_dbm(), 5.0e-5);
+
+ // adding dBW to dBW is something you probably shouldn't do, but let's see if
+ // it works...
+ auto result_dBW2 = dBW_t(10.0) + dBm_t(40.0);
+ EXPECT_NEAR(20.0, result_dBW2(), 5.0e-5);
+ isSame = std::is_same<decltype(result_dBW2),
+ unit_t<squared<watts>, double, decibel_scale>>::value;
+ EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitContainer, dBSubtraction) {
+ bool isSame;
+
+ auto result_dbw = dBW_t(10.0) - dB_t(30.0);
+ EXPECT_NEAR(-20.0, result_dbw(), 5.0e-5);
+ isSame = std::is_same<decltype(result_dbw), dBW_t>::value;
+ EXPECT_TRUE(isSame);
+
+ auto result_dbm = dBm_t(100.0) - dB_t(30.0);
+ EXPECT_NEAR(70.0, result_dbm(), 5.0e-5);
+ isSame = std::is_same<decltype(result_dbm), dBm_t>::value;
+ EXPECT_TRUE(isSame);
+
+ auto result_db = dBW_t(100.0) - dBW_t(80.0);
+ EXPECT_NEAR(20.0, result_db(), 5.0e-5);
+ isSame = std::is_same<decltype(result_db), dB_t>::value;
+ EXPECT_TRUE(isSame);
+
+ result_db = dB_t(100.0) - dB_t(80.0);
+ EXPECT_NEAR(20.0, result_db(), 5.0e-5);
+ isSame = std::is_same<decltype(result_db), dB_t>::value;
+ EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitContainer, unit_cast) {
+ meter_t test1(5.7);
+ hectare_t test2(16);
+
+ double dResult1 = 5.7;
+
+ double dResult2 = 16;
+ int iResult2 = 16;
+
+ EXPECT_EQ(dResult1, unit_cast<double>(test1));
+ EXPECT_EQ(dResult2, unit_cast<double>(test2));
+ EXPECT_EQ(iResult2, unit_cast<int>(test2));
+
+ EXPECT_TRUE(
+ (std::is_same<double, decltype(unit_cast<double>(test1))>::value));
+ EXPECT_TRUE((std::is_same<int, decltype(unit_cast<int>(test2))>::value));
+}
+
+// literal syntax is only supported in GCC 4.7+ and MSVC2015+
+#if !defined(_MSC_VER) || _MSC_VER > 1800
+TEST_F(UnitContainer, literals) {
+ // basic functionality testing
+ EXPECT_TRUE((std::is_same<decltype(16.2_m), meter_t>::value));
+ EXPECT_TRUE(meter_t(16.2) == 16.2_m);
+ EXPECT_TRUE(meter_t(16) == 16_m);
+
+ EXPECT_TRUE((std::is_same<decltype(11.2_ft), foot_t>::value));
+ EXPECT_TRUE(foot_t(11.2) == 11.2_ft);
+ EXPECT_TRUE(foot_t(11) == 11_ft);
+
+ // auto using literal syntax
+ auto x = 10.0_m;
+ EXPECT_TRUE((std::is_same<decltype(x), meter_t>::value));
+ EXPECT_TRUE(meter_t(10) == x);
+
+ // conversion using literal syntax
+ foot_t y = 0.3048_m;
+ EXPECT_TRUE(1_ft == y);
+
+ // Pythagorean theorem
+ meter_t a = 3_m;
+ meter_t b = 4_m;
+ meter_t c = sqrt(pow<2>(a) + pow<2>(b));
+ EXPECT_TRUE(c == 5_m);
+}
+#endif
+
+TEST_F(UnitConversion, length) {
+ double test;
+ test = convert<meters, nanometers>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, micrometers>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, millimeters>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, centimeters>(0.01);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, kilometers>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, meters>(1.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, feet>(0.3048);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, miles>(1609.344);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, inches>(0.0254);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, nauticalMiles>(1852.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, astronicalUnits>(149597870700.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, lightyears>(9460730472580800.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<meters, parsec>(3.08567758e16);
+ EXPECT_NEAR(1.0, test, 5.0e7);
+
+ test = convert<feet, feet>(6.3);
+ EXPECT_NEAR(6.3, test, 5.0e-5);
+ test = convert<feet, inches>(6.0);
+ EXPECT_NEAR(72.0, test, 5.0e-5);
+ test = convert<inches, feet>(6.0);
+ EXPECT_NEAR(0.5, test, 5.0e-5);
+ test = convert<meter, feet>(1.0);
+ EXPECT_NEAR(3.28084, test, 5.0e-5);
+ test = convert<miles, nauticalMiles>(6.3);
+ EXPECT_NEAR(5.47455, test, 5.0e-6);
+ test = convert<miles, meters>(11.0);
+ EXPECT_NEAR(17702.8, test, 5.0e-2);
+ test = convert<meters, chains>(1.0);
+ EXPECT_NEAR(0.0497097, test, 5.0e-7);
+}
+
+TEST_F(UnitConversion, mass) {
+ double test;
+
+ test = convert<kilograms, grams>(1.0e-3);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, micrograms>(1.0e-9);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, milligrams>(1.0e-6);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, kilograms>(1.0);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, metric_tons>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, pounds>(0.453592);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, long_tons>(1016.05);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, short_tons>(907.185);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, mass::ounces>(0.0283495);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<kilograms, carats>(0.0002);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<slugs, kilograms>(1.0);
+ EXPECT_NEAR(14.593903, test, 5.0e-7);
+
+ test = convert<pounds, carats>(6.3);
+ EXPECT_NEAR(14288.2, test, 5.0e-2);
+}
+
+TEST_F(UnitConversion, time) {
+ double result = 0;
+ double daysPerYear = 365;
+ double hoursPerDay = 24;
+ double minsPerHour = 60;
+ double secsPerMin = 60;
+ double daysPerWeek = 7;
+
+ result = 2 * daysPerYear * hoursPerDay * minsPerHour * secsPerMin *
+ (1 / minsPerHour) * (1 / secsPerMin) * (1 / hoursPerDay) *
+ (1 / daysPerWeek);
+ EXPECT_NEAR(104.286, result, 5.0e-4);
+
+ year_t twoYears(2.0);
+ week_t twoYearsInWeeks = twoYears;
+ EXPECT_NEAR(week_t(104.286).to<double>(), twoYearsInWeeks.to<double>(),
+ 5.0e-4);
+
+ double test;
+
+ test = convert<seconds, seconds>(1.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, nanoseconds>(1.0e-9);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, microseconds>(1.0e-6);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, milliseconds>(1.0e-3);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, minutes>(60.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, hours>(3600.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, days>(86400.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, weeks>(604800.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<seconds, years>(3.154e7);
+ EXPECT_NEAR(1.0, test, 5.0e3);
+
+ test = convert<years, weeks>(2.0);
+ EXPECT_NEAR(104.2857142857143, test, 5.0e-14);
+ test = convert<hours, minutes>(4.0);
+ EXPECT_NEAR(240.0, test, 5.0e-14);
+ test = convert<julian_years, days>(1.0);
+ EXPECT_NEAR(365.25, test, 5.0e-14);
+ test = convert<gregorian_years, days>(1.0);
+ EXPECT_NEAR(365.2425, test, 5.0e-14);
+}
+
+TEST_F(UnitConversion, angle) {
+ angle::degree_t quarterCircleDeg(90.0);
+ angle::radian_t quarterCircleRad = quarterCircleDeg;
+ EXPECT_NEAR(angle::radian_t(constants::detail::PI_VAL / 2.0).to<double>(),
+ quarterCircleRad.to<double>(), 5.0e-12);
+
+ double test;
+
+ test = convert<angle::radians, angle::radians>(1.0);
+ EXPECT_NEAR(1.0, test, 5.0e-20);
+ test = convert<angle::radians, angle::milliradians>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-4);
+ test = convert<angle::radians, angle::degrees>(0.0174533);
+ EXPECT_NEAR(1.0, test, 5.0e-7);
+ test = convert<angle::radians, angle::arcminutes>(0.000290888);
+ EXPECT_NEAR(0.99999928265913, test, 5.0e-8);
+ test = convert<angle::radians, angle::arcseconds>(4.8481e-6);
+ EXPECT_NEAR(0.999992407, test, 5.0e-10);
+ test = convert<angle::radians, angle::turns>(6.28319);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+ test = convert<angle::radians, angle::gradians>(0.015708);
+ EXPECT_NEAR(1.0, test, 5.0e-6);
+
+ test = convert<angle::radians, angle::radians>(2.1);
+ EXPECT_NEAR(2.1, test, 5.0e-6);
+ test = convert<angle::arcseconds, angle::gradians>(2.1);
+ EXPECT_NEAR(0.000648148, test, 5.0e-6);
+ test = convert<angle::radians, angle::degrees>(constants::detail::PI_VAL);
+ EXPECT_NEAR(180.0, test, 5.0e-6);
+ test = convert<angle::degrees, angle::radians>(90.0);
+ EXPECT_NEAR(constants::detail::PI_VAL / 2, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, current) {
+ double test;
+
+ test = convert<current::A, current::mA>(2.1);
+ EXPECT_NEAR(2100.0, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, temperature) {
+ // temp conversion are weird/hard since they involve translations AND scaling.
+ double test;
+
+ test = convert<kelvin, kelvin>(72.0);
+ EXPECT_NEAR(72.0, test, 5.0e-5);
+ test = convert<fahrenheit, fahrenheit>(72.0);
+ EXPECT_NEAR(72.0, test, 5.0e-5);
+ test = convert<kelvin, fahrenheit>(300.0);
+ EXPECT_NEAR(80.33, test, 5.0e-5);
+ test = convert<fahrenheit, kelvin>(451.0);
+ EXPECT_NEAR(505.928, test, 5.0e-4);
+ test = convert<kelvin, celsius>(300.0);
+ EXPECT_NEAR(26.85, test, 5.0e-3);
+ test = convert<celsius, kelvin>(451.0);
+ EXPECT_NEAR(724.15, test, 5.0e-3);
+ test = convert<fahrenheit, celsius>(72.0);
+ EXPECT_NEAR(22.2222, test, 5.0e-5);
+ test = convert<celsius, fahrenheit>(100.0);
+ EXPECT_NEAR(212.0, test, 5.0e-5);
+ test = convert<fahrenheit, celsius>(32.0);
+ EXPECT_NEAR(0.0, test, 5.0e-5);
+ test = convert<celsius, fahrenheit>(0.0);
+ EXPECT_NEAR(32.0, test, 5.0e-5);
+ test = convert<rankine, kelvin>(100.0);
+ EXPECT_NEAR(55.5556, test, 5.0e-5);
+ test = convert<kelvin, rankine>(100.0);
+ EXPECT_NEAR(180.0, test, 5.0e-5);
+ test = convert<fahrenheit, rankine>(100.0);
+ EXPECT_NEAR(559.67, test, 5.0e-5);
+ test = convert<rankine, fahrenheit>(72.0);
+ EXPECT_NEAR(-387.67, test, 5.0e-5);
+ test = convert<reaumur, kelvin>(100.0);
+ EXPECT_NEAR(398.0, test, 5.0e-1);
+ test = convert<reaumur, celsius>(80.0);
+ EXPECT_NEAR(100.0, test, 5.0e-5);
+ test = convert<celsius, reaumur>(212.0);
+ EXPECT_NEAR(169.6, test, 5.0e-2);
+ test = convert<reaumur, fahrenheit>(80.0);
+ EXPECT_NEAR(212.0, test, 5.0e-5);
+ test = convert<fahrenheit, reaumur>(37.0);
+ EXPECT_NEAR(2.222, test, 5.0e-3);
+}
+
+TEST_F(UnitConversion, luminous_intensity) {
+ double test;
+
+ test = convert<candela, millicandela>(72.0);
+ EXPECT_NEAR(72000.0, test, 5.0e-5);
+ test = convert<millicandela, candela>(376.0);
+ EXPECT_NEAR(0.376, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, solid_angle) {
+ double test;
+ bool same;
+
+ same = std::is_same<traits::base_unit_of<steradians>,
+ traits::base_unit_of<degrees_squared>>::value;
+ EXPECT_TRUE(same);
+
+ test = convert<steradians, steradians>(72.0);
+ EXPECT_NEAR(72.0, test, 5.0e-5);
+ test = convert<steradians, degrees_squared>(1.0);
+ EXPECT_NEAR(3282.8, test, 5.0e-2);
+ test = convert<steradians, spats>(8.0);
+ EXPECT_NEAR(0.636619772367582, test, 5.0e-14);
+ test = convert<degrees_squared, steradians>(3282.8);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<degrees_squared, degrees_squared>(72.0);
+ EXPECT_NEAR(72.0, test, 5.0e-5);
+ test = convert<degrees_squared, spats>(3282.8);
+ EXPECT_NEAR(1.0 / (4 * constants::detail::PI_VAL), test, 5.0e-5);
+ test = convert<spats, steradians>(1.0 / (4 * constants::detail::PI_VAL));
+ EXPECT_NEAR(1.0, test, 5.0e-14);
+ test = convert<spats, degrees_squared>(1.0 / (4 * constants::detail::PI_VAL));
+ EXPECT_NEAR(3282.8, test, 5.0e-2);
+ test = convert<spats, spats>(72.0);
+ EXPECT_NEAR(72.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, frequency) {
+ double test;
+
+ test = convert<hertz, kilohertz>(63000.0);
+ EXPECT_NEAR(63.0, test, 5.0e-5);
+ test = convert<hertz, hertz>(6.3);
+ EXPECT_NEAR(6.3, test, 5.0e-5);
+ test = convert<kilohertz, hertz>(5.0);
+ EXPECT_NEAR(5000.0, test, 5.0e-5);
+ test = convert<megahertz, hertz>(1.0);
+ EXPECT_NEAR(1.0e6, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, velocity) {
+ double test;
+ bool same;
+
+ same = std::is_same<meters_per_second,
+ unit<std::ratio<1>, category::velocity_unit>>::value;
+ EXPECT_TRUE(same);
+ same = traits::is_convertible_unit<miles_per_hour, meters_per_second>::value;
+ EXPECT_TRUE(same);
+
+ test = convert<meters_per_second, miles_per_hour>(1250.0);
+ EXPECT_NEAR(2796.17, test, 5.0e-3);
+ test = convert<feet_per_second, kilometers_per_hour>(2796.17);
+ EXPECT_NEAR(3068.181418, test, 5.0e-7);
+ test = convert<knots, miles_per_hour>(600.0);
+ EXPECT_NEAR(690.468, test, 5.0e-4);
+ test = convert<miles_per_hour, feet_per_second>(120.0);
+ EXPECT_NEAR(176.0, test, 5.0e-5);
+ test = convert<feet_per_second, meters_per_second>(10.0);
+ EXPECT_NEAR(3.048, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, angular_velocity) {
+ double test;
+ bool same;
+
+ same =
+ std::is_same<radians_per_second,
+ unit<std::ratio<1>, category::angular_velocity_unit>>::value;
+ EXPECT_TRUE(same);
+ same = traits::is_convertible_unit<rpm, radians_per_second>::value;
+ EXPECT_TRUE(same);
+
+ test = convert<radians_per_second, milliarcseconds_per_year>(1.0);
+ EXPECT_NEAR(6.504e15, test, 1.0e12);
+ test = convert<degrees_per_second, radians_per_second>(1.0);
+ EXPECT_NEAR(0.0174533, test, 5.0e-8);
+ test = convert<rpm, radians_per_second>(1.0);
+ EXPECT_NEAR(0.10471975512, test, 5.0e-13);
+ test = convert<milliarcseconds_per_year, radians_per_second>(1.0);
+ EXPECT_NEAR(1.537e-16, test, 5.0e-20);
+}
+
+TEST_F(UnitConversion, acceleration) {
+ double test;
+
+ test = convert<standard_gravity, meters_per_second_squared>(1.0);
+ EXPECT_NEAR(9.80665, test, 5.0e-10);
+}
+TEST_F(UnitConversion, force) {
+ double test;
+
+ test = convert<units::force::newton, units::force::newton>(1.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<units::force::newton, units::force::pounds>(6.3);
+ EXPECT_NEAR(1.4163, test, 5.0e-5);
+ test = convert<units::force::newton, units::force::dynes>(5.0);
+ EXPECT_NEAR(500000.0, test, 5.0e-5);
+ test = convert<units::force::newtons, units::force::poundals>(2.1);
+ EXPECT_NEAR(15.1893, test, 5.0e-5);
+ test = convert<units::force::newtons, units::force::kiloponds>(173.0);
+ EXPECT_NEAR(17.6411, test, 5.0e-5);
+ test = convert<units::force::poundals, units::force::kiloponds>(21.879);
+ EXPECT_NEAR(0.308451933, test, 5.0e-10);
+}
+
+TEST_F(UnitConversion, area) {
+ double test;
+
+ test = convert<hectares, acres>(6.3);
+ EXPECT_NEAR(15.5676, test, 5.0e-5);
+ test = convert<square_miles, square_kilometers>(10.0);
+ EXPECT_NEAR(25.8999, test, 5.0e-5);
+ test = convert<square_inch, square_meter>(4.0);
+ EXPECT_NEAR(0.00258064, test, 5.0e-9);
+ test = convert<acre, square_foot>(5.0);
+ EXPECT_NEAR(217800.0, test, 5.0e-5);
+ test = convert<square_meter, square_foot>(1.0);
+ EXPECT_NEAR(10.7639, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, pressure) {
+ double test;
+
+ test = convert<pascals, torr>(1.0);
+ EXPECT_NEAR(0.00750062, test, 5.0e-5);
+ test = convert<bar, psi>(2.2);
+ EXPECT_NEAR(31.9083, test, 5.0e-5);
+ test = convert<atmospheres, bar>(4.0);
+ EXPECT_NEAR(4.053, test, 5.0e-5);
+ test = convert<torr, pascals>(800.0);
+ EXPECT_NEAR(106657.89474, test, 5.0e-5);
+ test = convert<psi, atmospheres>(38.0);
+ EXPECT_NEAR(2.58575, test, 5.0e-5);
+ test = convert<psi, pascals>(1.0);
+ EXPECT_NEAR(6894.76, test, 5.0e-3);
+ test = convert<pascals, bar>(0.25);
+ EXPECT_NEAR(2.5e-6, test, 5.0e-5);
+ test = convert<torr, atmospheres>(9.0);
+ EXPECT_NEAR(0.0118421, test, 5.0e-8);
+ test = convert<bar, torr>(12.0);
+ EXPECT_NEAR(9000.74, test, 5.0e-3);
+ test = convert<atmospheres, psi>(1.0);
+ EXPECT_NEAR(14.6959, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, charge) {
+ double test;
+
+ test = convert<coulombs, ampere_hours>(4.0);
+ EXPECT_NEAR(0.00111111, test, 5.0e-9);
+ test = convert<ampere_hours, coulombs>(1.0);
+ EXPECT_NEAR(3600.0, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, energy) {
+ double test;
+
+ test = convert<joules, calories>(8000.000464);
+ EXPECT_NEAR(1912.046, test, 5.0e-4);
+ test = convert<therms, joules>(12.0);
+ EXPECT_NEAR(1.266e+9, test, 5.0e5);
+ test = convert<megajoules, watt_hours>(100.0);
+ EXPECT_NEAR(27777.778, test, 5.0e-4);
+ test = convert<kilocalories, megajoules>(56.0);
+ EXPECT_NEAR(0.234304, test, 5.0e-7);
+ test = convert<kilojoules, therms>(56.0);
+ EXPECT_NEAR(0.000530904, test, 5.0e-5);
+ test = convert<british_thermal_units, kilojoules>(18.56399995447);
+ EXPECT_NEAR(19.5860568, test, 5.0e-5);
+ test = convert<calories, energy::foot_pounds>(18.56399995447);
+ EXPECT_NEAR(57.28776190423856, test, 5.0e-5);
+ test = convert<megajoules, calories>(1.0);
+ EXPECT_NEAR(239006.0, test, 5.0e-1);
+ test = convert<kilocalories, kilowatt_hours>(2.0);
+ EXPECT_NEAR(0.00232444, test, 5.0e-9);
+ test = convert<therms, kilocalories>(0.1);
+ EXPECT_NEAR(2521.04, test, 5.0e-3);
+ test = convert<watt_hours, megajoules>(67.0);
+ EXPECT_NEAR(0.2412, test, 5.0e-5);
+ test = convert<british_thermal_units, watt_hours>(100.0);
+ EXPECT_NEAR(29.3071, test, 5.0e-5);
+ test = convert<calories, BTU>(100.0);
+ EXPECT_NEAR(0.396567, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, power) {
+ double test;
+
+ test = convert<compound_unit<energy::foot_pounds, inverse<seconds>>, watts>(
+ 550.0);
+ EXPECT_NEAR(745.7, test, 5.0e-2);
+ test = convert<watts, gigawatts>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-4);
+ test = convert<microwatts, watts>(200000.0);
+ EXPECT_NEAR(0.2, test, 5.0e-4);
+ test = convert<horsepower, watts>(100.0);
+ EXPECT_NEAR(74570.0, test, 5.0e-1);
+ test = convert<horsepower, megawatts>(5.0);
+ EXPECT_NEAR(0.0037284994, test, 5.0e-7);
+ test = convert<kilowatts, horsepower>(232.0);
+ EXPECT_NEAR(311.117, test, 5.0e-4);
+ test = convert<milliwatts, horsepower>(1001.0);
+ EXPECT_NEAR(0.001342363, test, 5.0e-9);
+}
+
+TEST_F(UnitConversion, voltage) {
+ double test;
+
+ test = convert<volts, millivolts>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picovolts, volts>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanovolts, volts>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microvolts, volts>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millivolts, volts>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilovolts, volts>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megavolts, volts>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigavolts, volts>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<statvolts, volts>(299.792458);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millivolts, statvolts>(1000.0);
+ EXPECT_NEAR(299.792458, test, 5.0e-5);
+ test = convert<abvolts, nanovolts>(0.1);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microvolts, abvolts>(0.01);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, capacitance) {
+ double test;
+
+ test = convert<farads, millifarads>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picofarads, farads>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanofarads, farads>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microfarads, farads>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millifarads, farads>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilofarads, farads>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megafarads, farads>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigafarads, farads>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, impedance) {
+ double test;
+
+ test = convert<ohms, milliohms>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picoohms, ohms>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanoohms, ohms>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microohms, ohms>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<milliohms, ohms>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kiloohms, ohms>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megaohms, ohms>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigaohms, ohms>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, conductance) {
+ double test;
+
+ test = convert<siemens, millisiemens>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picosiemens, siemens>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanosiemens, siemens>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microsiemens, siemens>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millisiemens, siemens>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilosiemens, siemens>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megasiemens, siemens>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigasiemens, siemens>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, magnetic_flux) {
+ double test;
+
+ test = convert<webers, milliwebers>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picowebers, webers>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanowebers, webers>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microwebers, webers>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<milliwebers, webers>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilowebers, webers>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megawebers, webers>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigawebers, webers>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<maxwells, webers>(100000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanowebers, maxwells>(10.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, magnetic_field_strength) {
+ double test;
+
+ test = convert<teslas, milliteslas>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picoteslas, teslas>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanoteslas, teslas>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microteslas, teslas>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<milliteslas, teslas>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kiloteslas, teslas>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megateslas, teslas>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigateslas, teslas>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gauss, teslas>(10000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanoteslas, gauss>(100000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, inductance) {
+ double test;
+
+ test = convert<henries, millihenries>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picohenries, henries>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanohenries, henries>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microhenries, henries>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millihenries, henries>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilohenries, henries>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megahenries, henries>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigahenries, henries>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, luminous_flux) {
+ double test;
+
+ test = convert<lumens, millilumens>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picolumens, lumens>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanolumens, lumens>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microlumens, lumens>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millilumens, lumens>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilolumens, lumens>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megalumens, lumens>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigalumens, lumens>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, illuminance) {
+ double test;
+
+ test = convert<luxes, milliluxes>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picoluxes, luxes>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanoluxes, luxes>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microluxes, luxes>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<milliluxes, luxes>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kiloluxes, luxes>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megaluxes, luxes>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigaluxes, luxes>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+
+ test = convert<footcandles, luxes>(0.092903);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<lux, lumens_per_square_inch>(1550.0031000062);
+ EXPECT_NEAR(1.0, test, 5.0e-13);
+ test = convert<phots, luxes>(0.0001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, radiation) {
+ double test;
+
+ test = convert<becquerels, millibecquerels>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picobecquerels, becquerels>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanobecquerels, becquerels>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microbecquerels, becquerels>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millibecquerels, becquerels>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilobecquerels, becquerels>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megabecquerels, becquerels>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigabecquerels, becquerels>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+
+ test = convert<grays, milligrays>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picograys, grays>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanograys, grays>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<micrograys, grays>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<milligrays, grays>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilograys, grays>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megagrays, grays>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigagrays, grays>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+
+ test = convert<sieverts, millisieverts>(10.0);
+ EXPECT_NEAR(10000.0, test, 5.0e-5);
+ test = convert<picosieverts, sieverts>(1000000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<nanosieverts, sieverts>(1000000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<microsieverts, sieverts>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<millisieverts, sieverts>(1000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<kilosieverts, sieverts>(0.001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<megasieverts, sieverts>(0.000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<gigasieverts, sieverts>(0.000000001);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+
+ test = convert<becquerels, curies>(37.0e9);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<becquerels, rutherfords>(1000000.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<rads, grays>(100.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, torque) {
+ double test;
+
+ test = convert<torque::foot_pounds, newton_meter>(1.0);
+ EXPECT_NEAR(1.355817948, test, 5.0e-5);
+ test = convert<inch_pounds, newton_meter>(1.0);
+ EXPECT_NEAR(0.112984829, test, 5.0e-5);
+ test = convert<foot_poundals, newton_meter>(1.0);
+ EXPECT_NEAR(4.214011009e-2, test, 5.0e-5);
+ test = convert<meter_kilograms, newton_meter>(1.0);
+ EXPECT_NEAR(9.80665, test, 5.0e-5);
+ test = convert<inch_pound, meter_kilogram>(86.79616930855788);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<foot_poundals, inch_pound>(2.681170713);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, volume) {
+ double test;
+
+ test = convert<cubic_meters, cubic_meter>(1.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<cubic_millimeters, cubic_meter>(1.0);
+ EXPECT_NEAR(1.0e-9, test, 5.0e-5);
+ test = convert<cubic_kilometers, cubic_meter>(1.0);
+ EXPECT_NEAR(1.0e9, test, 5.0e-5);
+ test = convert<liters, cubic_meter>(1.0);
+ EXPECT_NEAR(0.001, test, 5.0e-5);
+ test = convert<milliliters, cubic_meter>(1.0);
+ EXPECT_NEAR(1.0e-6, test, 5.0e-5);
+ test = convert<cubic_inches, cubic_meter>(1.0);
+ EXPECT_NEAR(1.6387e-5, test, 5.0e-10);
+ test = convert<cubic_feet, cubic_meter>(1.0);
+ EXPECT_NEAR(0.0283168, test, 5.0e-8);
+ test = convert<cubic_yards, cubic_meter>(1.0);
+ EXPECT_NEAR(0.764555, test, 5.0e-7);
+ test = convert<cubic_miles, cubic_meter>(1.0);
+ EXPECT_NEAR(4.168e+9, test, 5.0e5);
+ test = convert<gallons, cubic_meter>(1.0);
+ EXPECT_NEAR(0.00378541, test, 5.0e-8);
+ test = convert<quarts, cubic_meter>(1.0);
+ EXPECT_NEAR(0.000946353, test, 5.0e-10);
+ test = convert<pints, cubic_meter>(1.0);
+ EXPECT_NEAR(0.000473176, test, 5.0e-10);
+ test = convert<cups, cubic_meter>(1.0);
+ EXPECT_NEAR(0.00024, test, 5.0e-6);
+ test = convert<volume::fluid_ounces, cubic_meter>(1.0);
+ EXPECT_NEAR(2.9574e-5, test, 5.0e-5);
+ test = convert<barrels, cubic_meter>(1.0);
+ EXPECT_NEAR(0.158987294928, test, 5.0e-13);
+ test = convert<bushels, cubic_meter>(1.0);
+ EXPECT_NEAR(0.0352391, test, 5.0e-8);
+ test = convert<cords, cubic_meter>(1.0);
+ EXPECT_NEAR(3.62456, test, 5.0e-6);
+ test = convert<cubic_fathoms, cubic_meter>(1.0);
+ EXPECT_NEAR(6.11644, test, 5.0e-6);
+ test = convert<tablespoons, cubic_meter>(1.0);
+ EXPECT_NEAR(1.4787e-5, test, 5.0e-10);
+ test = convert<teaspoons, cubic_meter>(1.0);
+ EXPECT_NEAR(4.9289e-6, test, 5.0e-11);
+ test = convert<pinches, cubic_meter>(1.0);
+ EXPECT_NEAR(616.11519921875e-9, test, 5.0e-20);
+ test = convert<dashes, cubic_meter>(1.0);
+ EXPECT_NEAR(308.057599609375e-9, test, 5.0e-20);
+ test = convert<drops, cubic_meter>(1.0);
+ EXPECT_NEAR(82.14869322916e-9, test, 5.0e-9);
+ test = convert<fifths, cubic_meter>(1.0);
+ EXPECT_NEAR(0.00075708236, test, 5.0e-12);
+ test = convert<drams, cubic_meter>(1.0);
+ EXPECT_NEAR(3.69669e-6, test, 5.0e-12);
+ test = convert<gills, cubic_meter>(1.0);
+ EXPECT_NEAR(0.000118294, test, 5.0e-10);
+ test = convert<pecks, cubic_meter>(1.0);
+ EXPECT_NEAR(0.00880977, test, 5.0e-9);
+ test = convert<sacks, cubic_meter>(9.4591978);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<shots, cubic_meter>(1.0);
+ EXPECT_NEAR(4.43603e-5, test, 5.0e-11);
+ test = convert<strikes, cubic_meter>(1.0);
+ EXPECT_NEAR(0.07047814033376, test, 5.0e-5);
+ test = convert<volume::fluid_ounces, milliliters>(1.0);
+ EXPECT_NEAR(29.5735, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, density) {
+ double test;
+
+ test = convert<kilograms_per_cubic_meter, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(1.0, test, 5.0e-5);
+ test = convert<grams_per_milliliter, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(1000.0, test, 5.0e-5);
+ test = convert<kilograms_per_liter, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(1000.0, test, 5.0e-5);
+ test = convert<ounces_per_cubic_foot, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(1.001153961, test, 5.0e-10);
+ test = convert<ounces_per_cubic_inch, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(1.729994044e3, test, 5.0e-7);
+ test = convert<ounces_per_gallon, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(7.489151707, test, 5.0e-10);
+ test = convert<pounds_per_cubic_foot, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(16.01846337, test, 5.0e-9);
+ test = convert<pounds_per_cubic_inch, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(2.767990471e4, test, 5.0e-6);
+ test = convert<pounds_per_gallon, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(119.8264273, test, 5.0e-8);
+ test = convert<slugs_per_cubic_foot, kilograms_per_cubic_meter>(1.0);
+ EXPECT_NEAR(515.3788184, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, concentration) {
+ double test;
+
+ test = ppm_t(1.0);
+ EXPECT_NEAR(1.0e-6, test, 5.0e-12);
+ test = ppb_t(1.0);
+ EXPECT_NEAR(1.0e-9, test, 5.0e-12);
+ test = ppt_t(1.0);
+ EXPECT_NEAR(1.0e-12, test, 5.0e-12);
+ test = percent_t(18.0);
+ EXPECT_NEAR(0.18, test, 5.0e-12);
+}
+
+TEST_F(UnitConversion, data) {
+ EXPECT_EQ(8, (convert<byte, bit>(1)));
+
+ EXPECT_EQ(1000, (convert<kilobytes, bytes>(1)));
+ EXPECT_EQ(1000, (convert<megabytes, kilobytes>(1)));
+ EXPECT_EQ(1000, (convert<gigabytes, megabytes>(1)));
+ EXPECT_EQ(1000, (convert<terabytes, gigabytes>(1)));
+ EXPECT_EQ(1000, (convert<petabytes, terabytes>(1)));
+ EXPECT_EQ(1000, (convert<exabytes, petabytes>(1)));
+
+ EXPECT_EQ(1024, (convert<kibibytes, bytes>(1)));
+ EXPECT_EQ(1024, (convert<mebibytes, kibibytes>(1)));
+ EXPECT_EQ(1024, (convert<gibibytes, mebibytes>(1)));
+ EXPECT_EQ(1024, (convert<tebibytes, gibibytes>(1)));
+ EXPECT_EQ(1024, (convert<pebibytes, tebibytes>(1)));
+ EXPECT_EQ(1024, (convert<exbibytes, pebibytes>(1)));
+
+ EXPECT_EQ(93750000, (convert<gigabytes, kibibits>(12)));
+
+ EXPECT_EQ(1000, (convert<kilobits, bits>(1)));
+ EXPECT_EQ(1000, (convert<megabits, kilobits>(1)));
+ EXPECT_EQ(1000, (convert<gigabits, megabits>(1)));
+ EXPECT_EQ(1000, (convert<terabits, gigabits>(1)));
+ EXPECT_EQ(1000, (convert<petabits, terabits>(1)));
+ EXPECT_EQ(1000, (convert<exabits, petabits>(1)));
+
+ EXPECT_EQ(1024, (convert<kibibits, bits>(1)));
+ EXPECT_EQ(1024, (convert<mebibits, kibibits>(1)));
+ EXPECT_EQ(1024, (convert<gibibits, mebibits>(1)));
+ EXPECT_EQ(1024, (convert<tebibits, gibibits>(1)));
+ EXPECT_EQ(1024, (convert<pebibits, tebibits>(1)));
+ EXPECT_EQ(1024, (convert<exbibits, pebibits>(1)));
+
+ // Source: https://en.wikipedia.org/wiki/Binary_prefix
+ EXPECT_NEAR(percent_t(2.4), kibibyte_t(1) / kilobyte_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(4.9), mebibyte_t(1) / megabyte_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(7.4), gibibyte_t(1) / gigabyte_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(10.0), tebibyte_t(1) / terabyte_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(12.6), pebibyte_t(1) / petabyte_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(15.3), exbibyte_t(1) / exabyte_t(1) - 1, 0.005);
+}
+
+TEST_F(UnitConversion, data_transfer_rate) {
+ EXPECT_EQ(8, (convert<bytes_per_second, bits_per_second>(1)));
+
+ EXPECT_EQ(1000, (convert<kilobytes_per_second, bytes_per_second>(1)));
+ EXPECT_EQ(1000, (convert<megabytes_per_second, kilobytes_per_second>(1)));
+ EXPECT_EQ(1000, (convert<gigabytes_per_second, megabytes_per_second>(1)));
+ EXPECT_EQ(1000, (convert<terabytes_per_second, gigabytes_per_second>(1)));
+ EXPECT_EQ(1000, (convert<petabytes_per_second, terabytes_per_second>(1)));
+ EXPECT_EQ(1000, (convert<exabytes_per_second, petabytes_per_second>(1)));
+
+ EXPECT_EQ(1024, (convert<kibibytes_per_second, bytes_per_second>(1)));
+ EXPECT_EQ(1024, (convert<mebibytes_per_second, kibibytes_per_second>(1)));
+ EXPECT_EQ(1024, (convert<gibibytes_per_second, mebibytes_per_second>(1)));
+ EXPECT_EQ(1024, (convert<tebibytes_per_second, gibibytes_per_second>(1)));
+ EXPECT_EQ(1024, (convert<pebibytes_per_second, tebibytes_per_second>(1)));
+ EXPECT_EQ(1024, (convert<exbibytes_per_second, pebibytes_per_second>(1)));
+
+ EXPECT_EQ(93750000, (convert<gigabytes_per_second, kibibits_per_second>(12)));
+
+ EXPECT_EQ(1000, (convert<kilobits_per_second, bits_per_second>(1)));
+ EXPECT_EQ(1000, (convert<megabits_per_second, kilobits_per_second>(1)));
+ EXPECT_EQ(1000, (convert<gigabits_per_second, megabits_per_second>(1)));
+ EXPECT_EQ(1000, (convert<terabits_per_second, gigabits_per_second>(1)));
+ EXPECT_EQ(1000, (convert<petabits_per_second, terabits_per_second>(1)));
+ EXPECT_EQ(1000, (convert<exabits_per_second, petabits_per_second>(1)));
+
+ EXPECT_EQ(1024, (convert<kibibits_per_second, bits_per_second>(1)));
+ EXPECT_EQ(1024, (convert<mebibits_per_second, kibibits_per_second>(1)));
+ EXPECT_EQ(1024, (convert<gibibits_per_second, mebibits_per_second>(1)));
+ EXPECT_EQ(1024, (convert<tebibits_per_second, gibibits_per_second>(1)));
+ EXPECT_EQ(1024, (convert<pebibits_per_second, tebibits_per_second>(1)));
+ EXPECT_EQ(1024, (convert<exbibits_per_second, pebibits_per_second>(1)));
+
+ // Source: https://en.wikipedia.org/wiki/Binary_prefix
+ EXPECT_NEAR(percent_t(2.4),
+ kibibytes_per_second_t(1) / kilobytes_per_second_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(4.9),
+ mebibytes_per_second_t(1) / megabytes_per_second_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(7.4),
+ gibibytes_per_second_t(1) / gigabytes_per_second_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(10.0),
+ tebibytes_per_second_t(1) / terabytes_per_second_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(12.6),
+ pebibytes_per_second_t(1) / petabytes_per_second_t(1) - 1, 0.005);
+ EXPECT_NEAR(percent_t(15.3),
+ exbibytes_per_second_t(1) / exabytes_per_second_t(1) - 1, 0.005);
+}
+
+TEST_F(UnitConversion, pi) {
+ EXPECT_TRUE(
+ units::traits::is_dimensionless_unit<decltype(constants::pi)>::value);
+ EXPECT_TRUE(units::traits::is_dimensionless_unit<constants::PI>::value);
+
+ // implicit conversion/arithmetic
+ EXPECT_NEAR(3.14159, constants::pi, 5.0e-6);
+ EXPECT_NEAR(6.28318531, (2 * constants::pi), 5.0e-9);
+ EXPECT_NEAR(6.28318531, (constants::pi + constants::pi), 5.0e-9);
+ EXPECT_NEAR(0.0, (constants::pi - constants::pi), 5.0e-9);
+ EXPECT_NEAR(31.00627668, units::math::cpow<3>(constants::pi), 5.0e-10);
+ EXPECT_NEAR(0.0322515344, (1.0 / units::math::cpow<3>(constants::pi)),
+ 5.0e-11);
+ EXPECT_TRUE(constants::detail::PI_VAL == constants::pi);
+ EXPECT_TRUE(1.0 != constants::pi);
+ EXPECT_TRUE(4.0 > constants::pi);
+ EXPECT_TRUE(3.0 < constants::pi);
+ EXPECT_TRUE(constants::pi > 3.0);
+ EXPECT_TRUE(constants::pi < 4.0);
+
+ // explicit conversion
+ EXPECT_NEAR(3.14159, constants::pi.to<double>(), 5.0e-6);
+
+ // auto multiplication
+ EXPECT_TRUE(
+ (std::is_same<meter_t, decltype(constants::pi * meter_t(1))>::value));
+ EXPECT_TRUE(
+ (std::is_same<meter_t, decltype(meter_t(1) * constants::pi)>::value));
+
+ EXPECT_NEAR(constants::detail::PI_VAL,
+ (constants::pi * meter_t(1)).to<double>(), 5.0e-10);
+ EXPECT_NEAR(constants::detail::PI_VAL,
+ (meter_t(1) * constants::pi).to<double>(), 5.0e-10);
+
+ // explicit multiplication
+ meter_t a = constants::pi * meter_t(1);
+ meter_t b = meter_t(1) * constants::pi;
+
+ EXPECT_NEAR(constants::detail::PI_VAL, a.to<double>(), 5.0e-10);
+ EXPECT_NEAR(constants::detail::PI_VAL, b.to<double>(), 5.0e-10);
+
+ // auto division
+ EXPECT_TRUE(
+ (std::is_same<hertz_t, decltype(constants::pi / second_t(1))>::value));
+ EXPECT_TRUE(
+ (std::is_same<second_t, decltype(second_t(1) / constants::pi)>::value));
+
+ EXPECT_NEAR(constants::detail::PI_VAL,
+ (constants::pi / second_t(1)).to<double>(), 5.0e-10);
+ EXPECT_NEAR(1.0 / constants::detail::PI_VAL,
+ (second_t(1) / constants::pi).to<double>(), 5.0e-10);
+
+ // explicit
+ hertz_t c = constants::pi / second_t(1);
+ second_t d = second_t(1) / constants::pi;
+
+ EXPECT_NEAR(constants::detail::PI_VAL, c.to<double>(), 5.0e-10);
+ EXPECT_NEAR(1.0 / constants::detail::PI_VAL, d.to<double>(), 5.0e-10);
+}
+
+TEST_F(UnitConversion, constants) {
+ // Source: NIST "2014 CODATA recommended values"
+ EXPECT_NEAR(299792458, constants::c(), 5.0e-9);
+ EXPECT_NEAR(6.67408e-11, constants::G(), 5.0e-17);
+ EXPECT_NEAR(6.626070040e-34, constants::h(), 5.0e-44);
+ EXPECT_NEAR(1.2566370614e-6, constants::mu0(), 5.0e-17);
+ EXPECT_NEAR(8.854187817e-12, constants::epsilon0(), 5.0e-21);
+ EXPECT_NEAR(376.73031346177, constants::Z0(), 5.0e-12);
+ EXPECT_NEAR(8987551787.3681764, constants::k_e(), 5.0e-6);
+ EXPECT_NEAR(1.6021766208e-19, constants::e(), 5.0e-29);
+ EXPECT_NEAR(9.10938356e-31, constants::m_e(), 5.0e-40);
+ EXPECT_NEAR(1.672621898e-27, constants::m_p(), 5.0e-37);
+ EXPECT_NEAR(9.274009994e-24, constants::mu_B(), 5.0e-32);
+ EXPECT_NEAR(6.022140857e23, constants::N_A(), 5.0e14);
+ EXPECT_NEAR(8.3144598, constants::R(), 5.0e-8);
+ EXPECT_NEAR(1.38064852e-23, constants::k_B(), 5.0e-31);
+ EXPECT_NEAR(96485.33289, constants::F(), 5.0e-5);
+ EXPECT_NEAR(5.670367e-8, constants::sigma(), 5.0e-14);
+}
+
+TEST_F(UnitConversion, std_chrono) {
+ nanosecond_t a = std::chrono::nanoseconds(10);
+ EXPECT_EQ(nanosecond_t(10), a);
+ microsecond_t b = std::chrono::microseconds(10);
+ EXPECT_EQ(microsecond_t(10), b);
+ millisecond_t c = std::chrono::milliseconds(10);
+ EXPECT_EQ(millisecond_t(10), c);
+ second_t d = std::chrono::seconds(1);
+ EXPECT_EQ(second_t(1), d);
+ minute_t e = std::chrono::minutes(120);
+ EXPECT_EQ(minute_t(120), e);
+ hour_t f = std::chrono::hours(2);
+ EXPECT_EQ(hour_t(2), f);
+
+ std::chrono::nanoseconds g = nanosecond_t(100);
+ EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(g).count(),
+ 100);
+ std::chrono::nanoseconds h = microsecond_t(2);
+ EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(h).count(),
+ 2000);
+ std::chrono::nanoseconds i = millisecond_t(1);
+ EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(i).count(),
+ 1000000);
+ std::chrono::nanoseconds j = second_t(1);
+ EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(j).count(),
+ 1000000000);
+ std::chrono::nanoseconds k = minute_t(1);
+ EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(k).count(),
+ 60000000000);
+ std::chrono::nanoseconds l = hour_t(1);
+ EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(l).count(),
+ 3600000000000);
+}
+
+TEST_F(UnitConversion, squaredTemperature) {
+ using squared_celsius = units::compound_unit<squared<celsius>>;
+ using squared_celsius_t = units::unit_t<squared_celsius>;
+ const squared_celsius_t right(100);
+ const celsius_t rootRight = units::math::sqrt(right);
+ EXPECT_EQ(celsius_t(10), rootRight);
+}
+
+TEST_F(UnitMath, min) {
+ meter_t a(1);
+ foot_t c(1);
+ EXPECT_EQ(c, math::min(a, c));
+}
+
+TEST_F(UnitMath, max) {
+ meter_t a(1);
+ foot_t c(1);
+ EXPECT_EQ(a, math::max(a, c));
+}
+
+TEST_F(UnitMath, cos) {
+ EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(
+ cos(angle::radian_t(0)))>::type>::value));
+ EXPECT_NEAR(scalar_t(-0.41614683654), cos(angle::radian_t(2)), 5.0e-11);
+ EXPECT_NEAR(scalar_t(-0.70710678118), cos(angle::degree_t(135)),
+ 5.0e-11);
+}
+
+TEST_F(UnitMath, sin) {
+ EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(
+ sin(angle::radian_t(0)))>::type>::value));
+ EXPECT_NEAR(scalar_t(0.90929742682), sin(angle::radian_t(2)), 5.0e-11);
+ EXPECT_NEAR(scalar_t(0.70710678118), sin(angle::degree_t(135)), 5.0e-11);
+}
+
+TEST_F(UnitMath, tan) {
+ EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(
+ tan(angle::radian_t(0)))>::type>::value));
+ EXPECT_NEAR(scalar_t(-2.18503986326), tan(angle::radian_t(2)), 5.0e-11);
+ EXPECT_NEAR(scalar_t(-1.0), tan(angle::degree_t(135)), 5.0e-11);
+}
+
+TEST_F(UnitMath, acos) {
+ EXPECT_TRUE(
+ (std::is_same<
+ typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(acos(scalar_t(0)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(2).to<double>(),
+ acos(scalar_t(-0.41614683654)).to<double>(), 5.0e-11);
+ EXPECT_NEAR(
+ angle::degree_t(135).to<double>(),
+ angle::degree_t(acos(scalar_t(-0.70710678118654752440084436210485)))
+ .to<double>(),
+ 5.0e-12);
+}
+
+TEST_F(UnitMath, asin) {
+ EXPECT_TRUE(
+ (std::is_same<
+ typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(asin(scalar_t(0)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(1.14159265).to<double>(),
+ asin(scalar_t(0.90929742682)).to<double>(), 5.0e-9);
+ EXPECT_NEAR(
+ angle::degree_t(45).to<double>(),
+ angle::degree_t(asin(scalar_t(0.70710678118654752440084436210485)))
+ .to<double>(),
+ 5.0e-12);
+}
+
+TEST_F(UnitMath, atan) {
+ EXPECT_TRUE(
+ (std::is_same<
+ typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(atan(scalar_t(0)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(-1.14159265).to<double>(),
+ atan(scalar_t(-2.18503986326)).to<double>(), 5.0e-9);
+ EXPECT_NEAR(angle::degree_t(-45).to<double>(),
+ angle::degree_t(atan(scalar_t(-1.0))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, atan2) {
+ EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(atan2(
+ scalar_t(1), scalar_t(1)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(constants::detail::PI_VAL / 4).to<double>(),
+ atan2(scalar_t(2), scalar_t(2)).to<double>(), 5.0e-12);
+ EXPECT_NEAR(
+ angle::degree_t(45).to<double>(),
+ angle::degree_t(atan2(scalar_t(2), scalar_t(2))).to<double>(),
+ 5.0e-12);
+
+ EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(atan2(
+ scalar_t(1), scalar_t(1)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(constants::detail::PI_VAL / 6).to<double>(),
+ atan2(scalar_t(1), scalar_t(std::sqrt(3))).to<double>(),
+ 5.0e-12);
+ EXPECT_NEAR(angle::degree_t(30).to<double>(),
+ angle::degree_t(atan2(scalar_t(1), scalar_t(std::sqrt(3))))
+ .to<double>(),
+ 5.0e-12);
+}
+
+TEST_F(UnitMath, cosh) {
+ EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(
+ cosh(angle::radian_t(0)))>::type>::value));
+ EXPECT_NEAR(scalar_t(3.76219569108), cosh(angle::radian_t(2)), 5.0e-11);
+ EXPECT_NEAR(scalar_t(5.32275215), cosh(angle::degree_t(135)), 5.0e-9);
+}
+
+TEST_F(UnitMath, sinh) {
+ EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(
+ sinh(angle::radian_t(0)))>::type>::value));
+ EXPECT_NEAR(scalar_t(3.62686040785), sinh(angle::radian_t(2)), 5.0e-11);
+ EXPECT_NEAR(scalar_t(5.22797192), sinh(angle::degree_t(135)), 5.0e-9);
+}
+
+TEST_F(UnitMath, tanh) {
+ EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(
+ tanh(angle::radian_t(0)))>::type>::value));
+ EXPECT_NEAR(scalar_t(0.96402758007), tanh(angle::radian_t(2)), 5.0e-11);
+ EXPECT_NEAR(scalar_t(0.98219338), tanh(angle::degree_t(135)), 5.0e-11);
+}
+
+TEST_F(UnitMath, acosh) {
+ EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(
+ acosh(scalar_t(0)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(1.316957896924817).to<double>(),
+ acosh(scalar_t(2.0)).to<double>(), 5.0e-11);
+ EXPECT_NEAR(angle::degree_t(75.456129290216893).to<double>(),
+ angle::degree_t(acosh(scalar_t(2.0))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, asinh) {
+ EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(
+ asinh(scalar_t(0)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(1.443635475178810).to<double>(),
+ asinh(scalar_t(2)).to<double>(), 5.0e-9);
+ EXPECT_NEAR(angle::degree_t(82.714219883108939).to<double>(),
+ angle::degree_t(asinh(scalar_t(2))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, atanh) {
+ EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(
+ atanh(scalar_t(0)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(0.549306144334055).to<double>(),
+ atanh(scalar_t(0.5)).to<double>(), 5.0e-9);
+ EXPECT_NEAR(angle::degree_t(31.472923730945389).to<double>(),
+ angle::degree_t(atanh(scalar_t(0.5))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, exp) {
+ double val = 10.0;
+ EXPECT_EQ(std::exp(val), exp(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log) {
+ double val = 100.0;
+ EXPECT_EQ(std::log(val), log(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log10) {
+ double val = 100.0;
+ EXPECT_EQ(std::log10(val), log10(scalar_t(val)));
+}
+
+TEST_F(UnitMath, modf) {
+ double val = 100.0;
+ double modfr1;
+ scalar_t modfr2;
+ EXPECT_EQ(std::modf(val, &modfr1), modf(scalar_t(val), &modfr2));
+ EXPECT_EQ(modfr1, modfr2);
+}
+
+TEST_F(UnitMath, exp2) {
+ double val = 10.0;
+ EXPECT_EQ(std::exp2(val), exp2(scalar_t(val)));
+}
+
+TEST_F(UnitMath, expm1) {
+ double val = 10.0;
+ EXPECT_EQ(std::expm1(val), expm1(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log1p) {
+ double val = 10.0;
+ EXPECT_EQ(std::log1p(val), log1p(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log2) {
+ double val = 10.0;
+ EXPECT_EQ(std::log2(val), log2(scalar_t(val)));
+}
+
+TEST_F(UnitMath, pow) {
+ bool isSame;
+ meter_t value(10.0);
+
+ auto sq = pow<2>(value);
+ EXPECT_NEAR(100.0, sq(), 5.0e-2);
+ isSame = std::is_same<decltype(sq), square_meter_t>::value;
+ EXPECT_TRUE(isSame);
+
+ auto cube = pow<3>(value);
+ EXPECT_NEAR(1000.0, cube(), 5.0e-2);
+ isSame = std::is_same<decltype(cube), unit_t<cubed<meter>>>::value;
+ EXPECT_TRUE(isSame);
+
+ auto fourth = pow<4>(value);
+ EXPECT_NEAR(10000.0, fourth(), 5.0e-2);
+ isSame = std::is_same<
+ decltype(fourth),
+ unit_t<compound_unit<squared<meter>, squared<meter>>>>::value;
+ EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitMath, sqrt) {
+ EXPECT_TRUE((std::is_same<typename std::decay<meter_t>::type,
+ typename std::decay<decltype(sqrt(
+ square_meter_t(4.0)))>::type>::value));
+ EXPECT_NEAR(meter_t(2.0).to<double>(),
+ sqrt(square_meter_t(4.0)).to<double>(), 5.0e-9);
+
+ EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(
+ sqrt(steradian_t(16.0)))>::type>::value));
+ EXPECT_NEAR(angle::radian_t(4.0).to<double>(),
+ sqrt(steradian_t(16.0)).to<double>(), 5.0e-9);
+
+ EXPECT_TRUE((std::is_convertible<typename std::decay<foot_t>::type,
+ typename std::decay<decltype(sqrt(
+ square_foot_t(10.0)))>::type>::value));
+
+ // for rational conversion (i.e. no integral root) let's check a bunch of
+ // different ways this could go wrong
+ foot_t resultFt = sqrt(square_foot_t(10.0));
+ EXPECT_NEAR(foot_t(3.16227766017).to<double>(),
+ sqrt(square_foot_t(10.0)).to<double>(), 5.0e-9);
+ EXPECT_NEAR(foot_t(3.16227766017).to<double>(), resultFt.to<double>(),
+ 5.0e-9);
+ EXPECT_EQ(resultFt, sqrt(square_foot_t(10.0)));
+}
+
+TEST_F(UnitMath, hypot) {
+ EXPECT_TRUE((std::is_same<typename std::decay<meter_t>::type,
+ typename std::decay<decltype(hypot(
+ meter_t(3.0), meter_t(4.0)))>::type>::value));
+ EXPECT_NEAR(meter_t(5.0).to<double>(),
+ (hypot(meter_t(3.0), meter_t(4.0))).to<double>(), 5.0e-9);
+
+ EXPECT_TRUE((std::is_same<typename std::decay<foot_t>::type,
+ typename std::decay<decltype(hypot(
+ foot_t(3.0), meter_t(1.2192)))>::type>::value));
+ EXPECT_NEAR(foot_t(5.0).to<double>(),
+ (hypot(foot_t(3.0), meter_t(1.2192))).to<double>(), 5.0e-9);
+}
+
+TEST_F(UnitMath, ceil) {
+ double val = 101.1;
+ EXPECT_EQ(std::ceil(val), ceil(meter_t(val)).to<double>());
+ EXPECT_TRUE((std::is_same<typename std::decay<meter_t>::type,
+ typename std::decay<decltype(
+ ceil(meter_t(val)))>::type>::value));
+}
+
+TEST_F(UnitMath, floor) {
+ double val = 101.1;
+ EXPECT_EQ(std::floor(val), floor(scalar_t(val)));
+}
+
+TEST_F(UnitMath, fmod) {
+ EXPECT_EQ(std::fmod(100.0, 101.2),
+ fmod(meter_t(100.0), meter_t(101.2)).to<double>());
+}
+
+TEST_F(UnitMath, trunc) {
+ double val = 101.1;
+ EXPECT_EQ(std::trunc(val), trunc(scalar_t(val)));
+}
+
+TEST_F(UnitMath, round) {
+ double val = 101.1;
+ EXPECT_EQ(std::round(val), round(scalar_t(val)));
+}
+
+TEST_F(UnitMath, copysign) {
+ double sign = -1;
+ meter_t val(5.0);
+ EXPECT_EQ(meter_t(-5.0), copysign(val, sign));
+ EXPECT_EQ(meter_t(-5.0), copysign(val, angle::radian_t(sign)));
+}
+
+TEST_F(UnitMath, fdim) {
+ EXPECT_EQ(meter_t(0.0), fdim(meter_t(8.0), meter_t(10.0)));
+ EXPECT_EQ(meter_t(2.0), fdim(meter_t(10.0), meter_t(8.0)));
+ EXPECT_NEAR(meter_t(9.3904).to<double>(),
+ fdim(meter_t(10.0), foot_t(2.0)).to<double>(),
+ 5.0e-320); // not sure why they aren't comparing exactly equal,
+ // but clearly they are.
+}
+
+TEST_F(UnitMath, fmin) {
+ EXPECT_EQ(meter_t(8.0), fmin(meter_t(8.0), meter_t(10.0)));
+ EXPECT_EQ(meter_t(8.0), fmin(meter_t(10.0), meter_t(8.0)));
+ EXPECT_EQ(foot_t(2.0), fmin(meter_t(10.0), foot_t(2.0)));
+}
+
+TEST_F(UnitMath, fmax) {
+ EXPECT_EQ(meter_t(10.0), fmax(meter_t(8.0), meter_t(10.0)));
+ EXPECT_EQ(meter_t(10.0), fmax(meter_t(10.0), meter_t(8.0)));
+ EXPECT_EQ(meter_t(10.0), fmax(meter_t(10.0), foot_t(2.0)));
+}
+
+TEST_F(UnitMath, fabs) {
+ EXPECT_EQ(meter_t(10.0), fabs(meter_t(-10.0)));
+ EXPECT_EQ(meter_t(10.0), fabs(meter_t(10.0)));
+}
+
+TEST_F(UnitMath, abs) {
+ EXPECT_EQ(meter_t(10.0), abs(meter_t(-10.0)));
+ EXPECT_EQ(meter_t(10.0), abs(meter_t(10.0)));
+}
+
+TEST_F(UnitMath, fma) {
+ meter_t x(2.0);
+ meter_t y(3.0);
+ square_meter_t z(1.0);
+ EXPECT_EQ(square_meter_t(7.0), fma(x, y, z));
+}
+
+// Constexpr
+#if !defined(_MSC_VER) || _MSC_VER > 1800
+TEST_F(Constexpr, construction) {
+ constexpr meter_t result0(0);
+ constexpr auto result1 = make_unit<meter_t>(1);
+ constexpr auto result2 = meter_t(2);
+
+ EXPECT_EQ(meter_t(0), result0);
+ EXPECT_EQ(meter_t(1), result1);
+ EXPECT_EQ(meter_t(2), result2);
+
+ EXPECT_TRUE(noexcept(result0));
+ EXPECT_TRUE(noexcept(result1));
+ EXPECT_TRUE(noexcept(result2));
+}
+
+TEST_F(Constexpr, constants) {
+ EXPECT_TRUE(noexcept(constants::c()));
+ EXPECT_TRUE(noexcept(constants::G()));
+ EXPECT_TRUE(noexcept(constants::h()));
+ EXPECT_TRUE(noexcept(constants::mu0()));
+ EXPECT_TRUE(noexcept(constants::epsilon0()));
+ EXPECT_TRUE(noexcept(constants::Z0()));
+ EXPECT_TRUE(noexcept(constants::k_e()));
+ EXPECT_TRUE(noexcept(constants::e()));
+ EXPECT_TRUE(noexcept(constants::m_e()));
+ EXPECT_TRUE(noexcept(constants::m_p()));
+ EXPECT_TRUE(noexcept(constants::mu_B()));
+ EXPECT_TRUE(noexcept(constants::N_A()));
+ EXPECT_TRUE(noexcept(constants::R()));
+ EXPECT_TRUE(noexcept(constants::k_B()));
+ EXPECT_TRUE(noexcept(constants::F()));
+ EXPECT_TRUE(noexcept(constants::sigma()));
+}
+
+TEST_F(Constexpr, arithmetic) {
+ constexpr auto result0(1_m + 1_m);
+ constexpr auto result1(1_m - 1_m);
+ constexpr auto result2(1_m * 1_m);
+ constexpr auto result3(1_m / 1_m);
+ constexpr auto result4(meter_t(1) + meter_t(1));
+ constexpr auto result5(meter_t(1) - meter_t(1));
+ constexpr auto result6(meter_t(1) * meter_t(1));
+ constexpr auto result7(meter_t(1) / meter_t(1));
+ constexpr auto result8(units::math::cpow<2>(meter_t(2)));
+ constexpr auto result9 = units::math::cpow<3>(2_m);
+ constexpr auto result10 = 2_m * 2_m;
+
+ EXPECT_TRUE(noexcept(result0));
+ EXPECT_TRUE(noexcept(result1));
+ EXPECT_TRUE(noexcept(result2));
+ EXPECT_TRUE(noexcept(result3));
+ EXPECT_TRUE(noexcept(result4));
+ EXPECT_TRUE(noexcept(result5));
+ EXPECT_TRUE(noexcept(result6));
+ EXPECT_TRUE(noexcept(result7));
+ EXPECT_TRUE(noexcept(result8));
+ EXPECT_TRUE(noexcept(result9));
+ EXPECT_TRUE(noexcept(result10));
+
+ EXPECT_EQ(8_cu_m, result9);
+ EXPECT_EQ(4_sq_m, result10);
+}
+
+TEST_F(Constexpr, realtional) {
+ constexpr bool equalityTrue = (1_m == 1_m);
+ constexpr bool equalityFalse = (1_m == 2_m);
+ constexpr bool lessThanTrue = (1_m < 2_m);
+ constexpr bool lessThanFalse = (1_m < 1_m);
+ constexpr bool lessThanEqualTrue1 = (1_m <= 1_m);
+ constexpr bool lessThanEqualTrue2 = (1_m <= 2_m);
+ constexpr bool lessThanEqualFalse = (1_m < 0_m);
+ constexpr bool greaterThanTrue = (2_m > 1_m);
+ constexpr bool greaterThanFalse = (2_m > 2_m);
+ constexpr bool greaterThanEqualTrue1 = (2_m >= 1_m);
+ constexpr bool greaterThanEqualTrue2 = (2_m >= 2_m);
+ constexpr bool greaterThanEqualFalse = (2_m > 3_m);
+
+ EXPECT_TRUE(equalityTrue);
+ EXPECT_TRUE(lessThanTrue);
+ EXPECT_TRUE(lessThanEqualTrue1);
+ EXPECT_TRUE(lessThanEqualTrue2);
+ EXPECT_TRUE(greaterThanTrue);
+ EXPECT_TRUE(greaterThanEqualTrue1);
+ EXPECT_TRUE(greaterThanEqualTrue2);
+ EXPECT_FALSE(equalityFalse);
+ EXPECT_FALSE(lessThanFalse);
+ EXPECT_FALSE(lessThanEqualFalse);
+ EXPECT_FALSE(greaterThanFalse);
+ EXPECT_FALSE(greaterThanEqualFalse);
+}
+
+TEST_F(Constexpr, stdArray) {
+ constexpr std::array<meter_t, 5> arr = {0_m, 1_m, 2_m, 3_m, 4_m};
+ constexpr bool equal = (arr[3] == 3_m);
+ EXPECT_TRUE(equal);
+}
+
+#endif
+
+TEST_F(CompileTimeArithmetic, unit_value_t) {
+ typedef unit_value_t<meters, 3, 2> mRatio;
+ EXPECT_EQ(meter_t(1.5), mRatio::value());
+}
+
+TEST_F(CompileTimeArithmetic, is_unit_value_t) {
+ typedef unit_value_t<meters, 3, 2> mRatio;
+
+ EXPECT_TRUE((traits::is_unit_value_t<mRatio>::value));
+ EXPECT_FALSE((traits::is_unit_value_t<meter_t>::value));
+ EXPECT_FALSE((traits::is_unit_value_t<double>::value));
+
+ EXPECT_TRUE((traits::is_unit_value_t<mRatio, meters>::value));
+ EXPECT_FALSE((traits::is_unit_value_t<meter_t, meters>::value));
+ EXPECT_FALSE((traits::is_unit_value_t<double, meters>::value));
+}
+
+TEST_F(CompileTimeArithmetic, is_unit_value_t_category) {
+ typedef unit_value_t<feet, 3, 2> mRatio;
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::length_unit, mRatio>::value));
+ EXPECT_FALSE(
+ (traits::is_unit_value_t_category<category::angle_unit, mRatio>::value));
+ EXPECT_FALSE((
+ traits::is_unit_value_t_category<category::length_unit, meter_t>::value));
+ EXPECT_FALSE(
+ (traits::is_unit_value_t_category<category::length_unit, double>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_add) {
+ typedef unit_value_t<meters, 3, 2> mRatio;
+
+ using sum = unit_value_add<mRatio, mRatio>;
+ EXPECT_EQ(meter_t(3.0), sum::value());
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::length_unit, sum>::value));
+
+ typedef unit_value_t<feet, 1> ftRatio;
+
+ using sumf = unit_value_add<ftRatio, mRatio>;
+ EXPECT_TRUE((
+ std::is_same<typename std::decay<foot_t>::type,
+ typename std::decay<decltype(sumf::value())>::type>::value));
+ EXPECT_NEAR(5.92125984, sumf::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::length_unit, sumf>::value));
+
+ typedef unit_value_t<celsius, 1> cRatio;
+ typedef unit_value_t<fahrenheit, 2> fRatio;
+
+ using sumc = unit_value_add<cRatio, fRatio>;
+ EXPECT_TRUE((
+ std::is_same<typename std::decay<celsius_t>::type,
+ typename std::decay<decltype(sumc::value())>::type>::value));
+ EXPECT_NEAR(2.11111111111, sumc::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::temperature_unit,
+ sumc>::value));
+
+ typedef unit_value_t<angle::radian, 1> rRatio;
+ typedef unit_value_t<angle::degree, 3> dRatio;
+
+ using sumr = unit_value_add<rRatio, dRatio>;
+ EXPECT_TRUE((
+ std::is_same<typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(sumr::value())>::type>::value));
+ EXPECT_NEAR(1.05235988, sumr::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::angle_unit, sumr>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_subtract) {
+ typedef unit_value_t<meters, 3, 2> mRatio;
+
+ using diff = unit_value_subtract<mRatio, mRatio>;
+ EXPECT_EQ(meter_t(0), diff::value());
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::length_unit, diff>::value));
+
+ typedef unit_value_t<feet, 1> ftRatio;
+
+ using difff = unit_value_subtract<ftRatio, mRatio>;
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<foot_t>::type,
+ typename std::decay<decltype(difff::value())>::type>::value));
+ EXPECT_NEAR(-3.92125984, difff::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::length_unit, difff>::value));
+
+ typedef unit_value_t<celsius, 1> cRatio;
+ typedef unit_value_t<fahrenheit, 2> fRatio;
+
+ using diffc = unit_value_subtract<cRatio, fRatio>;
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<celsius_t>::type,
+ typename std::decay<decltype(diffc::value())>::type>::value));
+ EXPECT_NEAR(-0.11111111111, diffc::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::temperature_unit,
+ diffc>::value));
+
+ typedef unit_value_t<angle::radian, 1> rRatio;
+ typedef unit_value_t<angle::degree, 3> dRatio;
+
+ using diffr = unit_value_subtract<rRatio, dRatio>;
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<angle::radian_t>::type,
+ typename std::decay<decltype(diffr::value())>::type>::value));
+ EXPECT_NEAR(0.947640122, diffr::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::angle_unit, diffr>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_multiply) {
+ typedef unit_value_t<meters, 2> mRatio;
+ typedef unit_value_t<feet, 656168, 100000> ftRatio; // 2 meter
+
+ using product = unit_value_multiply<mRatio, mRatio>;
+ EXPECT_EQ(square_meter_t(4), product::value());
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::area_unit, product>::value));
+
+ using productM = unit_value_multiply<mRatio, ftRatio>;
+
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<square_meter_t>::type,
+ typename std::decay<decltype(productM::value())>::type>::value));
+ EXPECT_NEAR(4.0, productM::value().to<double>(), 5.0e-7);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::area_unit, productM>::value));
+
+ using productF = unit_value_multiply<ftRatio, mRatio>;
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<square_foot_t>::type,
+ typename std::decay<decltype(productF::value())>::type>::value));
+ EXPECT_NEAR(43.0556444224, productF::value().to<double>(), 5.0e-6);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::area_unit, productF>::value));
+
+ using productF2 = unit_value_multiply<ftRatio, ftRatio>;
+ EXPECT_TRUE(
+ (std::is_same<
+ typename std::decay<square_foot_t>::type,
+ typename std::decay<decltype(productF2::value())>::type>::value));
+ EXPECT_NEAR(43.0556444224, productF2::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE((
+ traits::is_unit_value_t_category<category::area_unit, productF2>::value));
+
+ typedef unit_value_t<units::force::newton, 5> nRatio;
+
+ using productN = unit_value_multiply<nRatio, ftRatio>;
+ EXPECT_FALSE(
+ (std::is_same<
+ typename std::decay<torque::newton_meter_t>::type,
+ typename std::decay<decltype(productN::value())>::type>::value));
+ EXPECT_TRUE((std::is_convertible<
+ typename std::decay<torque::newton_meter_t>::type,
+ typename std::decay<decltype(productN::value())>::type>::value));
+ EXPECT_NEAR(32.8084, productN::value().to<double>(), 5.0e-8);
+ EXPECT_NEAR(10.0, (productN::value().convert<newton_meter>().to<double>()),
+ 5.0e-7);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::torque_unit,
+ productN>::value));
+
+ typedef unit_value_t<angle::radian, 11, 10> r1Ratio;
+ typedef unit_value_t<angle::radian, 22, 10> r2Ratio;
+
+ using productR = unit_value_multiply<r1Ratio, r2Ratio>;
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<steradian_t>::type,
+ typename std::decay<decltype(productR::value())>::type>::value));
+ EXPECT_NEAR(2.42, productR::value().to<double>(), 5.0e-8);
+ EXPECT_NEAR(7944.39137,
+ (productR::value().convert<degrees_squared>().to<double>()),
+ 5.0e-6);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::solid_angle_unit,
+ productR>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_divide) {
+ typedef unit_value_t<meters, 2> mRatio;
+ typedef unit_value_t<feet, 656168, 100000> ftRatio; // 2 meter
+
+ using product = unit_value_divide<mRatio, mRatio>;
+ EXPECT_EQ(scalar_t(1), product::value());
+ EXPECT_TRUE((
+ traits::is_unit_value_t_category<category::scalar_unit, product>::value));
+
+ using productM = unit_value_divide<mRatio, ftRatio>;
+
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(productM::value())>::type>::value));
+ EXPECT_NEAR(1, productM::value().to<double>(), 5.0e-7);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::scalar_unit,
+ productM>::value));
+
+ using productF = unit_value_divide<ftRatio, mRatio>;
+ EXPECT_TRUE((std::is_same<
+ typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(productF::value())>::type>::value));
+ EXPECT_NEAR(1.0, productF::value().to<double>(), 5.0e-6);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::scalar_unit,
+ productF>::value));
+
+ using productF2 = unit_value_divide<ftRatio, ftRatio>;
+ EXPECT_TRUE(
+ (std::is_same<
+ typename std::decay<scalar_t>::type,
+ typename std::decay<decltype(productF2::value())>::type>::value));
+ EXPECT_NEAR(1.0, productF2::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::scalar_unit,
+ productF2>::value));
+
+ typedef unit_value_t<seconds, 10> sRatio;
+
+ using productMS = unit_value_divide<mRatio, sRatio>;
+ EXPECT_TRUE(
+ (std::is_same<
+ typename std::decay<meters_per_second_t>::type,
+ typename std::decay<decltype(productMS::value())>::type>::value));
+ EXPECT_NEAR(0.2, productMS::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::velocity_unit,
+ productMS>::value));
+
+ typedef unit_value_t<angle::radian, 20> rRatio;
+
+ using productRS = unit_value_divide<rRatio, sRatio>;
+ EXPECT_TRUE(
+ (std::is_same<
+ typename std::decay<radians_per_second_t>::type,
+ typename std::decay<decltype(productRS::value())>::type>::value));
+ EXPECT_NEAR(2, productRS::value().to<double>(), 5.0e-8);
+ EXPECT_NEAR(114.592,
+ (productRS::value().convert<degrees_per_second>().to<double>()),
+ 5.0e-4);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::angular_velocity_unit,
+ productRS>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_power) {
+ typedef unit_value_t<meters, 2> mRatio;
+
+ using sq = unit_value_power<mRatio, 2>;
+ EXPECT_TRUE((std::is_convertible<
+ typename std::decay<square_meter_t>::type,
+ typename std::decay<decltype(sq::value())>::type>::value));
+ EXPECT_NEAR(4, sq::value().to<double>(), 5.0e-8);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::area_unit, sq>::value));
+
+ typedef unit_value_t<angle::radian, 18, 10> rRatio;
+
+ using sqr = unit_value_power<rRatio, 2>;
+ EXPECT_TRUE((std::is_convertible<
+ typename std::decay<steradian_t>::type,
+ typename std::decay<decltype(sqr::value())>::type>::value));
+ EXPECT_NEAR(3.24, sqr::value().to<double>(), 5.0e-8);
+ EXPECT_NEAR(10636.292574038049895092690529904,
+ (sqr::value().convert<degrees_squared>().to<double>()), 5.0e-10);
+ EXPECT_TRUE((traits::is_unit_value_t_category<category::solid_angle_unit,
+ sqr>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_sqrt) {
+ typedef unit_value_t<square_meters, 10> mRatio;
+
+ using root = unit_value_sqrt<mRatio>;
+ EXPECT_TRUE((std::is_convertible<
+ typename std::decay<meter_t>::type,
+ typename std::decay<decltype(root::value())>::type>::value));
+ EXPECT_NEAR(3.16227766017, root::value().to<double>(), 5.0e-9);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::length_unit, root>::value));
+
+ typedef unit_value_t<hectare, 51, 7> hRatio;
+
+ using rooth = unit_value_sqrt<hRatio, 100000000>;
+ EXPECT_TRUE((std::is_convertible<
+ typename std::decay<mile_t>::type,
+ typename std::decay<decltype(rooth::value())>::type>::value));
+ EXPECT_NEAR(2.69920623253, rooth::value().to<double>(), 5.0e-8);
+ EXPECT_NEAR(269.920623, rooth::value().convert<meters>().to<double>(),
+ 5.0e-6);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::length_unit, rooth>::value));
+
+ typedef unit_value_t<steradian, 18, 10> rRatio;
+
+ using rootr = unit_value_sqrt<rRatio>;
+ EXPECT_TRUE((traits::is_angle_unit<decltype(rootr::value())>::value));
+ EXPECT_NEAR(1.3416407865, rootr::value().to<double>(), 5.0e-8);
+ EXPECT_NEAR(76.870352574,
+ rootr::value().convert<angle::degrees>().to<double>(), 5.0e-6);
+ EXPECT_TRUE(
+ (traits::is_unit_value_t_category<category::angle_unit, rootr>::value));
+}
+
+TEST_F(CaseStudies, radarRangeEquation) {
+ watt_t P_t; // transmit power
+ scalar_t G; // gain
+ meter_t lambda; // wavelength
+ square_meter_t sigma; // radar cross section
+ meter_t R; // range
+ kelvin_t T_s; // system noise temp
+ hertz_t B_n; // bandwidth
+ scalar_t L; // loss
+
+ P_t = megawatt_t(1.4);
+ G = dB_t(33.0);
+ lambda = constants::c / megahertz_t(2800);
+ sigma = square_meter_t(1.0);
+ R = meter_t(111000.0);
+ T_s = kelvin_t(950.0);
+ B_n = megahertz_t(1.67);
+ L = dB_t(8.0);
+
+ scalar_t SNR = (P_t * math::pow<2>(G) * math::pow<2>(lambda) * sigma) /
+ (math::pow<3>(4 * constants::pi) * math::pow<4>(R) *
+ constants::k_B * T_s * B_n * L);
+
+ EXPECT_NEAR(1.535, SNR(), 5.0e-4);
+}
+
+TEST_F(CaseStudies, pythagoreanTheorum) {
+ EXPECT_EQ(meter_t(3), RightTriangle::a::value());
+ EXPECT_EQ(meter_t(4), RightTriangle::b::value());
+ EXPECT_EQ(meter_t(5), RightTriangle::c::value());
+ EXPECT_TRUE(pow<2>(RightTriangle::a::value()) +
+ pow<2>(RightTriangle::b::value()) ==
+ pow<2>(RightTriangle::c::value()));
+}
diff --git a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
new file mode 100644
index 0000000..2db9b54
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
@@ -0,0 +1,298 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 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,
+ {"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_SUITE_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..9a66a2e
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
@@ -0,0 +1,148 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 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..d11fdda
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
@@ -0,0 +1,736 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 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
+ const uint8_t contents[] = {0x03u, 0xe8u};
+ auto message = BuildMessage(0x08, true, true, contents);
+ handleData = [&](StringRef) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x08, true, false, contents);
+ 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
+ const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
+ auto message = BuildMessage(0x08, true, true, contents);
+ handleData = [&](StringRef) {
+ clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ };
+
+ loop->Run();
+
+ auto expectData = BuildMessage(0x08, true, false, contents);
+ 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;
+ });
+ };
+ const uint8_t contents[] = {0x03u, 0xe8u};
+ auto message = BuildMessage(0x08, true, true, contents);
+ 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, contents);
+ 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");
+ });
+ };
+ const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
+ auto message = BuildMessage(0x08, true, true, contents);
+ 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, contents);
+ 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_SUITE_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_SUITE_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_SUITE_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..2e37a17
--- /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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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<uint8_t>(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<uint8_t>(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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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..85b9b91
--- /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_SUITE_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_SUITE(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_SUITE(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_SUITE(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..b60e5ac
--- /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_SUITE(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_SUITE(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_SUITE(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_SUITE(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_SUITE(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_SUITE(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..2505ef5
--- /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_SUITE_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..c0a237d
--- /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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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_SUITE_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/llvm/FunctionExtrasTest.cpp b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp
new file mode 100644
index 0000000..5dcd11f
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp
@@ -0,0 +1,228 @@
+//===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/FunctionExtras.h"
+#include "gtest/gtest.h"
+
+#include <memory>
+
+using namespace wpi;
+
+namespace {
+
+TEST(UniqueFunctionTest, Basic) {
+ unique_function<int(int, int)> Sum = [](int A, int B) { return A + B; };
+ EXPECT_EQ(Sum(1, 2), 3);
+
+ unique_function<int(int, int)> Sum2 = std::move(Sum);
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ unique_function<int(int, int)> Sum3 = [](int A, int B) { return A + B; };
+ Sum2 = std::move(Sum3);
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ Sum2 = unique_function<int(int, int)>([](int A, int B) { return A + B; });
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ // Explicit self-move test.
+ *&Sum2 = std::move(Sum2);
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ Sum2 = unique_function<int(int, int)>();
+ EXPECT_FALSE(Sum2);
+
+ // Make sure we can forward through l-value reference parameters.
+ unique_function<void(int &)> Inc = [](int &X) { ++X; };
+ int X = 42;
+ Inc(X);
+ EXPECT_EQ(X, 43);
+
+ // Make sure we can forward through r-value reference parameters with
+ // move-only types.
+ unique_function<int(std::unique_ptr<int> &&)> ReadAndDeallocByRef =
+ [](std::unique_ptr<int> &&Ptr) {
+ int V = *Ptr;
+ Ptr.reset();
+ return V;
+ };
+ std::unique_ptr<int> Ptr{new int(13)};
+ EXPECT_EQ(ReadAndDeallocByRef(std::move(Ptr)), 13);
+ EXPECT_FALSE((bool)Ptr);
+
+ // Make sure we can pass a move-only temporary as opposed to a local variable.
+ EXPECT_EQ(ReadAndDeallocByRef(std::unique_ptr<int>(new int(42))), 42);
+
+ // Make sure we can pass a move-only type by-value.
+ unique_function<int(std::unique_ptr<int>)> ReadAndDeallocByVal =
+ [](std::unique_ptr<int> Ptr) {
+ int V = *Ptr;
+ Ptr.reset();
+ return V;
+ };
+ Ptr.reset(new int(13));
+ EXPECT_EQ(ReadAndDeallocByVal(std::move(Ptr)), 13);
+ EXPECT_FALSE((bool)Ptr);
+
+ EXPECT_EQ(ReadAndDeallocByVal(std::unique_ptr<int>(new int(42))), 42);
+}
+
+TEST(UniqueFunctionTest, Captures) {
+ long A = 1, B = 2, C = 3, D = 4, E = 5;
+
+ unique_function<long()> Tmp;
+
+ unique_function<long()> C1 = [A]() { return A; };
+ EXPECT_EQ(C1(), 1);
+ Tmp = std::move(C1);
+ EXPECT_EQ(Tmp(), 1);
+
+ unique_function<long()> C2 = [A, B]() { return A + B; };
+ EXPECT_EQ(C2(), 3);
+ Tmp = std::move(C2);
+ EXPECT_EQ(Tmp(), 3);
+
+ unique_function<long()> C3 = [A, B, C]() { return A + B + C; };
+ EXPECT_EQ(C3(), 6);
+ Tmp = std::move(C3);
+ EXPECT_EQ(Tmp(), 6);
+
+ unique_function<long()> C4 = [A, B, C, D]() { return A + B + C + D; };
+ EXPECT_EQ(C4(), 10);
+ Tmp = std::move(C4);
+ EXPECT_EQ(Tmp(), 10);
+
+ unique_function<long()> C5 = [A, B, C, D, E]() { return A + B + C + D + E; };
+ EXPECT_EQ(C5(), 15);
+ Tmp = std::move(C5);
+ EXPECT_EQ(Tmp(), 15);
+}
+
+TEST(UniqueFunctionTest, MoveOnly) {
+ struct SmallCallable {
+ std::unique_ptr<int> A{new int(1)};
+
+ int operator()(int B) { return *A + B; }
+ };
+ unique_function<int(int)> Small = SmallCallable();
+ EXPECT_EQ(Small(2), 3);
+ unique_function<int(int)> Small2 = std::move(Small);
+ EXPECT_EQ(Small2(2), 3);
+
+ struct LargeCallable {
+ std::unique_ptr<int> A{new int(1)};
+ std::unique_ptr<int> B{new int(2)};
+ std::unique_ptr<int> C{new int(3)};
+ std::unique_ptr<int> D{new int(4)};
+ std::unique_ptr<int> E{new int(5)};
+
+ int operator()() { return *A + *B + *C + *D + *E; }
+ };
+ unique_function<int()> Large = LargeCallable();
+ EXPECT_EQ(Large(), 15);
+ unique_function<int()> Large2 = std::move(Large);
+ EXPECT_EQ(Large2(), 15);
+}
+
+TEST(UniqueFunctionTest, CountForwardingCopies) {
+ struct CopyCounter {
+ int &CopyCount;
+
+ CopyCounter(int &CopyCount) : CopyCount(CopyCount) {}
+ CopyCounter(const CopyCounter &Arg) : CopyCount(Arg.CopyCount) {
+ ++CopyCount;
+ }
+ };
+
+ unique_function<void(CopyCounter)> ByValF = [](CopyCounter) {};
+ int CopyCount = 0;
+ ByValF(CopyCounter(CopyCount));
+ EXPECT_EQ(1, CopyCount);
+
+ CopyCount = 0;
+ {
+ CopyCounter Counter{CopyCount};
+ ByValF(Counter);
+ }
+ EXPECT_EQ(2, CopyCount);
+
+ // Check that we don't generate a copy at all when we can bind a reference all
+ // the way down, even if that reference could *in theory* allow copies.
+ unique_function<void(const CopyCounter &)> ByRefF = [](const CopyCounter &) {
+ };
+ CopyCount = 0;
+ ByRefF(CopyCounter(CopyCount));
+ EXPECT_EQ(0, CopyCount);
+
+ CopyCount = 0;
+ {
+ CopyCounter Counter{CopyCount};
+ ByRefF(Counter);
+ }
+ EXPECT_EQ(0, CopyCount);
+
+ // If we use a reference, we can make a stronger guarantee that *no* copy
+ // occurs.
+ struct Uncopyable {
+ Uncopyable() = default;
+ Uncopyable(const Uncopyable &) = delete;
+ };
+ unique_function<void(const Uncopyable &)> UncopyableF =
+ [](const Uncopyable &) {};
+ UncopyableF(Uncopyable());
+ Uncopyable X;
+ UncopyableF(X);
+}
+
+TEST(UniqueFunctionTest, CountForwardingMoves) {
+ struct MoveCounter {
+ int &MoveCount;
+
+ MoveCounter(int &MoveCount) : MoveCount(MoveCount) {}
+ MoveCounter(MoveCounter &&Arg) : MoveCount(Arg.MoveCount) { ++MoveCount; }
+ };
+
+ unique_function<void(MoveCounter)> ByValF = [](MoveCounter) {};
+ int MoveCount = 0;
+ ByValF(MoveCounter(MoveCount));
+ EXPECT_EQ(1, MoveCount);
+
+ MoveCount = 0;
+ {
+ MoveCounter Counter{MoveCount};
+ ByValF(std::move(Counter));
+ }
+ EXPECT_EQ(2, MoveCount);
+
+ // Check that when we use an r-value reference we get no spurious copies.
+ unique_function<void(MoveCounter &&)> ByRefF = [](MoveCounter &&) {};
+ MoveCount = 0;
+ ByRefF(MoveCounter(MoveCount));
+ EXPECT_EQ(0, MoveCount);
+
+ MoveCount = 0;
+ {
+ MoveCounter Counter{MoveCount};
+ ByRefF(std::move(Counter));
+ }
+ EXPECT_EQ(0, MoveCount);
+
+ // If we use an r-value reference we can in fact make a stronger guarantee
+ // with an unmovable type.
+ struct Unmovable {
+ Unmovable() = default;
+ Unmovable(Unmovable &&) = delete;
+ };
+ unique_function<void(const Unmovable &)> UnmovableF = [](const Unmovable &) {
+ };
+ UnmovableF(Unmovable());
+ Unmovable X;
+ UnmovableF(X);
+}
+
+} // anonymous namespace
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..448a757
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
@@ -0,0 +1,271 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2016-2019 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 lock(m_mutex);
+ while (!m_set) {
+ m_condition.wait(lock);
+ }
+ }
+ // Sets the condition to true, and wakes all waiting threads.
+ void Notify() {
+ std::scoped_lock 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..b36e558
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/spinlock_bench.cpp
@@ -0,0 +1,166 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 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::scoped_lock 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::scoped_lock 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::scoped_lock 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::scoped_lock 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::scoped_lock 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::scoped_lock 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::scoped_lock 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::scoped_lock 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::scoped_lock 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/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..5d2e120
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
@@ -0,0 +1,70 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 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..6e1bd5c
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
@@ -0,0 +1,74 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 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