blob: da2f91013b32178e13719d401e4f9d7988d261ec [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <lib/dnssd/minimal_mdns/core/QName.h>
#include <lib/support/UnitTestRegistration.h>
#include <nlunit-test.h>
namespace {
using namespace mdns::Minimal;
/// Convenience method to have a serialized QName:
///
/// static const uint8_t kData[] = "datahere\00";
/// AsSerializedQName(kData);
///
/// NOTE: this MUST be using the string "" format to add an extra NULL
/// terminator that this method discards.
template <size_t N>
static SerializedQNameIterator AsSerializedQName(const uint8_t (&v)[N])
{
// NOTE: the -1 is because we format these items as STRINGS and that
// appends an extra NULL terminator
return SerializedQNameIterator(BytesRange(v, v + N - 1), v);
}
void IteratorTest(nlTestSuite * inSuite, void * inContext)
{
{
static const uint8_t kOneItem[] = "\04test\00";
SerializedQNameIterator it = AsSerializedQName(kOneItem);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "test") == 0);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, it.IsValid());
}
{
static const uint8_t kManyItems[] = "\04this\02is\01a\04test\00";
SerializedQNameIterator it = AsSerializedQName(kManyItems);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "this") == 0);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "is") == 0);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "a") == 0);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "test") == 0);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, it.IsValid());
}
{
static const uint8_t kPtrItems[] = "abc\02is\01a\04test\00\04this\xc0\03";
SerializedQNameIterator it(BytesRange(kPtrItems, kPtrItems + sizeof(kPtrItems)), kPtrItems + 14);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "this") == 0);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "is") == 0);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "a") == 0);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, strcmp(it.Value(), "test") == 0);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, it.IsValid());
}
}
void ErrorTest(nlTestSuite * inSuite, void * inContext)
{
{
// Truncated before the end
static const uint8_t kData[] = "\04test";
SerializedQNameIterator it = AsSerializedQName(kData);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
{
// Truncated before the end
static const uint8_t kData[] = "\02";
SerializedQNameIterator it = AsSerializedQName(kData);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
{
// Truncated before the end
static const uint8_t kData[] = "\xc0";
SerializedQNameIterator it = AsSerializedQName(kData);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
}
void InvalidReferencing(nlTestSuite * inSuite, void * inContext)
{
{
// Truncated before the end (but seemingly valid in case of error)
// does NOT use AsSerializedQName (because out of range)
static const uint8_t kData[] = "\00\xc0\x00";
SerializedQNameIterator it(BytesRange(kData, kData + 2), kData + 1);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
{
// Infinite recursion
static const uint8_t kData[] = "\03test\xc0\x00";
SerializedQNameIterator it = AsSerializedQName(kData);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
{
// Infinite recursion by referencing own element (inside the stream)
static const uint8_t kData[] = "\03test\xc0\x05";
SerializedQNameIterator it = AsSerializedQName(kData);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
{
// Infinite recursion by referencing own element at the start
static const uint8_t kData[] = "\xc0\x00";
SerializedQNameIterator it = AsSerializedQName(kData);
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
{
// Reference that goes forwad instead of backward
static const uint8_t kData[] = "\03test\xc0\x07";
SerializedQNameIterator it = AsSerializedQName(kData);
NL_TEST_ASSERT(inSuite, it.Next());
NL_TEST_ASSERT(inSuite, !it.Next());
NL_TEST_ASSERT(inSuite, !it.IsValid());
}
}
void Comparison(nlTestSuite * inSuite, void * inContext)
{
static const uint8_t kManyItems[] = "\04this\02is\01a\04test\00";
{
const QNamePart kTestName[] = { "this" };
NL_TEST_ASSERT(inSuite, AsSerializedQName(kManyItems) != FullQName(kTestName));
}
{
const QNamePart kTestName[] = { "this", "is" };
NL_TEST_ASSERT(inSuite, AsSerializedQName(kManyItems) != FullQName(kTestName));
}
{
const QNamePart kTestName[] = { "is", "a", "test" };
NL_TEST_ASSERT(inSuite, AsSerializedQName(kManyItems) != FullQName(kTestName));
}
{
const QNamePart kTestName[] = { "this", "is", "a", "test" };
NL_TEST_ASSERT(inSuite, AsSerializedQName(kManyItems) == FullQName(kTestName));
}
{
const QNamePart kTestName[] = { "this", "is", "a", "test", "suffix" };
NL_TEST_ASSERT(inSuite, AsSerializedQName(kManyItems) != FullQName(kTestName));
}
{
const QNamePart kTestName[] = { "prefix", "this", "is", "a", "test" };
NL_TEST_ASSERT(inSuite, AsSerializedQName(kManyItems) != FullQName(kTestName));
}
}
void CaseInsensitiveSerializedCompare(nlTestSuite * inSuite, void * inContext)
{
static const uint8_t kManyItems[] = "\04thIs\02iS\01a\04tEst\00";
{
const QNamePart kTestName[] = { "this", "is", "a", "test" };
NL_TEST_ASSERT(inSuite,
SerializedQNameIterator(BytesRange(kManyItems, kManyItems + sizeof(kManyItems)), kManyItems) ==
FullQName(kTestName));
}
{
const QNamePart kTestName[] = { "THIS", "IS", "A", "test" };
NL_TEST_ASSERT(inSuite,
SerializedQNameIterator(BytesRange(kManyItems, kManyItems + sizeof(kManyItems)), kManyItems) ==
FullQName(kTestName));
}
{
const QNamePart kTestName[] = { "THIS", "IS", "A", "TEST" };
NL_TEST_ASSERT(inSuite,
SerializedQNameIterator(BytesRange(kManyItems, kManyItems + sizeof(kManyItems)), kManyItems) ==
FullQName(kTestName));
}
}
void CaseInsensitiveFullQNameCompare(nlTestSuite * inSuite, void * inContext)
{
{
const QNamePart kName1[] = { "this", "is", "a", "test" };
const QNamePart kName2[] = { "this", "IS", "a", "TEST" };
NL_TEST_ASSERT(inSuite, FullQName(kName1) == FullQName(kName2));
}
{
const QNamePart kName1[] = { "THIS", "IS", "a", "tesT" };
const QNamePart kName2[] = { "this", "IS", "A", "TEst" };
NL_TEST_ASSERT(inSuite, FullQName(kName1) == FullQName(kName2));
}
{
const QNamePart kName1[] = { "THIS", "IS", "a", "test" };
const QNamePart kName2[] = { "this", "IS", "A", "NEST" };
NL_TEST_ASSERT(inSuite, FullQName(kName1) != FullQName(kName2));
}
{
const QNamePart kName1[] = { "THIS", "IS", "a" };
const QNamePart kName2[] = { "this", "IS", "A", "NEST" };
NL_TEST_ASSERT(inSuite, FullQName(kName1) != FullQName(kName2));
}
{
const QNamePart kName1[] = { "THIS", "IS", "a" };
const QNamePart kName2[] = { "this", "IS" };
NL_TEST_ASSERT(inSuite, FullQName(kName1) != FullQName(kName2));
}
{
const QNamePart kName[] = { "this" };
NL_TEST_ASSERT(inSuite, FullQName() != FullQName(kName));
NL_TEST_ASSERT(inSuite, FullQName(kName) != FullQName());
}
}
void SerializedCompare(nlTestSuite * inSuite, void * inContext)
{
static const uint8_t kThisIsATest1[] = "\04this\02is\01a\04test\00";
static const uint8_t kThisIsATest2[] = "\04ThIs\02is\01A\04tESt\00";
static const uint8_t kThisIsDifferent[] = "\04this\02is\09different\00";
static const uint8_t kThisIs[] = "\04this\02is";
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsATest1) == AsSerializedQName(kThisIsATest1));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsATest2) == AsSerializedQName(kThisIsATest2));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsATest1) == AsSerializedQName(kThisIsATest2));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsATest1) != AsSerializedQName(kThisIsDifferent));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsDifferent) != AsSerializedQName(kThisIsATest1));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsDifferent) != AsSerializedQName(kThisIs));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIs) != AsSerializedQName(kThisIsDifferent));
// These items have back references and are "this.is.a.test"
static const uint8_t kPtrItems[] = "\03abc\02is\01a\04test\00\04this\xc0\04";
SerializedQNameIterator thisIsATestPtr(BytesRange(kPtrItems, kPtrItems + sizeof(kPtrItems)), kPtrItems + 15);
NL_TEST_ASSERT(inSuite, thisIsATestPtr == AsSerializedQName(kThisIsATest1));
NL_TEST_ASSERT(inSuite, thisIsATestPtr == AsSerializedQName(kThisIsATest2));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsATest1) == thisIsATestPtr);
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIsATest2) == thisIsATestPtr);
NL_TEST_ASSERT(inSuite, thisIsATestPtr != AsSerializedQName(kThisIs));
NL_TEST_ASSERT(inSuite, AsSerializedQName(kThisIs) != thisIsATestPtr);
}
} // namespace
// clang-format off
static const nlTest sTests[] =
{
NL_TEST_DEF("IteratorTest", IteratorTest),
NL_TEST_DEF("ErrorTest", ErrorTest),
NL_TEST_DEF("Comparison", Comparison),
NL_TEST_DEF("CaseInsensitiveSerializedCompare", CaseInsensitiveSerializedCompare),
NL_TEST_DEF("CaseInsensitiveFullQNameCompare", CaseInsensitiveFullQNameCompare),
NL_TEST_DEF("SerializedCompare", SerializedCompare),
NL_TEST_DEF("InvalidReferencing", InvalidReferencing),
NL_TEST_SENTINEL()
};
// clang-format on
int TestQName()
{
// clang-format off
nlTestSuite theSuite =
{
"QName",
&sTests[0],
nullptr,
nullptr
};
// clang-format on
nlTestRunner(&theSuite, nullptr);
return (nlTestRunnerStats(&theSuite));
}
CHIP_REGISTER_TEST_SUITE(TestQName)