diff options
author | michele papalini <micpapal+fdio@cisco.com> | 2017-02-23 17:01:34 +0100 |
---|---|---|
committer | Michele Papalini <micpapal+fdio@cisco.com> | 2017-02-23 17:23:19 +0000 |
commit | c580a00aac271a524e5a75b35f4b91c174ed227b (patch) | |
tree | feddc15a9f1a4eb319d950f8d6330ac2b91e3d99 /metis/ccnx/forwarder/metis/processor/test | |
parent | 9b30fc10fb1cbebe651e5a107e8ca5b24de54675 (diff) |
Initial commit: sb-forwarder, metis.
Change-Id: I65ee3c851a6901929ef4417ad80d34bca0dce445
Signed-off-by: michele papalini <micpapal+fdio@cisco.com>
Diffstat (limited to 'metis/ccnx/forwarder/metis/processor/test')
12 files changed, 4709 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/processor/test/CMakeLists.txt b/metis/ccnx/forwarder/metis/processor/test/CMakeLists.txt new file mode 100644 index 00000000..aae53353 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/CMakeLists.txt @@ -0,0 +1,21 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_metis_FIB + test_metis_FibEntryList + test_metis_HashTableFunction + test_metis_FibEntry + test_metis_MatchingRulesTable + test_metis_MessageProcessor + test_metis_PIT + test_metis_PitEntry + test_metis_StandardPIT +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_ContentStore.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_ContentStore.c new file mode 100644 index 00000000..65c2db74 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_ContentStore.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_ContentStore.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h> +#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h> + +LONGBOW_TEST_RUNNER(metis_ContentStore) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_ContentStore) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_ContentStore) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ============================================================================ + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Create_ZeroCapacity); + + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Fetch_ByName); + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Fetch_ByNameAndKeyId); + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Fetch_ByNameAndObjectHash); + + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Fetch_Lru); + + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Save_ZeroCapacity); + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Save_CapacityLimit); + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Save_WithoutEviction); + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Save_WithEviction); + + LONGBOW_RUN_TEST_CASE(Global, metisContentStore_Save_DuplicateHash); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Create_Destroy) +{ + size_t objectCapacity = 10; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + metisLogger_Release(&logger); + + assertTrue(store->objectCapacity == objectCapacity, "Wrong capacity, expected %zu got %zu", objectCapacity, store->objectCapacity); + assertTrue(store->objectCount == 0, "Wrong initial count, expected %u got %zu", 0, store->objectCount); + metisContentStore_Destroy(&store); + + assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Memory imbalance after create/destroy, expected %u got %u", 0, parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Create_ZeroCapacity) +{ + size_t objectCapacity = 0; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + assertTrue(store->objectCapacity == objectCapacity, "Wrong capacity, expected %zu got %zu", objectCapacity, store->objectCapacity); + assertTrue(store->objectCount == 0, "Wrong initial count, expected %u got %zu", 0, store->objectCount); + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Fetch_ByName) +{ + size_t objectCapacity = 10; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + MetisMessage *object_1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + MetisMessage *object_2 = metisMessage_CreateFromArray(metisTestDataV0_SecondObject, sizeof(metisTestDataV0_SecondObject), 1, 2, logger); + + metisContentStore_Save(store, object_1); + metisContentStore_Save(store, object_2); + + MetisMessage *interestByName = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 3, 5, logger); + MetisMessage *testObject = metisContentStore_Fetch(store, interestByName); + assertNotNull(testObject, "Fetch did not find match when it should have"); + + // two objects with same name, 1st one will win + assertTrue(testObject == object_1, "Fetch returned wrong object, expecting %p got %p", (void *) object_1, (void *) testObject); + + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); + metisMessage_Release(&object_1); + metisMessage_Release(&object_2); + metisMessage_Release(&testObject); + metisMessage_Release(&interestByName); +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Fetch_ByNameAndKeyId) +{ + size_t objectCapacity = 10; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + MetisMessage *object_1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + MetisMessage *object_2 = metisMessage_CreateFromArray(metisTestDataV0_SecondObject, sizeof(metisTestDataV0_SecondObject), 1, 2, logger); + + metisContentStore_Save(store, object_1); + metisContentStore_Save(store, object_2); + + MetisMessage *interestByNameKeyId = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 3, 5, logger); + MetisMessage *testObject = metisContentStore_Fetch(store, interestByNameKeyId); + assertNotNull(testObject, "Fetch did not find match when it should have"); + + // two objects with same name, 1st one will win + assertTrue(testObject == object_1, "Fetch returned wrong object, expecting %p got %p", (void *) object_1, (void *) testObject); + + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); + metisMessage_Release(&object_1); + metisMessage_Release(&object_2); + metisMessage_Release(&testObject); + metisMessage_Release(&interestByNameKeyId); +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Fetch_ByNameAndObjectHash) +{ + size_t objectCapacity = 10; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + MetisMessage *object_1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + MetisMessage *object_2 = metisMessage_CreateFromArray(metisTestDataV0_SecondObject, sizeof(metisTestDataV0_SecondObject), 1, 2, logger); + + metisContentStore_Save(store, object_1); + metisContentStore_Save(store, object_2); + + MetisMessage *interestByNameObjectHash = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 3, 5, logger); + + // this should retrieve object_1 because that is the one whose + // content object hash matches the interest + MetisMessage *testObject = metisContentStore_Fetch(store, interestByNameObjectHash); + assertNotNull(testObject, "Fetch did not find match when it should have"); + + // two objects with same name, 1st one will win + assertTrue(testObject == object_1, "Fetch returned wrong object, expecting %p got %p", (void *) object_1, (void *) testObject); + + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); + metisMessage_Release(&object_1); + metisMessage_Release(&object_2); + metisMessage_Release(&testObject); + metisMessage_Release(&interestByNameObjectHash); +} + +/* + * Create an cache and access objects to make sure the LRU is evicting the right way + */ +LONGBOW_TEST_CASE(Global, metisContentStore_Fetch_Lru) +{ + const size_t objectCapacity = 2; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + + MetisMessage *object1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + MetisMessage *object2 = metisMessage_CreateFromArray(metisTestDataV0_object_with_othername, sizeof(metisTestDataV0_object_with_othername), 2, 2, logger); + + metisContentStore_Save(store, object1); + metisContentStore_Save(store, object2); + + // object 2 sould be at top of LRU (was saved last). Fetch object 1, then evict object 2. + + // interest_with_name will match the name in object1. + MetisMessage *interestByName = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 3, 5, logger); + MetisMessage *testObject = metisContentStore_Fetch(store, interestByName); + + assertTrue(testObject == object1, "Fetch returned wrong object, expecting %p got %p", (void *) object1, (void *) testObject); + + // objectcapacity = 2, so object 3 will evict bottom of LRU + MetisMessage *object3 = metisMessage_CreateFromArray(metisTestDataV0_SecondObject, sizeof(metisTestDataV0_SecondObject), 4, 2, logger); + metisContentStore_Save(store, object3); + + // object 2 should be evicted + MetisMessage *interestOtherName = metisMessage_CreateFromArray(metisTestDataV0_InterestWithOtherName, sizeof(metisTestDataV0_InterestWithOtherName), 5, 5, logger); + MetisMessage *testEvictedObject = metisContentStore_Fetch(store, interestOtherName); + assertNull(testEvictedObject, "object with othername should have been evicted"); + + // as final sanity check, make sure object 1 is still in the list + MetisMessage *testObject1Again = metisContentStore_Fetch(store, interestByName); + assertNotNull(testObject1Again, "Did not retrieve object1 from the content store"); + + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); + metisMessage_Release(&testObject1Again); + metisMessage_Release(&object1); + metisMessage_Release(&object2); + metisMessage_Release(&object3); + metisMessage_Release(&testObject); + metisMessage_Release(&interestByName); + metisMessage_Release(&interestOtherName); +} + + +LONGBOW_TEST_CASE(Global, metisContentStore_Save_WithoutEviction) +{ + size_t objectCapacity = 10; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + MetisMessage *object_1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + MetisMessage *object_2 = metisMessage_CreateFromArray(metisTestDataV0_SecondObject, sizeof(metisTestDataV0_SecondObject), 1, 2, logger); + + metisContentStore_Save(store, object_1); + metisContentStore_Save(store, object_2); + + assertTrue(store->stats.countAdds == 2, "Wrong countAdds, expected %u got %" PRIu64, 2, store->stats.countAdds); + assertTrue(store->stats.countLruEvictions == 0, "Wrong countLruEvictions, expected %u got %" PRIu64, 0, store->stats.countLruEvictions); + assertTrue(metisLruList_Length(store->lruList) == 2, "Wrong metisLruList_Length, expected %u got %zu", 2, metisLruList_Length(store->lruList)); + + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); + metisMessage_Release(&object_1); + metisMessage_Release(&object_2); +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Save_WithEviction) +{ + size_t objectCapacity = 1; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + MetisMessage *object_1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + MetisMessage *object_2 = metisMessage_CreateFromArray(metisTestDataV0_SecondObject, sizeof(metisTestDataV0_SecondObject), 1, 2, logger); + + metisContentStore_Save(store, object_1); + + assertTrue(store->objectCount == 1, "Wrong objectCount. Expected %u, got %zu", 1, store->objectCount); + + metisContentStore_Save(store, object_2); + + // Capacity is 1, so we should never grow bigger than that. + assertTrue(store->objectCount == 1, "Wrong objectCount. Expected %u, got %zu", 1, store->objectCount); + + assertTrue(store->stats.countAdds == 2, "Wrong countAdds, expected %u got %" PRIu64, 2, store->stats.countAdds); + assertTrue(store->stats.countLruEvictions == 1, "Wrong countLruEvictions, expected %u got %" PRIu64, 1, store->stats.countLruEvictions); + assertTrue(metisLruList_Length(store->lruList) == 1, "Wrong metisLruList_Length, expected %u got %zu", 1, metisLruList_Length(store->lruList)); + + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); + metisMessage_Release(&object_1); + metisMessage_Release(&object_2); +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Save_ZeroCapacity) +{ + size_t objectCapacity = 0; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(objectCapacity, logger); + + MetisMessage *object_1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + bool success = metisContentStore_Save(store, object_1); + assertFalse(success, "Should have returned failure with 0 capacity object store saving something"); + + metisLogger_Release(&logger); + metisMessage_Release(&object_1); + metisContentStore_Destroy(&store); +} + +static MetisMessage * +_createUniqueMetisMessage(int tweakNumber, uint8_t *template, size_t templateSize, int nameOffset) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + PARCBuffer *buffer = parcBuffer_Allocate(templateSize); + memcpy(parcBuffer_Overlay(buffer, 0), template, templateSize); // Copy the template to new memory + + // Tweak the encoded object's name so the name hash varies each time. + uint8_t *bufPtr = parcBuffer_Overlay(buffer, 0); + bufPtr[nameOffset] = 'a' + tweakNumber; + + MetisMessage *result = metisMessage_CreateFromArray(bufPtr, templateSize, 1, 2, logger); + metisLogger_Release(&logger); + parcBuffer_Release(&buffer); + + return result; +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Save_CapacityLimit) +{ + size_t storeCapacity = 5; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(storeCapacity, logger); + + for (int i = 1; i < storeCapacity * 2; i++) { + + int offsetOfNameInEncodedObject = metisTestDataV0_EncodedObject.offset + 4; + MetisMessage *object = _createUniqueMetisMessage(i, metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), offsetOfNameInEncodedObject); + + bool success = metisContentStore_Save(store, object); + + assertTrue(success, "Unexpectedly failed to add entry to ContentStore"); + + if (i < store->objectCapacity) { + assertTrue(store->objectCount == i, "Unexpected value for store->objectCount"); + } else { + assertTrue(store->objectCount == store->objectCapacity, "Unexpected value (%zu) for store->objectCount (%zu)", + store->objectCount, store->objectCapacity); + } + + if (success) { + metisMessage_Release(&object); + } + } + metisLogger_Release(&logger); + metisContentStore_Destroy(&store); +} + +LONGBOW_TEST_CASE(Global, metisContentStore_Save_DuplicateHash) +{ + size_t storeCapacity = 5; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisContentStore *store = metisContentStore_Create(storeCapacity, logger); + + MetisMessage *object_1 = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + bool success = metisContentStore_Save(store, object_1); + + for (int i = 0; i < 10; i++) { + MetisMessage *object_1_dup = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + success = metisContentStore_Save(store, object_1_dup); + + assertFalse(success, "Unexpectedly added duplicated entry to ContentStore"); + + assertTrue(store->objectCount == 1, "ObjectCount should be 1"); + + metisMessage_Release(&object_1_dup); + } + + metisLogger_Release(&logger); + metisMessage_Release(&object_1); + metisContentStore_Destroy(&store); +} + +// ============================================================================ + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, hashTableFunction_ContentStoreEntryDestroyer); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, hashTableFunction_ContentStoreEntryDestroyer) +{ + testUnimplemented("This test is unimplemented"); +} + +// ============================================================================ + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_ContentStore); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_FIB.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_FIB.c new file mode 100644 index 00000000..0d02501f --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_FIB.c @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_FIB.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h> + +LONGBOW_TEST_RUNNER(metis_FIB) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_FIB) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_FIB) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisFib_AddOrUpdate_Add); + LONGBOW_RUN_TEST_CASE(Global, metisFib_AddOrUpdate_Update); + LONGBOW_RUN_TEST_CASE(Global, metisFib_Create_Destroy); + + LONGBOW_RUN_TEST_CASE(Global, metisFib_Match_Exists); + LONGBOW_RUN_TEST_CASE(Global, metisFib_Match_NotExists); + LONGBOW_RUN_TEST_CASE(Global, metisFib_Match_ExcludeIngress); + + LONGBOW_RUN_TEST_CASE(Global, metisFib_Remove_NoEntry); + LONGBOW_RUN_TEST_CASE(Global, metisFib_Remove_ExistsNotLast); + LONGBOW_RUN_TEST_CASE(Global, metisFib_Remove_ExistsIsLast); + + LONGBOW_RUN_TEST_CASE(Global, metisFIB_Length); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisFib_AddOrUpdate_Add) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + metisLogger_Release(&logger); + + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + unsigned interfaceIndex = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + CPIRouteEntry *route = cpiRouteEntry_Create(ccnxName, interfaceIndex, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + + metisFIB_AddOrUpdate(fib, route, "random"); + size_t hashCodeTableLength = parcHashCodeTable_Length(fib->tableByName); + + MetisFibEntry *fibEntry = parcHashCodeTable_Get(fib->tableByName, tlvName); + size_t nexthopCount = metisFibEntry_NexthopCount(fibEntry); + + cpiRouteEntry_Destroy(&route); + metisTlvName_Release(&tlvName); + metisFIB_Destroy(&fib); + + assertTrue(hashCodeTableLength == 1, "Wrong hash table length, expected %u got %zu", 1, hashCodeTableLength); + assertTrue(nexthopCount == 1, "Wrong hash table length, expected %u got %zu", 1, nexthopCount); +} + +LONGBOW_TEST_CASE(Global, metisFib_AddOrUpdate_Update) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + metisLogger_Release(&logger); + + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + // ----- Add + CPIRouteEntry *route_1 = cpiRouteEntry_Create(ccnxName_Copy(ccnxName), interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, route_1, "random"); + + // ----- Update + unsigned interfaceIndex_2 = 33; + CPIRouteEntry *route_2 = cpiRouteEntry_Create(ccnxName_Copy(ccnxName), interfaceIndex_2, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, route_2, "random"); + + // ----- Measure + size_t hashCodeTableLength = parcHashCodeTable_Length(fib->tableByName); + MetisFibEntry *fibEntry = parcHashCodeTable_Get(fib->tableByName, tlvName); + size_t nexthopCount = metisFibEntry_NexthopCount(fibEntry); + + + cpiRouteEntry_Destroy(&route_1); + cpiRouteEntry_Destroy(&route_2); + ccnxName_Release(&ccnxName); + metisTlvName_Release(&tlvName); + metisFIB_Destroy(&fib); + + assertTrue(hashCodeTableLength == 1, "Wrong hash table length, expected %u got %zu", 1, hashCodeTableLength); + assertTrue(nexthopCount == 2, "Wrong hash table length, expected %u got %zu", 2, nexthopCount); +} + +LONGBOW_TEST_CASE(Global, metisFib_Create_Destroy) +{ + size_t beforeMemory = parcMemory_Outstanding(); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + metisLogger_Release(&logger); + + metisFIB_Destroy(&fib); + size_t afterMemory = parcMemory_Outstanding(); + + assertTrue(beforeMemory == afterMemory, "Memory imbalance on create/destroy: expected %zu got %zu", beforeMemory, afterMemory); +} + +/** + * Add /hello/ouch and lookup that name + */ +LONGBOW_TEST_CASE(Global, metisFib_Match_Exists) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch"); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + // ----- Add + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd, "random"); + + // ----- Match + MetisFibEntry *entry = metisFIB_Match(fib, interest); + + // ----- Measure + size_t nexthopsLength = metisNumberSet_Length(metisFibEntry_GetNexthops(entry)); + + // ----- Cleanup + cpiRouteEntry_Destroy(&routeAdd); + metisMessage_Release(&interest); + metisFIB_Destroy(&fib); + + // ----- Validate + assertTrue(nexthopsLength == 1, "Wrong nexthops length, expected %u got %zu", 1, nexthopsLength); +} + +/** + * Add /foo/bar to connection 10 + * Add /foo to connection 11 + * Forward an Interest /foo/bar/cat from connection 10. Should select 11. + */ +LONGBOW_TEST_CASE(Global, metisFib_Match_ExcludeIngress) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + + CCNxName *nameFoo = ccnxName_CreateFromCString("lci:/foo"); + CCNxName *nameFooBar = ccnxName_CreateFromCString("lci:/foo/bar"); + + uint8_t encodedInterest[] = { + 0x01, 0x00, 0x00, 37, // ver = 1, type = interest, length = 37 + 0xFF, 0x00, 0x00, 8, // hoplimit = 255, header length = 8 + // ------------------------ + 0x00, 0x01, 0x00, 25, // type = interest, length = 25 + // ------------------------ + 0x00, 0x00, 0x00, 21, // type = name, length = 21 + 0x00, 0x01, 0x00, 3, // type = name, length = 3 + 'f', 'o', 'o', + 0x00, 0x01, 0x00, 3, // type = name, length = 3 + 'b', 'a', 'r', + 0x00, 0x01, 0x00, 3, // type = name, length = 3 + 'c', 'a', 't', + }; + + MetisMessage *interest = metisMessage_CreateFromArray(encodedInterest, sizeof(encodedInterest), 10, 2, logger); + metisLogger_Release(&logger); + + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + CPIRouteEntry *routeAdd; + + // ----- Add long route to Interface 10 + routeAdd = cpiRouteEntry_Create(nameFooBar, 10, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd, "random"); + cpiRouteEntry_Destroy(&routeAdd); + + // ----- Add short route to Interface 11 + routeAdd = cpiRouteEntry_Create(nameFoo, 11, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd, "random"); + cpiRouteEntry_Destroy(&routeAdd); + + // ----- Match + MetisFibEntry *entry = metisFIB_Match(fib, interest); + + // ----- Measure + size_t nexthopsLength = metisNumberSet_Length(metisFibEntry_GetNexthops(entry)); + + // ----- Validate + assertTrue(nexthopsLength == 1, "Wrong nexthops length, expected %u got %zu", 1, nexthopsLength); + bool hasEgress = metisNumberSet_Contains(metisFibEntry_GetNexthops(entry), 11); + assertTrue(hasEgress, "Egress interface 11 not in nexthop set"); + + // ----- Cleanup + metisMessage_Release(&interest); + metisFIB_Destroy(&fib); +} + + +/** + * Add /hello/ouch and lookup /party/ouch + */ +LONGBOW_TEST_CASE(Global, metisFib_Match_NotExists) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch"); + + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithOtherName, sizeof(metisTestDataV0_InterestWithOtherName), 1, 2, logger); + metisLogger_Release(&logger); + + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + // ----- Add + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd, "random"); + + // ----- Match + MetisFibEntry *entry = metisFIB_Match(fib, interest); + + // ----- Measure + assertTrue(entry == NULL, "expected null"); + cpiRouteEntry_Destroy(&routeAdd); + metisMessage_Release(&interest); + metisFIB_Destroy(&fib); + + //size_t nexthopsLength = metisNumberSet_Length(metisFibEntry_GetNexthops(entry)); + + // ----- Cleanup + //cpiRouteEntry_Destroy(&routeAdd); + //metisMessage_Release(&interest); + //metisFibEntry_Release(&entry); + //metisFIB_Destroy(&fib); + + // ----- Validate + //assertTrue(nexthopsLength == 0, "Wrong nexthops length, expected %u got %zu", 0, nexthopsLength); +} + +/** + * Add /foo/bar and try to remove /baz + */ +LONGBOW_TEST_CASE(Global, metisFib_Remove_NoEntry) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + metisLogger_Release(&logger); + + CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/foo/bar"); + CCNxName *ccnxNameToRemove = ccnxName_CreateFromCString("lci:/baz"); + MetisTlvName *tlvNameToCheck = metisTlvName_CreateFromCCNxName(ccnxNameToAdd); + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + // ----- Add + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd, "random"); + + // ----- Remove + CPIRouteEntry *routeRemove = cpiRouteEntry_Create(ccnxNameToRemove, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_Remove(fib, routeRemove); + + // ----- Measure + size_t hashCodeTableLength = parcHashCodeTable_Length(fib->tableByName); + MetisFibEntry *fibEntry = parcHashCodeTable_Get(fib->tableByName, tlvNameToCheck); + size_t nexthopCount = metisFibEntry_NexthopCount(fibEntry); + + // ----- Cleanup + cpiRouteEntry_Destroy(&routeAdd); + cpiRouteEntry_Destroy(&routeRemove); + metisTlvName_Release(&tlvNameToCheck); + metisFIB_Destroy(&fib); + + // ----- Validate + assertTrue(hashCodeTableLength == 1, "Wrong hash table length, expected %u got %zu", 1, hashCodeTableLength); + assertTrue(nexthopCount == 1, "Wrong hash table length, expected %u got %zu", 1, nexthopCount); +} + +LONGBOW_TEST_CASE(Global, metisFib_Remove_ExistsNotLast) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + metisLogger_Release(&logger); + + CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/foo/bar"); + CCNxName *ccnxNameToRemove = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvNameToCheck = metisTlvName_CreateFromCCNxName(ccnxNameToAdd); + unsigned interfaceIndex_1 = 11; + unsigned interfaceIndex_2 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + // ----- Add two next hops + CPIRouteEntry *routeAdd1 = cpiRouteEntry_Create(ccnxName_Copy(ccnxNameToAdd), interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd1, "random"); + + CPIRouteEntry *routeAdd2 = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_2, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd2, "random"); + + // ----- Remove + CPIRouteEntry *routeRemove = cpiRouteEntry_Create(ccnxNameToRemove, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_Remove(fib, routeRemove); + + // ----- Measure + size_t hashCodeTableLength = parcHashCodeTable_Length(fib->tableByName); + MetisFibEntry *fibEntry = parcHashCodeTable_Get(fib->tableByName, tlvNameToCheck); + size_t nexthopCount = metisFibEntry_NexthopCount(fibEntry); + + // ----- Cleanup + cpiRouteEntry_Destroy(&routeAdd1); + cpiRouteEntry_Destroy(&routeAdd2); + cpiRouteEntry_Destroy(&routeRemove); + metisTlvName_Release(&tlvNameToCheck); + metisFIB_Destroy(&fib); + + // ----- Validate + assertTrue(hashCodeTableLength == 1, "Wrong hash table length, expected %u got %zu", 1, hashCodeTableLength); + assertTrue(nexthopCount == 1, "Wrong hash table length, expected %u got %zu", 1, nexthopCount); +} + +/** + * Remove the last nexthop for a route. should remove the route + */ +LONGBOW_TEST_CASE(Global, metisFib_Remove_ExistsIsLast) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + metisLogger_Release(&logger); + + CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/foo/bar"); + CCNxName *ccnxNameToRemove = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvNameToCheck = metisTlvName_CreateFromCCNxName(ccnxNameToAdd); + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + // ----- Add + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd, "random"); + + // ----- Remove + CPIRouteEntry *routeRemove = cpiRouteEntry_Create(ccnxNameToRemove, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_Remove(fib, routeRemove); + + // ----- Measure + size_t hashCodeTableLength = parcHashCodeTable_Length(fib->tableByName); + + // ----- Cleanup + cpiRouteEntry_Destroy(&routeAdd); + cpiRouteEntry_Destroy(&routeRemove); + metisTlvName_Release(&tlvNameToCheck); + metisFIB_Destroy(&fib); + + // ----- Validate + assertTrue(hashCodeTableLength == 0, "Wrong hash table length, expected %u got %zu", 0, hashCodeTableLength); +} + +LONGBOW_TEST_CASE(Global, metisFIB_Length) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + + // CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/%02=hello/%F0%00=ouch"); + CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch"); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + // ----- Add + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_1, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(fib, routeAdd, "random"); + + // ----- Measure + size_t tableLength = metisFIB_Length(fib); + + // ----- Cleanup + cpiRouteEntry_Destroy(&routeAdd); + metisMessage_Release(&interest); + metisFIB_Destroy(&fib); + + // ----- Validate + assertTrue(tableLength == 1, "Wrong table length, expected %u got %zu", 1, tableLength); +} + +// ==================================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _hashTableFunction_FibEntryDestroyer); + LONGBOW_RUN_TEST_CASE(Local, _hashTableFunction_TlvNameDestroyer); + LONGBOW_RUN_TEST_CASE(Local, _metisFIB_CreateFibEntry); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _hashTableFunction_FibEntryDestroyer) +{ + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + MetisFibEntry *fibEntry = metisFibEntry_Create(tlvName, "random"); + + _hashTableFunction_FibEntryDestroyer((void **) &fibEntry); + + metisTlvName_Release(&tlvName); + ccnxName_Release(&ccnxName); + assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance after hashTableFunction_TlvNameDestroyer: %u", parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Local, _hashTableFunction_TlvNameDestroyer) +{ + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + + _hashTableFunction_TlvNameDestroyer((void **) &tlvName); + ccnxName_Release(&ccnxName); + + assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance after hashTableFunction_TlvNameDestroyer: %u", parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Local, _metisFIB_CreateFibEntry) +{ + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisFIB *fib = metisFIB_Create(logger); + metisLogger_Release(&logger); + + _metisFIB_CreateFibEntry(fib, tlvName, "random"); + size_t hashCodeTableLength = parcHashCodeTable_Length(fib->tableByName); + + metisFIB_Destroy(&fib); + metisTlvName_Release(&tlvName); + ccnxName_Release(&ccnxName); + + assertTrue(hashCodeTableLength == 1, "Wrong hash table size, expected %u got %zu", 1, hashCodeTableLength); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_FIB); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_FibEntry.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_FibEntry.c new file mode 100644 index 00000000..19b46d47 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_FibEntry.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_FibEntry.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(metis_FibEntry) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_FibEntry) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_FibEntry) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisFibEntry_AddNexthop); + LONGBOW_RUN_TEST_CASE(Global, metisFibEntry_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, metisFibEntry_SetStrategy); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisFibEntry_AddNexthop) +{ + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + MetisFibEntry *fibEntry = metisFibEntry_Create(tlvName,"random"); + + CCNxName *ccnxName1 = ccnxName_CreateFromCString("lci:/foo/bar"); + CPIRouteEntry *cpiRouteEntry1 = cpiRouteEntry_Create(ccnxName1, 1, NULL, 0, 0, NULL, 1); + CCNxName *ccnxName2 = ccnxName_CreateFromCString("lci:/foo/bar"); + CPIRouteEntry *cpiRouteEntry2 = cpiRouteEntry_Create(ccnxName2, 2, NULL, 0, 0, NULL, 1); + metisFibEntry_AddNexthop(fibEntry, cpiRouteEntry1); + metisFibEntry_AddNexthop(fibEntry, cpiRouteEntry2); + + assertTrue(metisFibEntry_NexthopCount(fibEntry) == 2, "wrong nexthop length, expected %u got %zu", 2, metisFibEntry_NexthopCount(fibEntry)); + + cpiRouteEntry_Destroy(&cpiRouteEntry1); + cpiRouteEntry_Destroy(&cpiRouteEntry2); + metisFibEntry_Release(&fibEntry); + metisTlvName_Release(&tlvName); + ccnxName_Release(&ccnxName); +} + +LONGBOW_TEST_CASE(Global, metisFibEntry_Create_Destroy) +{ + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + + size_t beforeMemory = parcMemory_Outstanding(); + MetisFibEntry *fibEntry = metisFibEntry_Create(tlvName, "random"); + metisFibEntry_Release(&fibEntry); + size_t afterMemory = parcMemory_Outstanding(); + + metisTlvName_Release(&tlvName); + ccnxName_Release(&ccnxName); + + assertTrue(beforeMemory == afterMemory, "Memory imbalance on create/destroy: expected %zu got %zu", beforeMemory, afterMemory); +} + + +LONGBOW_TEST_CASE(Global, metisFibEntry_SetStrategy) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_FibEntry); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_FibEntryList.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_FibEntryList.c new file mode 100644 index 00000000..25de216b --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_FibEntryList.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_FibEntryList.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(metis_FibEntryList) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_FibEntryList) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_FibEntryList) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisFibEntryList_Append); + LONGBOW_RUN_TEST_CASE(Global, metisFibEntryList_Create); + LONGBOW_RUN_TEST_CASE(Global, metisFibEntryList_Destroy); + LONGBOW_RUN_TEST_CASE(Global, metisFibEntryList_Get); + LONGBOW_RUN_TEST_CASE(Global, metisFibEntryList_Length); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisFibEntryList_Append) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisFibEntryList_Create) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisFibEntryList_Destroy) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisFibEntryList_Get) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisFibEntryList_Length) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, metisFibEntryList_ListDestroyer); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, metisFibEntryList_ListDestroyer) +{ + testUnimplemented(""); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_FibEntryList); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_HashTableFunction.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_HashTableFunction.c new file mode 100644 index 00000000..8f61cfb0 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_HashTableFunction.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_HashTableFunction.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(metis_HashTableFunction) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_HashTableFunction) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_HashTableFunction) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_MessageNameAndKeyIdEquals); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_MessageNameAndKeyIdHashCode); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_MessageNameAndObjectHashEquals); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_MessageNameAndObjectHashHashCode); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_MessageNameEquals); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_MessageNameHashCode); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_TlvNameCompare); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_TlvNameEquals); + LONGBOW_RUN_TEST_CASE(Global, metisHashTableFunction_TlvNameHashCode); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_MessageNameAndKeyIdEquals) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_MessageNameAndKeyIdHashCode) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_MessageNameAndObjectHashEquals) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_MessageNameAndObjectHashHashCode) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_MessageNameEquals) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_MessageNameHashCode) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_TlvNameCompare) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_TlvNameEquals) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, metisHashTableFunction_TlvNameHashCode) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_HashTableFunction); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_MatchingRulesTable.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_MatchingRulesTable.c new file mode 100644 index 00000000..63e31dea --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_MatchingRulesTable.c @@ -0,0 +1,666 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_MatchingRulesTable.c" +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h> + +LONGBOW_TEST_RUNNER(metis_MatchingRulesTable) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(HashFunctions); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_MatchingRulesTable) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_MatchingRulesTable) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ============================================================================ + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_Create_Destroy); + + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_Add_ByName); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_Add_ByNameAndKeyId); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_Add_ByNameAndObjectHash); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_AddToAllTables); + + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_Get); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_RemoveFromBest); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_RemoveFromAll); + + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_NoMatch); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_1Table); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_2Tables); + LONGBOW_RUN_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_3Tables); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_Add_ByName) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + void *data = (void *) 0x01; + + metisMatchingRulesTable_AddToBestTable(rulesTable, interest, data); + size_t tableLength = parcHashCodeTable_Length(rulesTable->tableByName); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); + + assertTrue(tableLength == 1, "tableByName wrong length, expected %u got %zu", 1, tableLength); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_Add_ByNameAndKeyId) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 1, 1, logger); + metisLogger_Release(&logger); + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + void *data = (void *) 0x01; + + metisMatchingRulesTable_AddToBestTable(rulesTable, interest, data); + size_t tableLength = parcHashCodeTable_Length(rulesTable->tableByNameAndKeyId); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); + + assertTrue(tableLength == 1, "tableByNameAndKeyId wrong length, expected %u got %zu", 1, tableLength); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_Add_ByNameAndObjectHash) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + metisLogger_Release(&logger); + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + void *data = (void *) 0x01; + + metisMatchingRulesTable_AddToBestTable(rulesTable, interest, data); + size_t tableLength = parcHashCodeTable_Length(rulesTable->tableByNameAndObjectHash); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); + + assertTrue(tableLength == 1, "tableByNameAndObjectHash wrong length, expected %u got %zu", 1, tableLength); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_AddToAllTables) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + metisLogger_Release(&logger); + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + void *data = (void *) 0x01; + + metisMatchingRulesTable_AddToAllTables(rulesTable, interest, data); + size_t tableLength = parcHashCodeTable_Length(rulesTable->tableByNameAndObjectHash); + assertTrue(tableLength == 1, "tableToAllTables wrong length, expected %u got %zu", 1, tableLength); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); + +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_Create_Destroy) +{ + size_t baselineMemory = parcMemory_Outstanding(); + + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + metisMatchingRulesTable_Destroy(&rulesTable); + + assertTrue(parcMemory_Outstanding() == baselineMemory, "Memory imbalance on create/destroy: %u", parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_Get) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + metisLogger_Release(&logger); + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + void *data = (void *) 0x01; + + metisMatchingRulesTable_AddToBestTable(rulesTable, interest, data); + void *test = metisMatchingRulesTable_Get(rulesTable, interest); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); + + assertTrue(data == test, "metisMatchingRulesTable_Get returned wrong result, expected %p got %p", data, test); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_RemoveFromAll) +{ + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + void *data = (void *) 0x01; + + size_t before = parcHashCodeTable_Length(rulesTable->tableByName); + parcHashCodeTable_Add(rulesTable->tableByName, interest, data); + metisMatchingRulesTable_RemoveFromAll(rulesTable, interest); + size_t after = parcHashCodeTable_Length(rulesTable->tableByName); + + metisMessage_Release(&interest); + metisMatchingRulesTable_Destroy(&rulesTable); + + assertTrue(after == before, "Did not remove interest in HashCodeTable: before %zu after %zu", before, after); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_RemoveFromBest) +{ + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + void *data = (void *) 0x01; + + size_t before = parcHashCodeTable_Length(rulesTable->tableByName); + parcHashCodeTable_Add(rulesTable->tableByName, interest, data); + metisMatchingRulesTable_RemoveFromBest(rulesTable, interest); + size_t after = parcHashCodeTable_Length(rulesTable->tableByName); + + metisMessage_Release(&interest); + metisMatchingRulesTable_Destroy(&rulesTable); + + assertTrue(after == before, "Did not remove interest in HashCodeTable: before %zu after %zu", before, after); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_NoMatch) +{ + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 1, logger); + metisLogger_Release(&logger); + + PARCArrayList *list = metisMatchingRulesTable_GetUnion(rulesTable, object); + assertTrue(parcArrayList_Size(list) == 0, "Incorrect result length, expected %u got %zu", 0, parcArrayList_Size(list)); + + parcArrayList_Destroy(&list); + metisMessage_Release(&object); + metisMatchingRulesTable_Destroy(&rulesTable); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_1Table) +{ + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interestByName = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + void *data = (void *) 0x01; + + // add the interest to the table + metisMatchingRulesTable_AddToBestTable(rulesTable, interestByName, data); + + // now retrieve it with a matching content object + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 4, logger); + + PARCArrayList *list = metisMatchingRulesTable_GetUnion(rulesTable, object); + assertTrue(parcArrayList_Size(list) == 1, "Incorrect result length, expected %u got %zu", 1, parcArrayList_Size(list)); + + metisLogger_Release(&logger); + parcArrayList_Destroy(&list); + metisMessage_Release(&object); + metisMessage_Release(&interestByName); + metisMatchingRulesTable_Destroy(&rulesTable); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_2Tables) +{ + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interestByName = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *interestByNameAndKeyId = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 1, 2, logger); + void *data = (void *) 0x01; + + // add the interest to the tables + metisMatchingRulesTable_AddToBestTable(rulesTable, interestByName, data); + metisMatchingRulesTable_AddToBestTable(rulesTable, interestByNameAndKeyId, data); + + // now retrieve it with a matching content object + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 4, logger); + + PARCArrayList *list = metisMatchingRulesTable_GetUnion(rulesTable, object); + assertTrue(parcArrayList_Size(list) == 2, "Incorrect result length, expected %u got %zu", 2, parcArrayList_Size(list)); + + metisLogger_Release(&logger); + parcArrayList_Destroy(&list); + metisMessage_Release(&object); + metisMessage_Release(&interestByName); + metisMessage_Release(&interestByNameAndKeyId); + metisMatchingRulesTable_Destroy(&rulesTable); +} + +LONGBOW_TEST_CASE(Global, metisMatchingRulesTable_GetUnion_3Tables) +{ + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interestByName = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *interestByNameAndKeyId = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 1, 2, logger); + MetisMessage *interestByNameAndObjectHash = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 2, logger); + void *data = (void *) 0x01; + + // add the interest to the tables + assertTrue(metisMatchingRulesTable_AddToBestTable(rulesTable, interestByName, data), "Cannot add interestByName"); + assertTrue(metisMatchingRulesTable_AddToBestTable(rulesTable, interestByNameAndKeyId, data), "Cannot add interestByNameAndKeyId"); + assertTrue(metisMatchingRulesTable_AddToBestTable(rulesTable, interestByNameAndObjectHash, data), "Cannot add interestByNameAndObjectHash"); + + // now retrieve it with a matching content object + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 4, logger); + + PARCArrayList *list = metisMatchingRulesTable_GetUnion(rulesTable, object); + assertTrue(parcArrayList_Size(list) == 3, "Incorrect result length, expected %u got %zu", 3, parcArrayList_Size(list)); + + metisLogger_Release(&logger); + parcArrayList_Destroy(&list); + metisMessage_Release(&object); + metisMessage_Release(&interestByName); + metisMessage_Release(&interestByNameAndKeyId); + metisMessage_Release(&interestByNameAndObjectHash); + metisMatchingRulesTable_Destroy(&rulesTable); +} + +// ============================================================================ + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, metisMatchingRulesTable_GetTableForMessage_TableByNameAndObjectHash); + LONGBOW_RUN_TEST_CASE(Local, metisMatchingRulesTable_GetTableForMessage_TableByNameAndKeyId); + LONGBOW_RUN_TEST_CASE(Local, metisMatchingRulesTable_GetTableForMessage_TableByName); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * Use an interest with only a name, should select tableByName + */ +LONGBOW_TEST_CASE(Local, metisMatchingRulesTable_GetTableForMessage_TableByName) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCHashCodeTable *table = metisMatchingRulesTable_GetTableForMessage(rulesTable, interest); + + assertTrue(table == rulesTable->tableByName, + "Chose wrong table, expected TableByName, got %s", + (table == rulesTable->tableByNameAndKeyId) ? "tableByNameAndKeyId" : + (table == rulesTable->tableByNameAndObjectHash) ? "tableByNameAndObjectHash" : "unknown"); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); +} + +/** + * Use an interest with a name and keyid, should select tableByNameAndKeyId + */ +LONGBOW_TEST_CASE(Local, metisMatchingRulesTable_GetTableForMessage_TableByNameAndKeyId) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 1, 1, logger); + metisLogger_Release(&logger); + + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCHashCodeTable *table = metisMatchingRulesTable_GetTableForMessage(rulesTable, interest); + + assertTrue(table == rulesTable->tableByNameAndKeyId, + "Chose wrong table, expected TableByNameAndKeyId, got %s", + (table == rulesTable->tableByName) ? "tableByName" : + (table == rulesTable->tableByNameAndObjectHash) ? "tableByNameAndObjectHash" : "unknown"); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); +} + +/** + * Use an interest with a name and objecthash, should select tableByNameAndObjectHash + */ +LONGBOW_TEST_CASE(Local, metisMatchingRulesTable_GetTableForMessage_TableByNameAndObjectHash) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + metisLogger_Release(&logger); + + MetisMatchingRulesTable *rulesTable = metisMatchingRulesTable_Create(NULL); + PARCHashCodeTable *table = metisMatchingRulesTable_GetTableForMessage(rulesTable, interest); + + assertTrue(table == rulesTable->tableByNameAndObjectHash, + "Chose wrong table, expected TableByName, got %s", + (table == rulesTable->tableByNameAndKeyId) ? "tableByNameAndKeyId" : + (table == rulesTable->tableByName) ? "tableByName" : "unknown"); + + metisMatchingRulesTable_Destroy(&rulesTable); + metisMessage_Release(&interest); +} + +// ============================================================================ + +LONGBOW_TEST_FIXTURE(HashFunctions) +{ + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameAndKeyIdEquals_IsEqual); + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameAndKeyIdEquals_IsNotEqual); + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameAndKeyIdHashCode); + + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameAndObjectHashEquals_IsEqual); + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameAndObjectHashEquals_IsNotEqual); + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameAndObjectHashHashCode); + + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameEquals_IsEqual); + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameEquals_IsNotEqual); + LONGBOW_RUN_TEST_CASE(HashFunctions, hashTableFunction_NameHashCode); +} + +LONGBOW_TEST_FIXTURE_SETUP(HashFunctions) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(HashFunctions) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * Test an interest and content object that match on (Name, KeyId) + */ +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameAndKeyIdEquals_IsEqual) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *a = metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest, sizeof(metisTestDataV0_EncodedInterest), 1, 1, logger); + MetisMessage *b = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 1, logger); + metisLogger_Release(&logger); + + bool success = metisHashTableFunction_MessageNameAndKeyIdEquals(a, b); + + metisMessage_Release(&a); + metisMessage_Release(&b); + + assertTrue(success, "Two equal names and keyids did not compare equal"); +} + +/** + * Test two interests that do not match on (Name, KeyId) + */ +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameAndKeyIdEquals_IsNotEqual) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *a = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 1, 1, logger); + MetisMessage *b = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid2, sizeof(metisTestDataV0_InterestWithName_keyid2), 1, 1, logger); + metisLogger_Release(&logger); + + bool success = metisHashTableFunction_MessageNameAndKeyIdEquals(a, b); + + metisMessage_Release(&a); + metisMessage_Release(&b); + + assertFalse(success, "Two unequal names compared equal"); +} + +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameAndKeyIdHashCode) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 1, 1, logger); + metisLogger_Release(&logger); + + MetisTlvName *name = metisMessage_GetName(interest); + uint32_t name_hash = metisTlvName_HashCode(name); + uint32_t keyid_hash; + metisMessage_GetKeyIdHash(interest, &keyid_hash); + + HashCodeType truth_hash = parcHash32_Data_Cumulative(&keyid_hash, sizeof(keyid_hash), name_hash); + + // the function to test + HashCodeType test_hash = metisHashTableFunction_MessageNameAndKeyIdHashCode(interest); + + metisMessage_Release(&interest); + + assertTrue(test_hash == truth_hash, "Got wrong hash, expected %08"PRIX64 " got %08"PRIX64, truth_hash, test_hash); +} + +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameAndObjectHashEquals_IsEqual) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *a = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + MetisMessage *b = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 1, logger); + metisLogger_Release(&logger); + + bool success = metisHashTableFunction_MessageNameAndObjectHashEquals(a, b); + + metisMessage_Release(&a); + metisMessage_Release(&b); + + assertTrue(success, "Two equal names and hashes did not compare equal"); +} + +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameAndObjectHashEquals_IsNotEqual) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *a = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + MetisMessage *b = metisMessage_CreateFromArray(metisTestDataV0_SecondObject, sizeof(metisTestDataV0_SecondObject), 1, 1, logger); + metisLogger_Release(&logger); + + bool success = metisHashTableFunction_MessageNameAndObjectHashEquals(a, b); + + metisMessage_Release(&a); + metisMessage_Release(&b); + + assertFalse(success, "Two unequal names and hashes compared equal"); +} + +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameAndObjectHashHashCode) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + metisLogger_Release(&logger); + + MetisTlvName *name = metisMessage_GetName(interest); + uint32_t name_hash = metisTlvName_HashCode(name); + uint32_t object_hash; + metisMessage_GetContentObjectHashHash(interest, &object_hash); + + HashCodeType truth_hash = parcHash32_Data_Cumulative(&object_hash, sizeof(object_hash), name_hash); + + // the function we actually want to test + HashCodeType test_hash = (HashCodeType)metisHashTableFunction_MessageNameAndObjectHashHashCode(interest); + + metisMessage_Release(&interest); + + assertTrue(test_hash == truth_hash, "Got wrong hash, expected %08"PRIX64 " got %08"PRIX64 , truth_hash, test_hash); +} + +/** + * Takes two MetisMessage and compares their names for equality + */ +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameEquals_IsEqual) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *a = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + MetisMessage *b = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_keyid, sizeof(metisTestDataV0_InterestWithName_keyid), 1, 1, logger); + metisLogger_Release(&logger); + + bool success = metisHashTableFunction_MessageNameEquals(a, b); + + metisMessage_Release(&a); + metisMessage_Release(&b); + + assertTrue(success, "Two equal names did not compare equal"); +} + +/** + * test two interests with different names + */ +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameEquals_IsNotEqual) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *a = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + MetisMessage *b = metisMessage_CreateFromArray(metisTestDataV0_InterestWithOtherName, sizeof(metisTestDataV0_InterestWithOtherName), 1, 1, logger); + metisLogger_Release(&logger); + + bool success = metisHashTableFunction_MessageNameEquals(a, b); + + metisMessage_Release(&a); + metisMessage_Release(&b); + + assertFalse(success, "Two unequal names compared equal"); +} + +/** + * Used on a MetisMessage key type, should return the HashCode + * of the message's name + */ +LONGBOW_TEST_CASE(HashFunctions, hashTableFunction_NameHashCode) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + + MetisTlvName *name = metisMessage_GetName(interest); + HashCodeType truth_hash = (HashCodeType)metisTlvName_HashCode(name); + HashCodeType test_hash = metisHashTableFunction_MessageNameHashCode(interest); + + metisMessage_Release(&interest); + + assertTrue(test_hash == truth_hash, "Got wrong hash, expected %08"PRIX64 " got %08"PRIX64, truth_hash, test_hash); +} + +// ============================================================================ + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_MatchingRulesTable); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_MessageProcessor.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_MessageProcessor.c new file mode 100644 index 00000000..1da3ca94 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_MessageProcessor.c @@ -0,0 +1,1533 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_MessageProcessor.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h> +#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h> +#include "../../core/test/testrig_MetisIoOperations.h" + +// Include this so we can step the clock forward without waiting real time +#include "../../core/metis_Forwarder.c" + +#include "testrig_MockTap.h" + +// ========================================================================= + +LONGBOW_TEST_RUNNER(metis_MessageProcessor) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_MessageProcessor) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_MessageProcessor) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_AddTap); + + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_Receive_WithTap); + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_Receive_Interest_WithoutTap); + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_Receive_Object_WithoutTap); + + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_RemoveTap_RemoveCurrentTap); + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_RemoveTap_RemoveOtherTap); + + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_AddOrUpdateRoute); + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_RemoveRoute); + + LONGBOW_RUN_TEST_CASE(Global, metisMessageProcessor_SetContentStoreSize); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + memset(&testTap, 0, sizeof(testTap)); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_Create_Destroy) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + + uint32_t beforeBalance = parcMemory_Outstanding(); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + metisMessageProcessor_Destroy(&processor); + uint32_t afterBalance = parcMemory_Outstanding(); + + metisForwarder_Destroy(&metis); + assertTrue(beforeBalance == afterBalance, "Memory imbalance on create/destroy: before %u after %u", beforeBalance, afterBalance); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_AddTap) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + metisMessageProcessor_AddTap(processor, &testTapTemplate); + void *currentTap = processor->tap; + + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(currentTap == &testTapTemplate, "tap did not get set correctly, expected %p got %p", (void *) &testTapTemplate, currentTap); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_Receive_WithTap) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + testTap.callOnReceive = true; + metisMessageProcessor_AddTap(processor, &testTapTemplate); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 4, 5, logger); + + // now test the function and measure results + metisMessageProcessor_Receive(processor, interest); + + // cleanup + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(testTap.onReceiveCount == 1, "Incorrect testTap.onReceiveCount, expected %u got %u", 1, testTap.onReceiveCount); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_Receive_Interest_WithoutTap) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 4, 5, logger); + + // now test the function and measure results + uint32_t beforeCountReceived = processor->stats.countReceived; + uint32_t beforeCountInterestsReceived = processor->stats.countInterestsReceived; + metisMessageProcessor_Receive(processor, interest); + uint32_t afterCountInterestsReceived = processor->stats.countInterestsReceived; + uint32_t afterCountReceived = processor->stats.countReceived; + + // cleanup + // do not cleanup interest, metisMessageProcessor_Receive() takes ownership + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountReceived == beforeCountReceived + 1, + "Incorrect afterCountReceived, expected %u got %u", + beforeCountReceived + 1, + afterCountReceived); + + assertTrue(afterCountInterestsReceived == beforeCountInterestsReceived + 1, + "Incorrect afterCountInterestsReceived, expected %u got %u", + beforeCountInterestsReceived + 1, + afterCountInterestsReceived); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_Receive_Object_WithoutTap) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 4, 5, logger); + + + // now test the function and measure results + uint32_t beforeCountReceived = processor->stats.countReceived; + uint32_t beforeCountObjectsReceived = processor->stats.countObjectsReceived; + metisMessageProcessor_Receive(processor, object); + uint32_t afterCountObjectsReceived = processor->stats.countObjectsReceived; + uint32_t afterCountReceived = processor->stats.countReceived; + + // cleanup + // do not cleanup object, metisMessageProcessor_Receive() takes ownership + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountReceived == beforeCountReceived + 1, + "Incorrect afterCountReceived, expected %u got %u", + beforeCountReceived + 1, + afterCountReceived); + + assertTrue(afterCountObjectsReceived == beforeCountObjectsReceived + 1, + "Incorrect afterCountInterestsReceived, expected %u got %u", + afterCountObjectsReceived, + beforeCountObjectsReceived + 1); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_RemoveTap_RemoveCurrentTap) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + metisMessageProcessor_AddTap(processor, &testTapTemplate); + metisMessageProcessor_RemoveTap(processor, &testTapTemplate); + void *currentTap = processor->tap; + + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(currentTap == NULL, "tap did not get removed correctly, expected %p got %p", NULL, currentTap); +} + +/** + * If we remove a tap that is not currently set, should have no effect. + */ +LONGBOW_TEST_CASE(Global, metisMessageProcessor_RemoveTap_RemoveOtherTap) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + MetisTap otherTap; + + metisMessageProcessor_AddTap(processor, &testTapTemplate); + metisMessageProcessor_RemoveTap(processor, &otherTap); + void *currentTap = processor->tap; + + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(currentTap == &testTapTemplate, "tap incorrectly removed, expected %p got %p", (void *) &testTapTemplate, currentTap); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_AddOrUpdateRoute) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + unsigned interfaceIndex = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + CPIRouteEntry *route = cpiRouteEntry_Create(ccnxName, interfaceIndex, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + + metisMessageProcessor_AddOrUpdateRoute(processor, route); + + size_t hashCodeTableLength = metisFIB_Length(processor->fib); + + cpiRouteEntry_Destroy(&route); + metisTlvName_Release(&tlvName); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(hashCodeTableLength == 1, "Wrong hash table length, expected %u got %zu", 1, hashCodeTableLength); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_RemoveRoute) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/foo/bar"); + MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + unsigned interfaceIndex = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + + CPIRouteEntry *route = cpiRouteEntry_Create(ccnxName, interfaceIndex, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + + metisMessageProcessor_AddOrUpdateRoute(processor, route); + metisMessageProcessor_RemoveRoute(processor, route); + + size_t hashCodeTableLength = metisFIB_Length(processor->fib); + + cpiRouteEntry_Destroy(&route); + metisTlvName_Release(&tlvName); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(hashCodeTableLength == 0, "Wrong hash table length, expected %u got %zu", 0, hashCodeTableLength); +} + +LONGBOW_TEST_CASE(Global, metisMessageProcessor_SetContentStoreSize) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + + size_t newCapacity = 1234; + metisForwarder_SetContentObjectStoreSize(metis, newCapacity); + + MetisContentStoreInterface *storeImpl = metisMessageProcessor_GetContentObjectStore(metis->processor); + + size_t testCapacity = metisContentStoreInterface_GetObjectCapacity(storeImpl); + assertTrue(testCapacity == newCapacity, "Expected the new store capacity"); + + metisForwarder_Destroy(&metis); +} + +// =================================================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_Drop_TestTapNoDrop); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_Drop_TestTapWithDrop); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_Drop_Interest); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_Drop_Object); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_NoConnection); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_SendFails); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_SendInterest); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_SendObject); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_ZeroHopLimit_Remote); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_ZeroHopLimit_Local); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToNexthops); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardToNexthops_DontForwardToIngress); + + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV0_InPit); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV0_NotInPit); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV1_InPit); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV1_NotInPit); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InPit); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NotInPit); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InCache); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InCacheButExpired); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NotInCache); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InFib); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NotInFib); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NoHopLimit); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_AggregateInterestInPit_NewEntry); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_AggregateInterestInPit_ExistingEntry); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_SatisfyFromContentStore_IsInStore); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_SatisfyFromContentStore_IsNotInStore); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_SatisfyFromContentStore_WithKeyIdNotVerified_WithoutVerification); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardViaFib_IsNotInFib); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_ForwardViaFib_IsInFib_EmptyEgressSet); + + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_NoHopLimit); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Local_Zero); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Local_NonZero); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Remote_Zero); + LONGBOW_RUN_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Remote_NonZero); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + memset(&testTap, 0, sizeof(testTap)); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * Test that the tap does not fire if testTap.callOnDrop is false + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_Drop_TestTapNoDrop) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + + testTap.callOnDrop = false; + metisMessageProcessor_AddTap(processor, &testTapTemplate); + + // should not increment a counter + metisMessageProcessor_Drop(processor, interest); + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(testTap.onDropCount == 0, "Incorrect onDropCount, expecting %u, got %u", 0, testTap.onDropCount); +} + +/** + * Test that the tap does fire if testTap.callOnDrop is true + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_Drop_TestTapWithDrop) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + + testTap.callOnDrop = true; + metisMessageProcessor_AddTap(processor, &testTapTemplate); + + // should increment a counter + metisMessageProcessor_Drop(processor, interest); + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(testTap.onDropCount == 1, "Incorrect onDropCount, expecting %u, got %u", 1, testTap.onDropCount); +} + +/** + * Test that when we drop an interest it is counted + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_Drop_Interest) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + + // should increment a counter + metisMessageProcessor_Drop(processor, interest); + + unsigned countDropped = processor->stats.countDropped; + unsigned countInterestsDropped = processor->stats.countInterestsDropped; + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(countInterestsDropped == 1, "Incorrect countInterestsDropped, expecting %u, got %u", 1, countInterestsDropped); + assertTrue(countDropped == 1, "Incorrect countDropped, expecting %u, got %u", 1, countDropped); +} + +/** + * Test that when we drop an object it is counted + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_Drop_Object) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + + // should increment a counter + metisMessageProcessor_Drop(processor, object); + + unsigned countDropped = processor->stats.countDropped; + unsigned countObjectsDropped = processor->stats.countObjectsDropped; + + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(countObjectsDropped == 1, "Incorrect countInterestsDropped, expecting %u, got %u", 1, countObjectsDropped); + assertTrue(countDropped == 1, "Incorrect countDropped, expecting %u, got %u", 1, countDropped); +} + +/** + * Send a message to a connection that does not exist in the connection table + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_NoConnection) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + + metisMessageProcessor_ForwardToInterfaceId(processor, object, 99); + + uint32_t countDroppedConnectionNotFound = processor->stats.countDroppedConnectionNotFound; + uint32_t countObjectsDropped = processor->stats.countObjectsDropped; + + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(countDroppedConnectionNotFound == 1, "Incorrect countDroppedConnectionNotFound, expecting %u, got %u", 1, countDroppedConnectionNotFound); + assertTrue(countObjectsDropped == 1, "Incorrect countDropped, expecting %u, got %u", 1, countObjectsDropped); +} + +/** + * Send to a connection that is down + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_SendFails) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 99, false, false, false); + MetisConnection *conn = metisConnection_Create(ops); + + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn); + metisMessageProcessor_ForwardToInterfaceId(processor, object, 99); + + uint32_t countSendFailures = processor->stats.countSendFailures; + uint32_t countObjectsDropped = processor->stats.countObjectsDropped; + + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ops); + + assertTrue(countSendFailures == 1, "Incorrect countSendFailures, expecting %u, got %u", 1, countSendFailures); + assertTrue(countObjectsDropped == 1, "Incorrect countDropped, expecting %u, got %u", 1, countObjectsDropped); +} + +/** + * Send an interest out a good connection + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_SendInterest) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest, sizeof(metisTestDataV0_EncodedInterest), 1, 2, logger); + + MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 99, true, true, false); + MockIoOperationsData *data = metisIoOperations_GetClosure(ops); + MetisConnection *conn = metisConnection_Create(ops); + + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn); + metisMessageProcessor_ForwardToInterfaceId(processor, object, 99); + + uint32_t countInterestForwarded = processor->stats.countInterestForwarded; + uint32_t sendCount = data->sendCount; + + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ops); + + assertTrue(countInterestForwarded == 1, "Incorrect countInterestForwarded, expecting %u, got %u", 1, countInterestForwarded); + assertTrue(sendCount == 1, "Incorrect sendCount, expecting %u, got %u", 1, sendCount); +} + +/** + * Send a content object out a good connection + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_SendObject) +{ + // setup + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 99, true, true, false); + MockIoOperationsData *data = metisIoOperations_GetClosure(ops); + MetisConnection *conn = metisConnection_Create(ops); + + // test + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn); + metisMessageProcessor_ForwardToInterfaceId(processor, object, 99); + + // measure + uint32_t countObjectsForwarded = processor->stats.countObjectsForwarded; + uint32_t sendCount = data->sendCount; + + // cleanup + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ops); + + // validate + assertTrue(countObjectsForwarded == 1, "Incorrect countObjectsForwarded, expecting %u, got %u", 1, countObjectsForwarded); + assertTrue(sendCount == 1, "Incorrect sendCount, expecting %u, got %u", 1, sendCount); +} + +/* + * Try to forward an interest with a 0 hop limit to a remote. Should fail + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_ZeroHopLimit_Remote) +{ + // setup + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest_zero_hoplimit, sizeof(metisTestDataV0_EncodedInterest_zero_hoplimit), 1, 2, logger); + + + unsigned connId = 99; + bool isLocal = false; + MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, connId, true, true, isLocal); + MockIoOperationsData *data = metisIoOperations_GetClosure(ops); + MetisConnection *conn = metisConnection_Create(ops); + + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn); + metisMessageProcessor_ForwardToInterfaceId(processor, object, connId); + + // measure + uint32_t countDropZeroToRemote = processor->stats.countDroppedZeroHopLimitToRemote; + uint32_t sendCount = data->sendCount; + + // cleanup + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ops); + + // validate + assertTrue(countDropZeroToRemote == 1, "Incorrect countDropZeroToRemote, expecting %u, got %u", 1, countDropZeroToRemote); + assertTrue(sendCount == 0, "Incorrect sendCount, expecting %u, got %u", 0, sendCount); +} + +/* + * Try to forward an interest with a 0 hop limit to a local. Should succeed. + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToInterfaceId_ZeroHopLimit_Local) +{ + // setup + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest_zero_hoplimit, sizeof(metisTestDataV0_EncodedInterest_zero_hoplimit), 1, 2, logger); + + + unsigned connId = 99; + bool isLocal = true; + MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, connId, true, true, isLocal); + MockIoOperationsData *data = metisIoOperations_GetClosure(ops); + MetisConnection *conn = metisConnection_Create(ops); + + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn); + metisMessageProcessor_ForwardToInterfaceId(processor, object, connId); + + // measure + uint32_t countDropZeroToRemote = processor->stats.countDroppedZeroHopLimitToRemote; + uint32_t sendCount = data->sendCount; + + // cleanup + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ops); + + // validate + assertTrue(countDropZeroToRemote == 0, "Incorrect countDropZeroToRemote, expecting %u, got %u", 0, countDropZeroToRemote); + assertTrue(sendCount == 1, "Incorrect sendCount, expecting %u, got %u", 1, sendCount); +} + + +/** + * Create 2 connections, and try to forwarder to both of them + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToNexthops) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + + // 2 connections + MetisIoOperations *ops_42 = mockIoOperationsData_CreateSimple(1, 2, 42, true, true, false); + MockIoOperationsData *data_42 = metisIoOperations_GetClosure(ops_42); + MetisConnection *conn_42 = metisConnection_Create(ops_42); + + MetisIoOperations *ops_43 = mockIoOperationsData_CreateSimple(1, 2, 43, true, true, false); + MockIoOperationsData *data_43 = metisIoOperations_GetClosure(ops_43); + MetisConnection *conn_43 = metisConnection_Create(ops_43); + + // Add the connections to the connection table + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn_42); + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn_43); + + // Setup the next hops + MetisNumberSet *nexthops = metisNumberSet_Create(); + metisNumberSet_Add(nexthops, 42); + metisNumberSet_Add(nexthops, 43); + + // forward the content object to both of them + metisMessageProcessor_ForwardToNexthops(processor, object, nexthops); + + // there should be 2 object forwards and each IoOps should have gotten 1 send + uint32_t countObjectsForwarded = processor->stats.countObjectsForwarded; + uint32_t sendCount_42 = data_42->sendCount; + uint32_t sendCount_43 = data_43->sendCount; + + // cleanup + metisNumberSet_Release(&nexthops); + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ops_42); + mockIoOperationsData_Destroy(&ops_43); + + // validate + assertTrue(countObjectsForwarded == 2, "Incorrect countObjectsForwarded, expecting %u, got %u", 2, countObjectsForwarded); + assertTrue(sendCount_42 == 1, "Incorrect sendCount_42, expecting %u, got %u", 1, sendCount_42); + assertTrue(sendCount_43 == 1, "Incorrect sendCount_43, expecting %u, got %u", 1, sendCount_43); +} + +/** + * There is a route in the FIB that points to the ingress interface of an interest. + * Ensure that we don't forward to that interface + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardToNexthops_DontForwardToIngress) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + // ingress interface is #42, so it should not get forwarded out there + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 42, 1, logger); + + + // 2 connections + MetisIoOperations *ops_42 = mockIoOperationsData_CreateSimple(1, 2, 42, true, true, false); + MockIoOperationsData *data_42 = metisIoOperations_GetClosure(ops_42); + MetisConnection *conn_42 = metisConnection_Create(ops_42); + + MetisIoOperations *ops_43 = mockIoOperationsData_CreateSimple(1, 2, 43, true, true, false); + MockIoOperationsData *data_43 = metisIoOperations_GetClosure(ops_43); + MetisConnection *conn_43 = metisConnection_Create(ops_43); + + // Add the connections to the connection table + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn_42); + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn_43); + + // Setup the next hops + MetisNumberSet *nexthops = metisNumberSet_Create(); + metisNumberSet_Add(nexthops, 42); + metisNumberSet_Add(nexthops, 43); + + // forward the content object to both of them + metisMessageProcessor_ForwardToNexthops(processor, object, nexthops); + + // there should be 2 object forwards and each IoOps should have gotten 1 send + uint32_t countObjectsForwarded = processor->stats.countObjectsForwarded; + uint32_t sendCount_42 = data_42->sendCount; + uint32_t sendCount_43 = data_43->sendCount; + + // cleanup + metisNumberSet_Release(&nexthops); + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ops_42); + mockIoOperationsData_Destroy(&ops_43); + + // validate + assertTrue(countObjectsForwarded == 1, "Incorrect countObjectsForwarded, expecting %u, got %u", 1, countObjectsForwarded); + assertTrue(sendCount_42 == 0, "Incorrect sendCount_42, expecting %u, got %u", 0, sendCount_42); + assertTrue(sendCount_43 == 1, "Incorrect sendCount_43, expecting %u, got %u", 1, sendCount_43); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV0_InPit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 3, 4, logger); + + + // receive the interst to add it to PIT + metisMessageProcessor_ReceiveInterest(processor, interest); + + // now test the function and measure results + // There is no actual connection "1" (the interest ingress port), so the forwarding + // will show up as a countDroppedConnectionNotFound. + + uint32_t beforeCountDroppedConnectionNotFound = processor->stats.countDroppedConnectionNotFound; + metisMessageProcessor_ReceiveContentObject(processor, object); + uint32_t afterCountDroppedConnectionNotFound = processor->stats.countDroppedConnectionNotFound; + + // cleanup + metisMessage_Release(&interest); + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountDroppedConnectionNotFound == beforeCountDroppedConnectionNotFound + 1, + "Incorrect afterCountDroppedConnectionNotFound, expected %u got %u", + beforeCountDroppedConnectionNotFound + 1, + afterCountDroppedConnectionNotFound); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV0_NotInPit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + + // now test the function and measure results + uint32_t beforeCountDroppedNoReversePath = processor->stats.countDroppedNoReversePath; + metisMessageProcessor_ReceiveContentObject(processor, object); + uint32_t afterCountDroppedNoReversePath = processor->stats.countDroppedNoReversePath; + + // cleanup + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountDroppedNoReversePath == beforeCountDroppedNoReversePath + 1, + "Incorrect afterCountDroppedNoReversePath, expected %u got %u", + beforeCountDroppedNoReversePath + 1, + afterCountDroppedNoReversePath); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV1_InPit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + MetisLogger *logger = metisForwarder_GetLogger(metis); +// metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug); +// metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV1_Interest_NameA_Crc32c, sizeof(metisTestDataV1_Interest_NameA_Crc32c), 1, 2, logger); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV1_ContentObject_NameA_Crc32c, sizeof(metisTestDataV1_ContentObject_NameA_Crc32c), 3, 4, logger); + + // receive the interst to add it to PIT + metisMessageProcessor_ReceiveInterest(processor, interest); + + // now test the function and measure results + // There is no actual connection "1" (the interest ingress port), so the forwarding + // will show up as a countDroppedConnectionNotFound. + + uint32_t beforeCountDroppedConnectionNotFound = processor->stats.countDroppedConnectionNotFound; + metisMessageProcessor_ReceiveContentObject(processor, object); + uint32_t afterCountDroppedConnectionNotFound = processor->stats.countDroppedConnectionNotFound; + + // cleanup + metisMessage_Release(&interest); + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountDroppedConnectionNotFound == beforeCountDroppedConnectionNotFound + 1, + "Incorrect afterCountDroppedConnectionNotFound, expected %u got %u", + beforeCountDroppedConnectionNotFound + 1, + afterCountDroppedConnectionNotFound); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveContentObjectV1_NotInPit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 2, logger); + + + // now test the function and measure results + uint32_t beforeCountDroppedNoReversePath = processor->stats.countDroppedNoReversePath; + metisMessageProcessor_ReceiveContentObject(processor, object); + uint32_t afterCountDroppedNoReversePath = processor->stats.countDroppedNoReversePath; + + // cleanup + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountDroppedNoReversePath == beforeCountDroppedNoReversePath + 1, + "Incorrect afterCountDroppedNoReversePath, expected %u got %u", + beforeCountDroppedNoReversePath + 1, + afterCountDroppedNoReversePath); +} + + +/** + * There's already a detailed test for this, we just check the stats counter + * to make sure the right logic flow is executed. The second interest must come from + * a different reverse path to be aggregated. + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InPit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest1 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *interest2 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 2, 2, logger); + + + // add it once + metisMessageProcessor_AggregateInterestInPit(processor, interest1); + + // now test the function and measure results + uint32_t beforeCountInterestsAggregated = processor->stats.countInterestsAggregated; + metisMessageProcessor_ReceiveInterest(processor, interest2); + uint32_t afterCountInterestsAggregated = processor->stats.countInterestsAggregated; + + // cleanup + metisMessage_Release(&interest1); + metisMessage_Release(&interest2); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountInterestsAggregated == beforeCountInterestsAggregated + 1, + "Incorrect afterCountInterestsAggregated, expected %u got %u", + beforeCountInterestsAggregated + 1, + afterCountInterestsAggregated); +} + +/** + * There's already a detailed test for this, we just check the stats counter + * to make sure the right logic flow is executed + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NotInPit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + + // now test the function and measure results + uint32_t beforeCountInterestsAggregated = processor->stats.countInterestsAggregated; + metisMessageProcessor_ReceiveInterest(processor, interest); + uint32_t afterCountInterestsAggregated = processor->stats.countInterestsAggregated; + + // also check its in the PIT now + MetisPitEntry *pitEntry = metisPIT_GetPitEntry(processor->pit, interest); + bool foundInPit = false; + if (pitEntry) { + foundInPit = true; + } + + // cleanup + metisPitEntry_Release(&pitEntry); + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountInterestsAggregated == beforeCountInterestsAggregated, + "Incorrect afterCountInterestsAggregated, expected %u got %u", + beforeCountInterestsAggregated, + afterCountInterestsAggregated); + + assertTrue(foundInPit, "Did not find interest in the PIT"); +} + +/** + * There's already a detailed test for this, we just check the stats counter + * to make sure the right logic flow is executed + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InCache) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 4, 5, logger); + + // add it to the cache + metisContentStoreInterface_PutContent(processor->contentStore, object, 0l); + + // now test the function and measure results + uint32_t beforeCountObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + metisMessageProcessor_ReceiveInterest(processor, interest); + uint32_t afterCountObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + + // cleanup + metisMessage_Release(&object); + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountObjectsForwardedFromStore == beforeCountObjectsForwardedFromStore + 1, + "Incorrect afterCountObjectsForwardedFromStore, expected %u got %u", + beforeCountObjectsForwardedFromStore + 1, + afterCountObjectsForwardedFromStore); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InCacheButExpired) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + + uint64_t currentTimeInTicks = metisForwarder_GetTicks(processor->metis); + + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, + sizeof(metisTestDataV0_InterestWithName), 1, currentTimeInTicks, logger); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, + sizeof(metisTestDataV0_EncodedObject), 4, currentTimeInTicks, logger); + + // add it to the cache. It's already expired, so should not be forwarded. + metisMessage_SetExpiryTimeTicks(object, currentTimeInTicks + 1000ULL); + metisContentStoreInterface_PutContent(processor->contentStore, object, currentTimeInTicks); + + // Crank metis clock. + metis->clockOffset = metisForwarder_NanosToTicks(5000000000ULL); // Add 5 seconds. Content is now expired. + + // now test the function and measure results. + uint32_t beforeCountObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + metisMessageProcessor_ReceiveInterest(processor, interest); + uint32_t afterCountObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + + // cleanup + metisMessage_Release(&object); + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate. Nothing should have been forwarded. + assertTrue(afterCountObjectsForwardedFromStore == beforeCountObjectsForwardedFromStore, + "Incorrect afterCountObjectsForwardedFromStore, expected %u got %u", + beforeCountObjectsForwardedFromStore, + afterCountObjectsForwardedFromStore); +} + + +/** + * There's already a detailed test for this, we just check the stats counter + * to make sure the right logic flow is executed + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NotInCache) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 4, 5, logger); + + + // now test the function and measure results + uint32_t beforeCountObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + metisMessageProcessor_ReceiveInterest(processor, interest); + uint32_t afterCountObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + + // cleanup + metisMessage_Release(&object); + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountObjectsForwardedFromStore == beforeCountObjectsForwardedFromStore, + "Incorrect afterCountObjectsForwardedFromStore, expected %u got %u", + beforeCountObjectsForwardedFromStore, + afterCountObjectsForwardedFromStore); +} + +/** + * There's already a detailed test for this, we just check the stats counter + * to make sure the right logic flow is executed + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_InFib) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + + // ----- Add Route + CCNxName *ccnxNameToAdd = + ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch"); + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, + interfaceIndex_1, + nexthop, + cpiNameRouteProtocolType_STATIC, + cpiNameRouteType_LONGEST_MATCH, + lifetime, + cost); + metisFIB_AddOrUpdate(processor->fib, routeAdd, "random"); + + // now test the function and measure results + // We will see it in countDroppedConnectionNotFound, because we didnt mock up the interface 22 connection + uint32_t beforeCountDroppedConnectionNotFound = processor->stats.countDroppedConnectionNotFound; + metisMessageProcessor_ReceiveInterest(processor, interest); + uint32_t afterCountDroppedConnectionNotFound = processor->stats.countDroppedConnectionNotFound; + + // cleanup + cpiRouteEntry_Destroy(&routeAdd); + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(afterCountDroppedConnectionNotFound == beforeCountDroppedConnectionNotFound + 1, + "Incorrect afterCountDroppedConnectionNotFound, expected %u got %u", + beforeCountDroppedConnectionNotFound + 1, + afterCountDroppedConnectionNotFound); +} + +/** + * There's already a detailed test for this, we just check the stats counter + * to make sure the right logic flow is executed + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NotInFib) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ReceiveInterest_NoHopLimit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest_no_hoplimit, + sizeof(metisTestDataV0_EncodedInterest_no_hoplimit), 1, 2, logger); + + + metisMessageProcessor_ReceiveInterest(processor, interest); + uint32_t dropCount = processor->stats.countDroppedNoHopLimit; + + // cleanup + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(dropCount == 1, + "Incorrect countDroppedNoHopLimit, expected %u got %u", + 1, + dropCount); +} + +/** + * Add an interest to the PIT when it does not exist. Should not increment the "stats.countInterestsAggregated' counter + * and should return FALSE, meaning not aggregated + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_AggregateInterestInPit_NewEntry) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + + uint32_t beforeCountInterestsAggregated = processor->stats.countInterestsAggregated; + bool aggregated = metisMessageProcessor_AggregateInterestInPit(processor, interest); + uint32_t afterCountInterestsAggregated = processor->stats.countInterestsAggregated; + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(afterCountInterestsAggregated == beforeCountInterestsAggregated, + "Incorrect afterCountInterestsAggregated, expected %u got %u", + beforeCountInterestsAggregated, + afterCountInterestsAggregated); + assertFalse(aggregated, "Interest aggregated when no interests in table!"); +} + +/** + * Add an interest to the PIT, then add it again. SHould increment the "stats.countInterestsAggregated" counter and + * should return TRUE meaning, it was aggregated. The second interest needs to come from a different interface. + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_AggregateInterestInPit_ExistingEntry) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest1 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *interest2 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 2, 2, logger); + + + // add it once + metisMessageProcessor_AggregateInterestInPit(processor, interest1); + + // now add it again + uint32_t beforeCountInterestsAggregated = processor->stats.countInterestsAggregated; + bool aggregated = metisMessageProcessor_AggregateInterestInPit(processor, interest2); + uint32_t afterCountInterestsAggregated = processor->stats.countInterestsAggregated; + + metisMessage_Release(&interest1); + metisMessage_Release(&interest2); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + assertTrue(afterCountInterestsAggregated == beforeCountInterestsAggregated + 1, + "Incorrect afterCountInterestsAggregated, expected %u got %u", + beforeCountInterestsAggregated + 1, + afterCountInterestsAggregated); + assertTrue(aggregated, "Interest not aggregated with self!"); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_SatisfyFromContentStore_WithKeyIdNotVerified_WithoutVerification) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + + MetisMessage *contentObjectWithKeyId = + metisMessage_CreateFromArray(metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256, + sizeof(metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256), 4, 5, logger); + + // add it to the cache + metisContentStoreInterface_PutContent(processor->contentStore, contentObjectWithKeyId, 1l); + + // Now create an Interest with the same name and a KeyId. + MetisMessage *interestWithKeyIdRestriction = + metisMessage_CreateFromArray(metisTestDataV1_Interest_NameAAndKeyId, + sizeof(metisTestDataV1_Interest_NameAAndKeyId), 4, 5, logger); + + // Now test the code. We should NOT match it, due to the content store not currently verifying keyIds. + bool success = _satisfyFromContentStore(processor, interestWithKeyIdRestriction); + unsigned countObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + + // cleanup + metisMessage_Release(&interestWithKeyIdRestriction); + metisMessage_Release(&contentObjectWithKeyId); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertFalse(success, "Expected Interest to not be satisfied from cache!"); + assertTrue(countObjectsForwardedFromStore == 0, + "Incorrect countObjectsForwardedFromStore, expected %u got %u", + 0, + countObjectsForwardedFromStore); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_SatisfyFromContentStore_IsInStore) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 4, 5, logger); + + // add it to the cache + metisContentStoreInterface_PutContent(processor->contentStore, object, 1l); + + // now test the code + bool success = _satisfyFromContentStore(processor, interest); + unsigned countObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + + // cleanup + metisMessage_Release(&interest); + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertTrue(success, "Interest not satisfied from cache!"); + assertTrue(countObjectsForwardedFromStore == 1, + "Incorrect countObjectsForwardedFromStore, expected %u got %u", + 1, + countObjectsForwardedFromStore); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_SatisfyFromContentStore_IsNotInStore) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + MetisMessage *object = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 4, 5, logger); + + + // don't add it to the cache + + // now test the code + bool success = _satisfyFromContentStore(processor, interest); + unsigned countObjectsForwardedFromStore = processor->stats.countInterestsSatisfiedFromStore; + + // cleanup + metisMessage_Release(&interest); + metisMessage_Release(&object); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // validate + assertFalse(success, "Interest satisfied from cache, when we didn't put it there!"); + assertTrue(countObjectsForwardedFromStore == 0, + "Incorrect countObjectsForwardedFromStore, expected %u got %u", + 0, + countObjectsForwardedFromStore); +} + +/** + * Add fib entry /hello/ouch and ask for /party/ouch + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardViaFib_IsNotInFib) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + // ----- Add + CCNxName *ccnxNameToAdd = + ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch"); + + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_1, nexthop, + cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(processor->fib, routeAdd,"random" ); + + // ----- Measure + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithOtherName, sizeof(metisTestDataV0_InterestWithOtherName), 1, 2, logger); + + + bool success = metisMessageProcessor_ForwardViaFib(processor, interest); + + // ----- Cleanup + cpiRouteEntry_Destroy(&routeAdd); + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // ----- Validate + assertFalse(success, "Returned true even though no route"); +} + +/** + * Forward to an existing FIB entry. The PIT entry has an empty egress set. + */ +LONGBOW_TEST_CASE(Local, metisMessageProcessor_ForwardViaFib_IsInFib_EmptyEgressSet) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + // ----- Add Route + CCNxName *ccnxNameToAdd = + ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch"); + unsigned interfaceIndex_1 = 22; + CPIAddress *nexthop = NULL; + struct timeval *lifetime = NULL; + unsigned cost = 12; + CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, interfaceIndex_1, nexthop, + cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetime, cost); + metisFIB_AddOrUpdate(processor->fib, routeAdd, "random"); + + // ----- Add PIT entry + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + + metisPIT_ReceiveInterest(processor->pit, interest); + + // ----- Measure + bool success = metisMessageProcessor_ForwardViaFib(processor, interest); + + // ----- Cleanup + cpiRouteEntry_Destroy(&routeAdd); + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + metisForwarder_Destroy(&metis); + + // ----- Validate + assertTrue(success, "Returned false with existing PIT entry"); +} + +MetisConnection * +setupMockConnection(MetisForwarder *metis, bool isLocal) +{ + MetisIoOperations *ops = mockIoOperationsData_CreateSimple(1, 2, 99, false, false, isLocal); + MetisConnection *conn = metisConnection_Create(ops); + metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis), conn); + + return conn; +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_NoHopLimit) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + bool isLocal = false; + MetisConnection *conn = setupMockConnection(metis, isLocal); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = + metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest_no_hoplimit, + sizeof(metisTestDataV0_EncodedInterest_no_hoplimit), metisConnection_GetConnectionId(conn), 2, logger); + + + bool success = metisMessageProcessor_CheckAndDecrementHopLimitOnIngress(processor, interest); + assertFalse(success, "Should have failed for an interest without hoplimit"); + + assertTrue(processor->stats.countDroppedNoHopLimit == 1, + "Wrong countDroppedNoHopLimit, got %u expected %u", processor->stats.countDroppedNoHopLimit, 1); + assertTrue(processor->stats.countDroppedZeroHopLimitFromRemote == 0, + "Wrong countDroppedZeroHopLimitFromRemote, got %u expected %u", processor->stats.countDroppedZeroHopLimitFromRemote, 0); + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + MetisIoOperations *ioOps = metisConnection_GetIoOperations(conn); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ioOps); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Local_Zero) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + bool isLocal = true; + MetisConnection *conn = setupMockConnection(metis, isLocal); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = + metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest_zero_hoplimit, + sizeof(metisTestDataV0_EncodedInterest_zero_hoplimit), metisConnection_GetConnectionId(conn), 2, logger); + + + bool success = metisMessageProcessor_CheckAndDecrementHopLimitOnIngress(processor, interest); + assertTrue(success, "Local with 0 hoplimit should have been ok"); + assertTrue(processor->stats.countDroppedNoHopLimit == 0, + "Wrong countDroppedNoHopLimit, got %u expected %u", processor->stats.countDroppedNoHopLimit, 0); + assertTrue(processor->stats.countDroppedZeroHopLimitFromRemote == 0, + "Wrong countDroppedZeroHopLimitFromRemote, got %u expected %u", processor->stats.countDroppedZeroHopLimitFromRemote, 0); + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + MetisIoOperations *ioOps = metisConnection_GetIoOperations(conn); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ioOps); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Local_NonZero) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + bool isLocal = true; + MetisConnection *conn = setupMockConnection(metis, isLocal); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = + metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest, + sizeof(metisTestDataV0_EncodedInterest), metisConnection_GetConnectionId(conn), 2, logger); + + + bool success = metisMessageProcessor_CheckAndDecrementHopLimitOnIngress(processor, interest); + assertTrue(success, "Local with non-0 hoplimit should have been ok"); + assertTrue(processor->stats.countDroppedNoHopLimit == 0, + "Wrong countDroppedNoHopLimit, got %u expected %u", processor->stats.countDroppedNoHopLimit, 0); + assertTrue(processor->stats.countDroppedZeroHopLimitFromRemote == 0, + "Wrong countDroppedZeroHopLimitFromRemote, got %u expected %u", processor->stats.countDroppedZeroHopLimitFromRemote, 0); + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + MetisIoOperations *ioOps = metisConnection_GetIoOperations(conn); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ioOps); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Remote_Zero) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + bool isLocal = false; + MetisConnection *conn = setupMockConnection(metis, isLocal); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = + metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest_zero_hoplimit, + sizeof(metisTestDataV0_EncodedInterest_zero_hoplimit), metisConnection_GetConnectionId(conn), 2, logger); + + + bool success = metisMessageProcessor_CheckAndDecrementHopLimitOnIngress(processor, interest); + assertFalse(success, "Remote with 0 hoplimit should have been failure"); + assertTrue(processor->stats.countDroppedNoHopLimit == 0, + "Wrong countDroppedNoHopLimit, got %u expected %u", processor->stats.countDroppedNoHopLimit, 0); + assertTrue(processor->stats.countDroppedZeroHopLimitFromRemote == 1, + "Wrong countDroppedZeroHopLimitFromRemote, got %u expected %u", processor->stats.countDroppedZeroHopLimitFromRemote, 1); + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + MetisIoOperations *ioOps = metisConnection_GetIoOperations(conn); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ioOps); +} + +LONGBOW_TEST_CASE(Local, metisMessageProcessor_CheckAndDecrementHopLimitOnIngress_Remote_NonZero) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisMessageProcessor *processor = metisMessageProcessor_Create(metis); + + bool isLocal = false; + MetisConnection *conn = setupMockConnection(metis, isLocal); + MetisLogger *logger = metisForwarder_GetLogger(metis); + MetisMessage *interest = + metisMessage_CreateFromArray(metisTestDataV0_EncodedInterest, sizeof(metisTestDataV0_EncodedInterest), metisConnection_GetConnectionId(conn), 2, logger); + + + bool success = metisMessageProcessor_CheckAndDecrementHopLimitOnIngress(processor, interest); + assertTrue(success, "Remote with non-0 hoplimit should have been ok"); + assertTrue(processor->stats.countDroppedNoHopLimit == 0, + "Wrong countDroppedNoHopLimit, got %u expected %u", processor->stats.countDroppedNoHopLimit, 0); + assertTrue(processor->stats.countDroppedZeroHopLimitFromRemote == 0, + "Wrong countDroppedZeroHopLimitFromRemote, got %u expected %u", processor->stats.countDroppedZeroHopLimitFromRemote, 0); + + metisMessage_Release(&interest); + metisMessageProcessor_Destroy(&processor); + MetisIoOperations *ioOps = metisConnection_GetIoOperations(conn); + metisForwarder_Destroy(&metis); + mockIoOperationsData_Destroy(&ioOps); +} + + +// ======================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_MessageProcessor); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_PIT.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_PIT.c new file mode 100644 index 00000000..90403402 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_PIT.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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. + */ + + +/* + * These tests were written before MetisMatchRulesTable was broken out of the PIT. + * So, many of the tests "cheat" by looking directly in a constiuent table in MetisMatchingRulesTable. + * They should be re-written to use the MetisMatchingRulesTable API. + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_PIT.c" + + + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <parc/logging/parc_LogReporterTextStdout.h> + +// =============================================================================================== +// Mock PIT +// These functions just count calls. The Destroy interface does not actually release memeory, you +// need to call _metisPIT_Release() yourself -- note that this is a static function with leading "_". + +typedef struct mock_pit { + unsigned countRelease; + unsigned countReceiveInterest; + unsigned countSatisfyInterest; + unsigned countRemoveInterest; + unsigned countGetPitEntry; +} _MockPIT; + +static void +_mockPITInterface_Release(MetisPIT **pitPtr) +{ + _MockPIT *mock = metisPIT_Closure(*pitPtr); + mock->countRelease++; + *pitPtr = NULL; +} + +static MetisPITVerdict +_mockPITInterface_ReceiveInterest(MetisPIT *pit, MetisMessage *interestMessage) +{ + _MockPIT *mock = metisPIT_Closure(pit); + mock->countReceiveInterest++; + return MetisPITVerdict_Aggregate; +} + +static MetisNumberSet * +_mockPITInterface_SatisfyInterest(MetisPIT *pit, const MetisMessage *objectMessage) +{ + _MockPIT *mock = metisPIT_Closure(pit); + mock->countSatisfyInterest++; + return NULL; +} + +static void +_mockPITInterface_RemoveInterest(MetisPIT *pit, const MetisMessage *interestMessage) +{ + _MockPIT *mock = metisPIT_Closure(pit); + mock->countRemoveInterest++; +} + +static MetisPitEntry * +_mockPITInterface_GetPitEntry(const MetisPIT *pit, const MetisMessage *interestMessage) +{ + _MockPIT *mock = metisPIT_Closure(pit); + mock->countGetPitEntry++; + return NULL; +} + +static MetisPIT * +_mockPIT_Create(void) +{ + size_t allocation = sizeof(MetisPIT) + sizeof(_MockPIT); + MetisPIT *pit = parcMemory_AllocateAndClear(allocation); + + pit->getPitEntry = _mockPITInterface_GetPitEntry; + pit->receiveInterest = _mockPITInterface_ReceiveInterest; + pit->release = _mockPITInterface_Release; + pit->removeInterest = _mockPITInterface_RemoveInterest; + pit->satisfyInterest = _mockPITInterface_SatisfyInterest; + + pit->closure = (uint8_t *) pit + sizeof(MetisPIT); + return pit; +} + +static void +_metisPIT_Release(MetisPIT **pitPtr) +{ + parcMemory_Deallocate(pitPtr); +} + + +// =============================================================================================== + +LONGBOW_TEST_RUNNER(metis_PIT) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_PIT) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_PIT) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// =============================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisPIT_Closure); + LONGBOW_RUN_TEST_CASE(Global, metisPIT_Release); + LONGBOW_RUN_TEST_CASE(Global, metisPIT_ReceiveInterest); + LONGBOW_RUN_TEST_CASE(Global, metisPIT_SatisfyInterest); + LONGBOW_RUN_TEST_CASE(Global, metisPIT_RemoveInterest); + LONGBOW_RUN_TEST_CASE(Global, metisPIT_GetPitEntry); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisPIT_Closure) +{ + MetisPIT *pit = _mockPIT_Create(); + _MockPIT *mock = metisPIT_Closure(pit); + assertTrue(mock == pit->closure, "Wrong pointer expected %p got %p", pit->closure, mock); + _metisPIT_Release(&pit); +} + +LONGBOW_TEST_CASE(Global, metisPIT_Release) +{ + MetisPIT *pit = _mockPIT_Create(); + MetisPIT *original = pit; + _MockPIT *mock = metisPIT_Closure(pit); + metisPIT_Release(&pit); + + assertTrue(mock->countRelease == 1, "Wrong count expected 1 got %u", mock->countRelease); + _metisPIT_Release(&original); +} + +LONGBOW_TEST_CASE(Global, metisPIT_ReceiveInterest) +{ + MetisPIT *pit = _mockPIT_Create(); + _MockPIT *mock = metisPIT_Closure(pit); + metisPIT_ReceiveInterest(pit, NULL); + + assertTrue(mock->countReceiveInterest == 1, "Wrong count expected 1 got %u", mock->countReceiveInterest); + _metisPIT_Release(&pit); +} + +LONGBOW_TEST_CASE(Global, metisPIT_SatisfyInterest) +{ + MetisPIT *pit = _mockPIT_Create(); + _MockPIT *mock = metisPIT_Closure(pit); + metisPIT_SatisfyInterest(pit, NULL); + + assertTrue(mock->countSatisfyInterest == 1, "Wrong count expected 1 got %u", mock->countSatisfyInterest); + _metisPIT_Release(&pit); +} + +LONGBOW_TEST_CASE(Global, metisPIT_RemoveInterest) +{ + MetisPIT *pit = _mockPIT_Create(); + _MockPIT *mock = metisPIT_Closure(pit); + metisPIT_RemoveInterest(pit, NULL); + + assertTrue(mock->countRemoveInterest == 1, "Wrong count expected 1 got %u", mock->countRemoveInterest); + _metisPIT_Release(&pit); +} + +LONGBOW_TEST_CASE(Global, metisPIT_GetPitEntry) +{ + MetisPIT *pit = _mockPIT_Create(); + _MockPIT *mock = metisPIT_Closure(pit); + metisPIT_GetPitEntry(pit, NULL); + + assertTrue(mock->countGetPitEntry == 1, "Wrong count expected 1 got %u", mock->countGetPitEntry); + _metisPIT_Release(&pit); +} + +// =============================================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_PIT); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_PitEntry.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_PitEntry.c new file mode 100644 index 00000000..2ad4b3da --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_PitEntry.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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 the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_PitEntry.c" +#include <LongBow/unit-test.h> + +#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h> + +#define __STDC_FORMAT_MACROS +#include <stdint.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +LONGBOW_TEST_RUNNER(metis_PitEntry) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_PitEntry) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_PitEntry) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ===================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_AddEgressId); + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_AddIngressId); + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_Copy); + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_GetExpiryTime); + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_SetExpiryTime); + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_GetIngressSet); + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_GetEgressSet); + + LONGBOW_RUN_TEST_CASE(Global, metisPitEntry_GetMessage); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_AddEgressId) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), 10000, 0); + + metisPitEntry_AddEgressId(entry, 10); + metisPitEntry_AddEgressId(entry, 11); + + size_t set_length = metisNumberSet_Length(entry->egressIdSet); + bool contains_10 = metisNumberSet_Contains(entry->egressIdSet, 10); + bool contains_11 = metisNumberSet_Contains(entry->egressIdSet, 11); + + metisPitEntry_Release(&entry); + metisMessage_Release(&interest); + + assertTrue(set_length == 2, "Wrong set length, expected %u got %zu", 2, set_length); + assertTrue(contains_10, "Set did not contain 10"); + assertTrue(contains_11, "Set did not contain 11"); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_AddIngressId) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), 10000, 0); + + metisPitEntry_AddIngressId(entry, 10); + metisPitEntry_AddIngressId(entry, 11); + + size_t set_length = metisNumberSet_Length(entry->ingressIdSet); + + // #1 is from the original interest + bool contains_1 = metisNumberSet_Contains(entry->ingressIdSet, 1); + bool contains_10 = metisNumberSet_Contains(entry->ingressIdSet, 10); + bool contains_11 = metisNumberSet_Contains(entry->ingressIdSet, 11); + + metisPitEntry_Release(&entry); + metisMessage_Release(&interest); + + assertTrue(set_length == 3, "Wrong set length, expected %u got %zu", 2, set_length); + assertTrue(contains_1, "Set did not contain 1"); + assertTrue(contains_10, "Set did not contain 10"); + assertTrue(contains_11, "Set did not contain 11"); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_Copy) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), 40000, 0); + unsigned refCountBeforeCopy = entry->refcount; + + MetisPitEntry *copy = metisPitEntry_Acquire(entry); + unsigned refCountAfterCopy = entry->refcount; + + metisPitEntry_Release(&entry); + unsigned refCountAfterDestroy = copy->refcount; + metisPitEntry_Release(©); + metisMessage_Release(&interest); + + assertTrue(refCountAfterCopy == refCountBeforeCopy + 1, "Refcount after copy not 1 larger: expected %u got %u", refCountBeforeCopy + 1, refCountAfterCopy); + assertTrue(refCountAfterDestroy == refCountBeforeCopy, "Refcount after destroy not same as before copy: expected %u got %u", refCountBeforeCopy, refCountAfterDestroy); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_Create_Destroy) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + size_t baselineMemory = parcMemory_Outstanding(); + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), 40000, 0); + metisPitEntry_Release(&entry); + size_t testMemory = parcMemory_Outstanding(); + + metisMessage_Release(&interest); + + assertTrue(testMemory == baselineMemory, "Memory imbalance on create/destroy: expected %zu got %zu", baselineMemory, testMemory); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_GetExpiryTime) +{ + MetisTicks expiry = 40000; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), expiry, 0); + + MetisTicks test = metisPitEntry_GetExpiryTime(entry); + + metisPitEntry_Release(&entry); + metisMessage_Release(&interest); + + assertTrue(expiry == test, "Got wrong expiry time, expected %" PRIu64 ", got %" PRIu64, expiry, test); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_SetExpiryTime) +{ + MetisTicks expiry = 40000; + MetisTicks expiry2 = 80000; + + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), expiry, 0); + + metisPitEntry_SetExpiryTime(entry, expiry2); + + MetisTicks test = metisPitEntry_GetExpiryTime(entry); + + metisPitEntry_Release(&entry); + metisMessage_Release(&interest); + + assertTrue(expiry2 == test, "Got wrong expiry time, expected %" PRIu64 ", got %" PRIu64, expiry2, test); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_GetIngressSet) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), 10000, 0); + + unsigned truth_set[] = { 1, 2, 3, 4, 0 }; + + MetisNumberSet *truth = metisNumberSet_Create(); + + for (int i = 0; truth_set[i] != 0; i++) { + metisNumberSet_Add(truth, truth_set[i]); + metisPitEntry_AddIngressId(entry, truth_set[i]); + } + + const MetisNumberSet *ingressSet = metisPitEntry_GetIngressSet(entry); + bool equals = metisNumberSet_Equals(truth, ingressSet); + + metisPitEntry_Release(&entry); + metisMessage_Release(&interest); + metisNumberSet_Release(&truth); + + assertTrue(equals, "Number set returned by metisPitEntry_GetIngressSet did not equal truth set"); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_GetEgressSet) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), 10000, 0); + + unsigned truth_set[] = { 1, 2, 3, 4, 0 }; + + MetisNumberSet *truth = metisNumberSet_Create(); + + for (int i = 0; truth_set[i] != 0; i++) { + metisNumberSet_Add(truth, truth_set[i]); + metisPitEntry_AddEgressId(entry, truth_set[i]); + } + + const MetisNumberSet *egressSet = metisPitEntry_GetEgressSet(entry); + bool equals = metisNumberSet_Equals(truth, egressSet); + + metisPitEntry_Release(&entry); + metisMessage_Release(&interest); + metisNumberSet_Release(&truth); + + assertTrue(equals, "Number set returned by metisPitEntry_GetIngressSet did not equal truth set"); +} + +LONGBOW_TEST_CASE(Global, metisPitEntry_GetMessage) +{ + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + metisLogger_Release(&logger); + + MetisPitEntry *entry = metisPitEntry_Create(metisMessage_Acquire(interest), 10000, 0); + MetisMessage *copy = metisPitEntry_GetMessage(entry); + + assertTrue(copy == interest, "Returned message not equal, expected %p got %p", (void *) interest, (void *) entry); + + metisPitEntry_Release(&entry); + metisMessage_Release(©); + metisMessage_Release(&interest); +} + +// ===================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_PitEntry); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/test_metis_StandardPIT.c b/metis/ccnx/forwarder/metis/processor/test/test_metis_StandardPIT.c new file mode 100644 index 00000000..b04a3964 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/test_metis_StandardPIT.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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. + */ + + +/* + * These tests were written before MetisMatchRulesTable was broken out of the PIT. + * So, many of the tests "cheat" by looking directly in a constiuent table in MetisMatchingRulesTable. + * They should be re-written to use the MetisMatchingRulesTable API. + */ + +// Include this so we can step the clock forward without waiting real time +#include "../../core/metis_Forwarder.c" + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../metis_StandardPIT.c" + +// so we can directly test the underlying tables +#include "../metis_MatchingRulesTable.c" + +// test data set +#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h> + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <parc/logging/parc_LogReporterTextStdout.h> + +MetisForwarder *metis; + +LONGBOW_TEST_RUNNER(metis_PIT) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(metis_PIT) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(metis_PIT) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// =============================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, metisPit_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, metisPit_ReceiveInterest_NewEntry); + LONGBOW_RUN_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingExpired); + LONGBOW_RUN_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingExpired_VerifyTable); + LONGBOW_RUN_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingCurrentSameReversePath); + LONGBOW_RUN_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingCurrentNewReversePath); + LONGBOW_RUN_TEST_CASE(Global, metisPit_SatisfyInterest); + LONGBOW_RUN_TEST_CASE(Global, metisPIT_RemoveInterest); + LONGBOW_RUN_TEST_CASE(Global, metisPIT_AddEgressConnectionId); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + metis = metisForwarder_Create(NULL); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + metisForwarder_Destroy(&metis); + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, metisPit_Create_Destroy) +{ + metisLogger_SetLogLevel(metisForwarder_GetLogger(metis), MetisLoggerFacility_Processor, PARCLogLevel_Debug); + size_t baselineMemory = parcMemory_Outstanding(); + + MetisPIT *pit = metisStandardPIT_Create(metis); + metisPIT_Release(&pit); + + assertTrue(parcMemory_Outstanding() == baselineMemory, "Memory imbalance on create/destroy: %u", parcMemory_Outstanding()); +} + +/** + * Receive an interest that is not already in the table + */ +LONGBOW_TEST_CASE(Global, metisPit_ReceiveInterest_NewEntry) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + + MetisPITVerdict verdict = metisPIT_ReceiveInterest(generic, interest); + size_t table_length = parcHashCodeTable_Length(pit->table->tableByName); + + metisMessage_Release(&interest); + + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(table_length == 1, "tableByName wrong length, expected %u got %zu", 1, table_length); + assertTrue(verdict == MetisPITVerdict_Forward, "New entry did not return PIT_VERDICT_NEW_ENTRY, got %d", verdict); +} + +/** + * Receive an interest that is in the table, but expired + */ +LONGBOW_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingExpired) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *interest_1 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + MetisMessage *interest_2 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 2, 2, logger); + + // stuff in the first interest + _metisPIT_StoreInTable(pit, interest_1); + + // we need to crank the clock forward over 4 seconds, so add 5 seconds to the clock + metis->clockOffset = metisForwarder_NanosToTicks(5000000000ULL); + + // now do the operation we're testing. The previous entry should show as expired + MetisPITVerdict verdict_2 = metisPIT_ReceiveInterest(generic, interest_2); + + size_t table_length = parcHashCodeTable_Length(pit->table->tableByName); + + metisMessage_Release(&interest_1); + metisMessage_Release(&interest_2); + + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(table_length == 1, "tableByName wrong length, expected %u got %zu", 1, table_length); + assertTrue(verdict_2 == MetisPITVerdict_Forward, "New entry did not return PIT_VERDICT_NEW_ENTRY, got %d", verdict_2); +} + +/** + * Receive an interest that is in the table, but expired. + * In this test, retrieve the interest from the table and make sure its the 2nd one. + */ +LONGBOW_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingExpired_VerifyTable) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *interest_1 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + MetisMessage *interest_2 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 2, 2, logger); + + // stuff in the first interest + _metisPIT_StoreInTable(pit, interest_1); + + // we need to crank the clock forward over 4 seconds, so add 5 seconds to the clock + metis->clockOffset = metisForwarder_NanosToTicks(5000000000ULL); + + // now do the operation we're testing. The previous entry should show as expired + metisPIT_ReceiveInterest(generic, interest_2); + + MetisPitEntry *entry = parcHashCodeTable_Get(pit->table->tableByName, interest_2); + const MetisNumberSet *ingressSet = metisPitEntry_GetIngressSet(entry); + bool containsTwo = metisNumberSet_Contains(ingressSet, 2); + + metisMessage_Release(&interest_1); + metisMessage_Release(&interest_2); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(containsTwo, "Got wrong ingressId, does not contain %u", 2); +} + +/** + * Receive an interest that is in the table, and not expired, and from an existing reverse path. + * This should cause the interest to be forwarded. + */ +LONGBOW_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingCurrentSameReversePath) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *interest_1 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + MetisMessage *interest_2 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 2, logger); + + // stuff in the first interest + _metisPIT_StoreInTable(pit, interest_1); + + // now do the operation we're testing + MetisPITVerdict verdict_2 = metisPIT_ReceiveInterest(generic, interest_2); + size_t table_length = parcHashCodeTable_Length(pit->table->tableByName); + + metisMessage_Release(&interest_1); + metisMessage_Release(&interest_2); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(table_length == 1, "tableByName wrong length, expected %u got %zu", 1, table_length); + assertTrue(verdict_2 == MetisPITVerdict_Forward, "New entry did not return MetisPITVerdict_Forward, got %d", verdict_2); +} + +/* + * Receive an interest that exists in the PIT but from a new reverse path. this should be + * aggregated as an existing entry. + */ +LONGBOW_TEST_CASE(Global, metisPit_ReceiveInterest_ExistingCurrentNewReversePath) +{ + printf("THE TEST\n"); + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *interest_1 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + MetisMessage *interest_2 = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 2, 2, logger); + + // stuff in the first interest + _metisPIT_StoreInTable(pit, interest_1); + + // now do the operation we're testing + MetisPITVerdict verdict_2 = metisPIT_ReceiveInterest(generic, interest_2); + size_t table_length = parcHashCodeTable_Length(pit->table->tableByName); + + metisMessage_Release(&interest_1); + metisMessage_Release(&interest_2); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(table_length == 1, "tableByName wrong length, expected %u got %zu", 1, table_length); + assertTrue(verdict_2 == MetisPITVerdict_Aggregate, "New entry did not return MetisPITVerdict_Aggregate, got %d", verdict_2); +} + + +LONGBOW_TEST_CASE(Global, metisPit_SatisfyInterest) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName_objecthash, sizeof(metisTestDataV0_InterestWithName_objecthash), 1, 1, logger); + MetisMessage *contentObjectMessage = metisMessage_CreateFromArray(metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject), 1, 1, logger); + + // we manually stuff it in to the proper table, then call the public API, which will + // figure out the right table then remove it. + size_t before = parcHashCodeTable_Length(pit->table->tableByName); + _metisPIT_StoreInTable(pit, interest); + MetisNumberSet *ingressSetUnion = metisPIT_SatisfyInterest(generic, contentObjectMessage); + metisPIT_RemoveInterest(generic, interest); + assertTrue(metisNumberSet_Length(ingressSetUnion) == 1, "Unexpected satisfy interest return set size (%zu)", + metisNumberSet_Length(ingressSetUnion)); + size_t after = parcHashCodeTable_Length(pit->table->tableByName); + + metisNumberSet_Release(&ingressSetUnion); + metisMessage_Release(&interest); + metisMessage_Release(&contentObjectMessage); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(after == before, "Did not remove interest in HashCodeTable: before %zu after %zu", before, after); +} + +LONGBOW_TEST_CASE(Global, metisPIT_RemoveInterest) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + + // we manually stuff it in to the proper table, then call the public API, which will + // figure out the right table then remove it. + size_t before = parcHashCodeTable_Length(pit->table->tableByName); + _metisPIT_StoreInTable(pit, interest); + metisPIT_RemoveInterest(generic, interest); + size_t after = parcHashCodeTable_Length(pit->table->tableByName); + + metisMessage_Release(&interest); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(after == before, "Did not remove interest in HashCodeTable: before %zu after %zu", before, after); +} + +LONGBOW_TEST_CASE(Global, metisPIT_AddEgressConnectionId) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + MetisLogger *logger = metisForwarder_GetLogger(metis); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + + _metisPIT_StoreInTable(pit, interest); + _metisPIT_AddEgressConnectionId(generic, interest, 6); + + MetisPitEntry *entry = metisPIT_GetPitEntry(generic, interest); + const MetisNumberSet *egressSet = metisPitEntry_GetEgressSet(entry); + + size_t egress_length = metisNumberSet_Length(egressSet); + bool contains_6 = metisNumberSet_Contains(egressSet, 6); + + metisPitEntry_Release(&entry); + metisMessage_Release(&interest); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(egress_length == 1, "Wrong egress_set length, expected %u got %zu", 1, egress_length); + assertTrue(contains_6, "Wrong egress_set match, did not contain %u", 6); +} + +// =============================================================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, metisPit_PitEntryDestroyer); + LONGBOW_RUN_TEST_CASE(Local, _metisPIT_StoreInTable); + LONGBOW_RUN_TEST_CASE(Local, metisPit_StoreInTable_IngressSetCheck); + LONGBOW_RUN_TEST_CASE(Local, _metisPIT_CalculateLifetime_WithLifetime); + LONGBOW_RUN_TEST_CASE(Local, _metisPIT_CalculateLifetime_DefaultLifetime); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + metis = metisForwarder_Create(NULL); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + metisForwarder_Destroy(&metis); + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, metisPit_PitEntryDestroyer) +{ + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_CASE(Local, _metisPIT_StoreInTable) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + + size_t before = parcHashCodeTable_Length(pit->table->tableByName); + _metisPIT_StoreInTable(pit, interest); + size_t after = parcHashCodeTable_Length(pit->table->tableByName); + + metisMessage_Release(&interest); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(after == before + 1, "Did not store interest in HashCodeTable: before %zu after %zu", before, after); +} + +LONGBOW_TEST_CASE(Local, metisPit_StoreInTable_IngressSetCheck) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + unsigned connid = 99; + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), connid, 1, logger); + metisLogger_Release(&logger); + + _metisPIT_StoreInTable(pit, interest); + MetisPitEntry *entry = parcHashCodeTable_Get(pit->table->tableByName, interest); + const MetisNumberSet *ingressSet = metisPitEntry_GetIngressSet(entry); + bool containsIngressId = metisNumberSet_Contains(ingressSet, connid); + + metisMessage_Release(&interest); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(containsIngressId, "PIT entry did not have the ingress id in its ingress set"); +} + +/* + * Use an interest with a lifetime + */ +LONGBOW_TEST_CASE(Local, _metisPIT_CalculateLifetime_WithLifetime) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_SecondInterest, sizeof(metisTestDataV0_SecondInterest), 1, 1, logger); + metisLogger_Release(&logger); + + MetisTicks now = metisForwarder_GetTicks(metis); + MetisTicks lifetime = _metisPIT_CalculateLifetime(pit, interest); + + metisMessage_Release(&interest); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + uint64_t value = 32000; + assertTrue(lifetime >= value + now, "Wrong lifetime, should be at least %" PRIu64 ", got %" PRIu64, now + value, lifetime); +} + +/* + * Use an interest without a Lifetime, should return with the default 4s lifetime + */ +LONGBOW_TEST_CASE(Local, _metisPIT_CalculateLifetime_DefaultLifetime) +{ + MetisForwarder *metis = metisForwarder_Create(NULL); + MetisPIT *generic = metisStandardPIT_Create(metis); + MetisStandardPIT *pit = metisPIT_Closure(generic); + + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + metisLogger_SetLogLevel(logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug); + parcLogReporter_Release(&reporter); + MetisMessage *interest = metisMessage_CreateFromArray(metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName), 1, 1, logger); + metisLogger_Release(&logger); + + MetisTicks now = metisForwarder_GetTicks(metis); + MetisTicks lifetime = _metisPIT_CalculateLifetime(pit, interest); + + metisMessage_Release(&interest); + metisPIT_Release(&generic); + metisForwarder_Destroy(&metis); + + assertTrue(lifetime >= 4000 + now, "Wrong lifetime, should be at least %" PRIu64 ", got %" PRIu64, now + 4000, lifetime); +} + + + +// =============================================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_PIT); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/metis/ccnx/forwarder/metis/processor/test/testrig_MockTap.h b/metis/ccnx/forwarder/metis/processor/test/testrig_MockTap.h new file mode 100644 index 00000000..e6c7e967 --- /dev/null +++ b/metis/ccnx/forwarder/metis/processor/test/testrig_MockTap.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * 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. + */ + + +#ifndef Metis_testrig_MockTap_h +#define Metis_testrig_MockTap_h + +// ========================================================================= +// Mock for tap testing +// Allows the test to set the IsTapOnX return values. +// Counts the number of calls to each TapOnX. +// Records the last message pointer +// The user sets and examines values in the static "testTap" variable and +// passes "testTapTemplate" to the tap setup. + +static bool testTap_IsTapOnReceive(const MetisTap *tap); +static bool testTap_IsTapOnSend(const MetisTap *tap); +static bool testTap_IsTapOnDrop(const MetisTap *tap); +static void testTap_TapOnReceive(MetisTap *tap, const MetisMessage *message); +static void testTap_TapOnSend(MetisTap *tap, const MetisMessage *message); +static void testTap_TapOnDrop(MetisTap *tap, const MetisMessage *message); + +// this test variable is zeroed in each FIXTURE_SETUP. +// To test tap functionality, set the various callOnX flags, run your test, +// then check the onXCounts to make sure they are right. +struct testTap_s { + bool callOnReceive; + bool callOnSend; + bool callOnDrop; + unsigned onReceiveCount; + unsigned onSendCount; + unsigned onDropCount; + + const MetisMessage *lastMessage; +} testTap; + +// you should not need tochange this template +MetisTap testTapTemplate = { + .context = &testTap, + .isTapOnReceive = &testTap_IsTapOnReceive, + .isTapOnSend = &testTap_IsTapOnSend, + .isTapOnDrop = &testTap_IsTapOnDrop, + .tapOnReceive = &testTap_TapOnReceive, + .tapOnSend = &testTap_TapOnSend, + .tapOnDrop = &testTap_TapOnDrop +}; + +static bool +testTap_IsTapOnReceive(const MetisTap *tap) +{ + struct testTap_s *mytap = (struct testTap_s *) tap->context; + return mytap->callOnReceive; +} + +static bool +testTap_IsTapOnSend(const MetisTap *tap) +{ + struct testTap_s *mytap = (struct testTap_s *) tap->context; + return mytap->callOnSend; +} + +static bool +testTap_IsTapOnDrop(const MetisTap *tap) +{ + struct testTap_s *mytap = (struct testTap_s *) tap->context; + return mytap->callOnDrop; +} + +static void +testTap_TapOnReceive(MetisTap *tap, const MetisMessage *message) +{ + struct testTap_s *mytap = (struct testTap_s *) tap->context; + mytap->onReceiveCount++; + mytap->lastMessage = message; +} + +static void +testTap_TapOnSend(MetisTap *tap, const MetisMessage *message) +{ + struct testTap_s *mytap = (struct testTap_s *) tap->context; + mytap->onSendCount++; + mytap->lastMessage = message; +} + +static void +testTap_TapOnDrop(MetisTap *tap, const MetisMessage *message) +{ + struct testTap_s *mytap = (struct testTap_s *) tap->context; + mytap->onDropCount++; + mytap->lastMessage = message; +} +#endif // Metis_testrig_MockTap_h |