Encoder: Fix off-by-one error encoding negative numbers

The documentation said we encoded the negative value equivalent to the
passed absolute value. That means encode_number() requires the
subtraction.

This commit takes the opportunity to unit-test the rest of the integer
API in the parser.

Signed-off-by: Thiago Macieira <thiago.macieira@intel.com>
diff --git a/src/cborencoder.c b/src/cborencoder.c
index c8a6920..fb95024 100644
--- a/src/cborencoder.c
+++ b/src/cborencoder.c
@@ -332,11 +332,13 @@
  * Appends the negative 64-bit integer whose absolute value is \a
  * absolute_value to the CBOR stream provided by \a encoder.
  *
+ * If the value \a absolute_value is zero, this function encodes -2^64 - 1.
+ *
  * \sa cbor_encode_uint, cbor_encode_int
  */
 CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value)
 {
-    return encode_number(encoder, absolute_value, NegativeIntegerType << MajorTypeShift);
+    return encode_number(encoder, absolute_value - 1, NegativeIntegerType << MajorTypeShift);
 }
 
 /**
diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp
index 9224b24..265cc00 100644
--- a/tests/encoder/tst_encoder.cpp
+++ b/tests/encoder/tst_encoder.cpp
@@ -129,6 +129,9 @@
     return QByteArray::fromRawData(data, N - 1);
 }
 
+struct NegativeInteger { quint64 abs; };
+Q_DECLARE_METATYPE(NegativeInteger)
+
 struct SimpleType { uint8_t type; };
 Q_DECLARE_METATYPE(SimpleType)
 
@@ -209,6 +212,8 @@
     }
 
     default:
+        if (type == qMetaTypeId<NegativeInteger>())
+            return cbor_encode_negative_int(encoder, v.value<NegativeInteger>().abs);
         if (type == qMetaTypeId<SimpleType>())
             return cbor_encode_simple_value(encoder, v.value<SimpleType>().type);
 #if QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
@@ -306,7 +311,7 @@
     QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
                                 << QVariant(std::numeric_limits<quint64>::max());
 
-    // signed integers containing positive numbers
+    // signed integers containing non-negative numbers
     QTest::newRow("0") << raw("\x00") << QVariant(0);
     QTest::newRow("1") << raw("\x01") << QVariant(1);
     QTest::newRow("10") << raw("\x0a") << QVariant(10);
@@ -319,7 +324,7 @@
     QTest::newRow("4294967295") << raw("\x1a\xff\xff\xff\xff") << QVariant(Q_INT64_C(4294967295));
     QTest::newRow("4294967296") << raw("\x1b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(4294967296));
 
-    // negative integers
+    // signed integers containing negative numbers
     QTest::newRow("-1") << raw("\x20") << QVariant(-1);
     QTest::newRow("-2") << raw("\x21") << QVariant(-2);
     QTest::newRow("-24") << raw("\x37") << QVariant(-24);
@@ -330,8 +335,22 @@
     QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << QVariant(-65537);
     QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << QVariant(Q_INT64_C(-4294967296));
     QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(-4294967297));
-//    QTest::newRow("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
-//                                 << QVariant::fromValue(BigNegative{std::numeric_limits<quint64>::max()});
+
+    // negative integers
+    auto neg = [](quint64 v) { return QVariant::fromValue<NegativeInteger>({v}); };
+    QTest::newRow("negative1") << raw("\x20") << neg(1);
+    QTest::newRow("negative2") << raw("\x21") << neg(2);
+    QTest::newRow("negative24") << raw("\x37") << neg(24);
+    QTest::newRow("negative25") << raw("\x38\x18") << neg(25);
+    QTest::newRow("negativeUINT8_MAX") << raw("\x38\xff") << neg(256);
+    QTest::newRow("negativeUINT8_MAX-1") << raw("\x39\x01\x00") << neg(257);
+    QTest::newRow("negativeUINT16_MAX") << raw("\x39\xff\xff") << neg(65536);
+    QTest::newRow("negativeUINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << neg(65537);
+    QTest::newRow("negativeUINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << neg(Q_UINT64_C(4294967296));
+    QTest::newRow("negativeUINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << neg(Q_UINT64_C(4294967297));
+    QTest::newRow("negativeUINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe")
+                                        << neg(std::numeric_limits<quint64>::max());
+    QTest::newRow("negativeUINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") << neg(0);
 
     QTest::newRow("simple0") << raw("\xe0") << QVariant::fromValue(SimpleType{0});
     QTest::newRow("simple19") << raw("\xf3") << QVariant::fromValue(SimpleType{19});
diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp
index 28705c1..6dd4a35 100644
--- a/tests/parser/tst_parser.cpp
+++ b/tests/parser/tst_parser.cpp
@@ -43,6 +43,8 @@
     void initParserEmpty();
 
     // parsing API
+    void integers_data();
+    void integers();
     void fixed_data();
     void fixed();
     void strings_data();
@@ -322,6 +324,104 @@
 
 }
 
+static void addIntegers()
+{
+    QTest::addColumn<QByteArray>("data");
+    QTest::addColumn<quint64>("expectedRaw");
+    QTest::addColumn<qint64>("expectedValue");
+    QTest::addColumn<bool>("isNegative");
+    QTest::addColumn<bool>("inInt64Range");
+
+    // unsigned integers
+    QTest::newRow("0") << raw("\x00") << Q_UINT64_C(0) << Q_INT64_C(0) << false << true;
+    QTest::newRow("1") << raw("\x01") << Q_UINT64_C(1) << Q_INT64_C(1) << false << true;
+    QTest::newRow("10") << raw("\x0a") << Q_UINT64_C(10) << Q_INT64_C(10) << false << true;
+    QTest::newRow("23") << raw("\x17") << Q_UINT64_C(23) << Q_INT64_C(23) << false << true;
+    QTest::newRow("24") << raw("\x18\x18") << Q_UINT64_C(24) << Q_INT64_C(24) << false << true;
+    QTest::newRow("UINT8_MAX") << raw("\x18\xff") << Q_UINT64_C(255) << Q_INT64_C(255) << false << true;
+    QTest::newRow("UINT8_MAX+1") << raw("\x19\x01\x00") << Q_UINT64_C(256) << Q_INT64_C(256) << false << true;
+    QTest::newRow("UINT16_MAX") << raw("\x19\xff\xff") << Q_UINT64_C(65535) << Q_INT64_C(65535) << false << true;
+    QTest::newRow("UINT16_MAX+1") << raw("\x1a\0\1\x00\x00") << Q_UINT64_C(65536) << Q_INT64_C(65536) << false << true;
+    QTest::newRow("UINT32_MAX") << raw("\x1a\xff\xff\xff\xff") << Q_UINT64_C(4294967295) << Q_INT64_C(4294967295) << false << true;
+    QTest::newRow("UINT32_MAX+1") << raw("\x1b\0\0\0\1\0\0\0\0") << Q_UINT64_C(4294967296) << Q_INT64_C(4294967296) << false << true;
+    QTest::newRow("INT64_MAX") << raw("\x1b" "\x7f\xff\xff\xff" "\xff\xff\xff\xff")
+                                << quint64(std::numeric_limits<qint64>::max())
+                                << std::numeric_limits<qint64>::max() << false << true;
+    QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
+                                << std::numeric_limits<quint64>::max() << qint64(-123456) << false << false;
+
+    // negative integers
+    QTest::newRow("-1") << raw("\x20") << Q_UINT64_C(0) << Q_INT64_C(-1) << true << true;
+    QTest::newRow("-2") << raw("\x21") << Q_UINT64_C(1) << Q_INT64_C(-2) << true << true;
+    QTest::newRow("-24") << raw("\x37") << Q_UINT64_C(23) << Q_INT64_C(-24) << true << true;
+    QTest::newRow("-25") << raw("\x38\x18") << Q_UINT64_C(24) << Q_INT64_C(-25) << true << true;
+    QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << Q_UINT64_C(255) << Q_INT64_C(-256) << true << true;
+    QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << Q_UINT64_C(256) << Q_INT64_C(-257) << true << true;
+    QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << Q_UINT64_C(65535) << Q_INT64_C(-65536) << true << true;
+    QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << Q_UINT64_C(65536) << Q_INT64_C(-65537) << true << true;
+    QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << Q_UINT64_C(4294967295) << Q_INT64_C(-4294967296) << true << true;
+    QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << Q_UINT64_C(4294967296) << Q_INT64_C(-4294967297) << true << true;
+    QTest::newRow("INT64_MIN+1") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xfe")
+                               << quint64(std::numeric_limits<qint64>::max() - 1)
+                               << (std::numeric_limits<qint64>::min() + 1)
+                               << true << true;
+    QTest::newRow("INT64_MIN") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff")
+                               << quint64(std::numeric_limits<qint64>::max())
+                               << std::numeric_limits<qint64>::min()
+                               << true << true;
+    QTest::newRow("INT64_MIN-1") << raw("\x3b\x80\0\0\0""\0\0\0\0") << Q_UINT64_C(9223372036854775808) << qint64(-123456) << true << false;
+    QTest::newRow("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe")
+                                 << (std::numeric_limits<quint64>::max() - 1) << qint64(-123456) << true << false;
+    QTest::newRow("-UINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
+                                   << std::numeric_limits<quint64>::max() << qint64(-123456) << true << false;
+}
+
+void tst_Parser::integers_data()
+{
+    addIntegers();
+}
+
+void tst_Parser::integers()
+{
+    QFETCH(QByteArray, data);
+    QFETCH(bool, isNegative);
+    QFETCH(quint64, expectedRaw);
+    QFETCH(qint64, expectedValue);
+    QFETCH(bool, inInt64Range);
+
+    CborParser parser;
+    CborValue first;
+    CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
+    QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
+    QVERIFY(cbor_value_is_integer(&first));
+
+    uint64_t raw;
+    cbor_value_get_raw_integer(&first, &raw);
+    QCOMPARE(quint64(raw), expectedRaw);
+
+    if (isNegative) {
+        QVERIFY(cbor_value_is_negative_integer(&first));
+        QVERIFY(!cbor_value_is_unsigned_integer(&first));
+    } else {
+        QVERIFY(!cbor_value_is_negative_integer(&first));
+        QVERIFY(cbor_value_is_unsigned_integer(&first));
+    }
+
+    int64_t value;
+    if (inInt64Range) {
+        cbor_value_get_int64(&first, &value);
+        QCOMPARE(qint64(value), expectedValue);
+    }
+
+    err = cbor_value_get_int64_checked(&first, &value);
+    QCOMPARE(err, inInt64Range ? CborNoError : CborErrorDataTooLarge);
+
+    int ivalue;
+    bool inIntRange = inInt64Range && (expectedValue == int(expectedValue));
+    err = cbor_value_get_int_checked(&first, &ivalue);
+    QCOMPARE(err, inIntRange ? CborNoError : CborErrorDataTooLarge);
+}
+
 void tst_Parser::fixed_data()
 {
     addColumns();