VOID TestWrap( PVMDIR_TEST_STATE pState ) { PVMDIR_CIRCULAR_BUFFER pCircularBuffer; PCIRCULAR_BUFFER_TEST_ELEMENT Element; DWORD dwError = 0; dwError = VmDirCircularBufferCreate(3, sizeof(CIRCULAR_BUFFER_TEST_ELEMENT), &pCircularBuffer); TestAssertEquals(dwError, 0); Element = VmDirCircularBufferGetNextEntry(pCircularBuffer); Element->name = arrCircularBufferTestData[0].name; Element->age = arrCircularBufferTestData[0].age; Element = VmDirCircularBufferGetNextEntry(pCircularBuffer); Element->name = arrCircularBufferTestData[1].name; Element->age = arrCircularBufferTestData[1].age; Element = VmDirCircularBufferGetNextEntry(pCircularBuffer); Element->name = arrCircularBufferTestData[2].name; Element->age = arrCircularBufferTestData[2].age; Element = VmDirCircularBufferGetNextEntry(pCircularBuffer); Element->name = arrCircularBufferTestData[3].name; Element->age = arrCircularBufferTestData[3].age; dwError = VmDirCircularBufferSelectElements(pCircularBuffer, 1, Callback, &arrCircularBufferTestData[1]); TestAssertEquals(dwError, 0); VmDirCircularBufferFree(pCircularBuffer); }
// Utility for running timings of reads of the filebundle segment with // different caching. uint32 TestReadAll(FileBundleReaderSegment& segment, uint32 request_count, uint32 max_blocks, uint32 block_size, std::string prefix) { std::string buffer; buffer.reserve(block_size); buffer.resize(request_count); char* out_buffer = const_cast<char*>(buffer.data()); // Test Read uncached of entire filebundle CachedReadAccessor accessor(max_blocks < 2 ? 2 : max_blocks, block_size); khTimer_t begin = khTimer::tick(); uint32 read_count = 0; uint32 packet_count = 0; uint64 stats_bytes_read = 0; uint64 stats_disk_accesses = 0; for(uint32 offset = 0; offset < file_bundle_size_; offset += request_count) { uint32 size = std::min(request_count, file_bundle_size_ - offset); if (max_blocks == 0) { // No cache...read via the segment directly. segment.Pread(out_buffer, size, offset); stats_bytes_read += size; stats_disk_accesses++; } else { // Read via the accessor. accessor.Pread(segment, out_buffer, size, offset); } read_count += size; packet_count++; } khTimer_t end = khTimer::tick(); uint32 megabytes_per_second = static_cast<uint32>(read_count / (khTimer::delta_s(begin, end) * 1000000)); std::cerr << prefix << ": " << request_count << " bytes per packet " << packet_count << " packets " << megabytes_per_second << " MB/sec" << std::endl; // Print some basic stats to make sure the cache is doing it's job right. if (max_blocks >= 2) { stats_bytes_read = accessor.StatsBytesRead(); stats_disk_accesses = accessor.StatsDiskAccesses(); } uint64 chunk_sizes = block_size; if (max_blocks < 2) { chunk_sizes = request_count; } uint64 desired_disk_accesses = (file_bundle_size_ + chunk_sizes - 1) / chunk_sizes; TestAssertEquals(stats_bytes_read, static_cast<uint64>(file_bundle_size_)); TestAssertEquals(stats_disk_accesses, static_cast<uint64>(desired_disk_accesses)); std::cerr << "Read " << stats_bytes_read << " bytes from disk in " << stats_disk_accesses << " reads" << std::endl; return megabytes_per_second; }
VOID TestMakeCapacityBiggerShouldSucceed( PVMDIR_TEST_STATE pState ) { PVMDIR_CIRCULAR_BUFFER pCircularBuffer = NULL; DWORD dwError = 0; dwError = VmDirCircularBufferCreate(2, sizeof(CIRCULAR_BUFFER_TEST_ELEMENT), &pCircularBuffer); TestAssertEquals(dwError, 0); dwError = VmDirCircularBufferSetCapacity(pCircularBuffer, 4); TestAssertEquals(dwError, 0); }
VOID _Test_VmDirStringNCpyA_SourceStringTooLong( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; CHAR szDestination[4] = { 'a' }; dwError = VmDirStringNCpyA( szDestination, VMDIR_ARRAY_SIZE(szDestination), "Hello, world!", 7); TestAssertEquals(dwError, ERROR_INVALID_PARAMETER); TestAssertEquals(szDestination[0], 'a'); }
VOID _Test_VmDirStringNCpyA_CallShouldFailCountMatchesSize( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; CHAR szBuffer[4] = { 'a' }; dwError = VmDirStringNCpyA( szBuffer, VMDIR_ARRAY_SIZE(szBuffer), "abcd", 4); TestAssertEquals(dwError, ERROR_INVALID_PARAMETER); TestAssertEquals(szBuffer[0], 'a'); }
// Test CacheBlockAddress Utility class, mostly that constructor and // comparison operators are properly defined. bool TestCacheBlockAddress() { uint32 offset = 100; uint32 segment_id = 1010; CachedReadAccessor::CacheBlockAddress address1(segment_id, offset); TestAssertEquals(offset, address1.Offset()); { // Equality and copy constructor tests. CachedReadAccessor::CacheBlockAddress address2(segment_id, offset); TestAssert(address1 == address2); TestAssert((address1 < address2) == false); CachedReadAccessor::CacheBlockAddress address3 = address1; TestAssert(address2 == address3); } { // less than by segment id CachedReadAccessor::CacheBlockAddress address2(segment_id+1, offset); TestAssert(address1 < address2); TestAssert(address1 != address2); TestAssert((address2 < address1) == false); } { // less than by offset CachedReadAccessor::CacheBlockAddress address2(segment_id, offset+1); TestAssert(address1 < address2); TestAssert(address1 != address2); TestAssert((address2 < address1) == false); } return true; }
VOID _Test_VmDirStringNCpyA_NullSourceString( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; CHAR szDestination[16] = { 'a' }; dwError = VmDirStringNCpyA( szDestination, VMDIR_ARRAY_SIZE(szDestination), NULL, 10); TestAssertEquals(dwError, ERROR_INVALID_PARAMETER); TestAssertEquals(szDestination[0], 'a'); }
VOID _Test_VmDirStringNCpyA_EmptySourceStringWrongCount( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; CHAR szDestination[16] = { 'a' }; dwError = VmDirStringNCpyA( szDestination, VMDIR_ARRAY_SIZE(szDestination), "", 10); TestAssertEquals(dwError, 0); TestAssertEquals(szDestination[0], '\0'); }
VOID TestSelectTooManyElementsQuietlySucceeds( PVMDIR_TEST_STATE pState ) { PVMDIR_CIRCULAR_BUFFER pCircularBuffer = NULL; DWORD dwError = 0; DWORD dwCount = 0; dwError = VmDirCircularBufferCreate(4, sizeof(CIRCULAR_BUFFER_TEST_ELEMENT), &pCircularBuffer); TestAssertEquals(dwError, 0); FillBuffer(pCircularBuffer, 4); dwError = VmDirCircularBufferSelectElements(pCircularBuffer, 20, CountingCallback, &dwCount); TestAssertEquals(dwError, 0); }
VOID testEmpty( PVMDIR_TEST_STATE pState, PDEQUE pDeque ) { DWORD dwError = 0; PDEQUEUE_TEST_ELEMENT pElement = NULL; BOOLEAN fEmpty = FALSE; //Test case: pop from empty queue fEmpty = dequeIsEmpty(pDeque); TestAssertEquals(fEmpty, TRUE); dwError = dequePopLeft(pDeque, (PVOID*)&pElement); TestAssertEquals(dwError, ERROR_NO_MORE_ITEMS); }
VOID TestSelectReturnsCorrectElementsInCorrectOrder( PVMDIR_TEST_STATE pState ) { PVMDIR_CIRCULAR_BUFFER pCircularBuffer = NULL; DWORD dwError = 0; DWORD dwCount = 0; dwError = VmDirCircularBufferCreate(4, sizeof(CIRCULAR_BUFFER_TEST_ELEMENT), &pCircularBuffer); TestAssertEquals(dwError, 0); FillBuffer(pCircularBuffer, 7); dwError = VmDirCircularBufferSelectElements(pCircularBuffer, 4, Callback2, &dwCount); TestAssertEquals(dwError, 0); }
VOID TestMakeCapacitySmallerShouldSucceed( PVMDIR_TEST_STATE pState ) { PVMDIR_CIRCULAR_BUFFER pCircularBuffer = NULL; DWORD dwError = 0; dwError = VmDirCircularBufferCreate(4, sizeof(CIRCULAR_BUFFER_TEST_ELEMENT), &pCircularBuffer); TestAssertEquals(dwError, 0); FillBuffer(pCircularBuffer, 4); dwError = VmDirCircularBufferSetCapacity(pCircularBuffer, 2); TestAssertEquals(pCircularBuffer->dwCapacity, 2); TestAssertEquals(dwError, 0); TestAssert(pCircularBuffer->dwHead < pCircularBuffer->dwCapacity); }
VOID TestSelectReturnsWhenCallbackReturnsFalse( PVMDIR_TEST_STATE pState ) { PVMDIR_CIRCULAR_BUFFER pCircularBuffer = NULL; DWORD dwError = 0; DWORD dwCount = 0; dwError = VmDirCircularBufferCreate(4, sizeof(CIRCULAR_BUFFER_TEST_ELEMENT), &pCircularBuffer); TestAssertEquals(dwError, 0); FillBuffer(pCircularBuffer, 4); dwError = VmDirCircularBufferSelectElements(pCircularBuffer, 4, CountingCallback, &dwCount); TestAssertEquals(dwError, 0); TestAssertEquals(dwCount, 2); }
VOID _Test_VmDirStringNCpyA_NullDestinationString( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; dwError = VmDirStringNCpyA(NULL, 4, "test", 4); TestAssertEquals(dwError, ERROR_INVALID_PARAMETER); }
VOID _Test_VmDirStringNCpyA_EmptySourceStringRightCount( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; CHAR szDestination[16] = { '\0' }; // // This call will yield different results on windows and linux. On // the former we call the _s version of strncpy so the string's first byte // will be NULL, regardless of what it is coming into the call. However, // on linux the string will be untouched after the call. // dwError = VmDirStringNCpyA( szDestination, VMDIR_ARRAY_SIZE(szDestination), "", 0); TestAssertEquals(dwError, 0); TestAssertEquals(szDestination[0], '\0'); }
VOID TestCleanupOfValidCircularBuffer( PVMDIR_TEST_STATE pState ) { PVMDIR_CIRCULAR_BUFFER pCircularBuffer; DWORD dwError = 0; dwError = VmDirCircularBufferCreate(4, sizeof(CIRCULAR_BUFFER_TEST_ELEMENT), &pCircularBuffer); TestAssertEquals(dwError, 0); VmDirCircularBufferFree(pCircularBuffer); }
VOID testStack( PVMDIR_TEST_STATE pState, PDEQUE pDeque ) { DWORD dwError = 0; PDEQUEUE_TEST_ELEMENT pElement = NULL; int i = 0; for (i = 0; i < VMDIR_ARRAY_SIZE(arrDequeTestData); i++) { dwError = dequePush(pDeque, &arrDequeTestData[i]); TestAssertEquals(dwError, ERROR_SUCCESS); } for (i = 0; i < VMDIR_ARRAY_SIZE(arrDequeTestData); i++) { dwError = dequePop(pDeque, (PVOID*)&pElement); TestAssertEquals(dwError, ERROR_SUCCESS); TestAssertPtrEquals(pElement, &arrDequeTestData[VMDIR_ARRAY_SIZE(arrDequeTestData)-i-1]); } }
VOID TestMaxDwordValueRoundTrip( PVMDIR_TEST_STATE pState ) { DWORD dwTestValue = 0; DWORD dwComparisonValue = 0; DWORD dwError = 0; dwTestValue = 0xFFFFFFFF; // Biggest possible DWORD dwError = VmDirSetRegKeyValueDword( VMDIR_CONFIG_PARAMETER_KEY_PATH, "TestMaxValue", dwTestValue); TestAssertEquals(dwError, 0); dwError = VmDirGetRegKeyValueDword( VMDIR_CONFIG_PARAMETER_KEY_PATH, "TestMaxValue", &dwComparisonValue, 0); TestAssertEquals(dwError, 0); TestAssertEquals(dwTestValue, dwComparisonValue); }
DWORD _TestVmDirAllocateStringPrintfWithBadParameters( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; PSTR pszString = NULL; dwError = VmDirAllocateStringPrintf( NULL, "dword ==> %d, string ==> '%s'", 42, "Hello, world!"); TestAssertEquals(dwError, ERROR_INVALID_PARAMETER); dwError = VmDirAllocateStringPrintf( &pszString, NULL, 42, "Hello, world!"); TestAssertEquals(dwError, ERROR_INVALID_PARAMETER); return 0; }
BOOLEAN Callback( PVOID Element, PVOID Context ) { PCIRCULAR_BUFFER_TEST_ELEMENT TestElement = (PCIRCULAR_BUFFER_TEST_ELEMENT)Element; PCIRCULAR_BUFFER_TEST_ELEMENT ReferenceElement = (PCIRCULAR_BUFFER_TEST_ELEMENT)Context; PVMDIR_TEST_STATE pState = NULL; pState = TestElement->pState; TestAssertEquals(TestElement->age, ReferenceElement->age); TestAssertStrEquals(TestElement->name, ReferenceElement->name); return TRUE; }
VOID _Test_VmDirStringNCpyA_CallShouldSucceedWithTruncation( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; CHAR szDestination[6] = { 'a' }; dwError = VmDirStringNCpyA( szDestination, VMDIR_ARRAY_SIZE(szDestination), "Hello, world!", VMDIR_ARRAY_SIZE(szDestination) - 1); TestAssertEquals(dwError, 0); TestAssertStrEquals(szDestination, "Hello"); }
DWORD _TestVmDirAllocateStringPrintfWithGoodParameters( PVMDIR_TEST_STATE pState ) { PSTR pszString = NULL; DWORD dwError = 0; dwError = VmDirAllocateStringPrintf( &pszString, "dword ==> %d, string ==> '%s'", (DWORD)42, "Hello, world!"); TestAssertEquals(dwError, ERROR_SUCCESS); TestAssertStrEquals(pszString, "dword ==> 42, string ==> 'Hello, world!'"); return 0; }
VOID TestDequeCode( PVMDIR_TEST_STATE pState ) { DWORD dwError = 0; PDEQUE pDeque = NULL; printf("Testing deque code ..."); dwError = dequeCreate(&pDeque); TestAssertEquals(dwError, ERROR_SUCCESS); testEmpty(pState, pDeque); testQueue(pState, pDeque); testStack(pState, pDeque); testEmpty(pState, pDeque); dequeFree(pDeque); printf(" PASSED\n"); }
DWORD TestStandardRightsForUser( PVMDIR_TEST_STATE pState, LDAP *pLd, PCSTR pszContainerName ) { DWORD dwError = 0; dwError = TryToListChildObjects(pState, pszContainerName); BAIL_ON_VMDIR_ERROR(dwError); dwError = TryToDeleteObject(pState, pszContainerName); BAIL_ON_VMDIR_ERROR(dwError); dwError = TryToReadProperties(pState, pszContainerName); BAIL_ON_VMDIR_ERROR(dwError); dwError = TryToReadSD(pState, pszContainerName); BAIL_ON_VMDIR_ERROR(dwError); dwError = TryToWriteProperties(pState, pszContainerName); BAIL_ON_VMDIR_ERROR(dwError); dwError = TryToWriteSD(pState, pszContainerName); BAIL_ON_VMDIR_ERROR(dwError); dwError = TryToListObject(pState, pszContainerName); BAIL_ON_VMDIR_ERROR(dwError); cleanup: return dwError; error: TestAssertEquals(dwError, 0); goto cleanup; }
// Test the CacheBlock utility object Read() and LastAccessTick(). bool TestCacheBlock() { uint32 offset = 13; uint32 segment_id = 0; uint32 block_size = file_bundle_size_ / 4; CachedReadAccessor::CacheBlockAddress address1(segment_id, 0); uint64 access_tick = 1033; CachedReadAccessor::CacheBlock block1(address1, block_size); // Check the initial access tick TestAssert(block1.LastAccessTick() == 0); block1.SetLastAccessTick(access_tick - 1); TestAssert(block1.LastAccessTick() == (access_tick - 1)); // Test Read // Read request that is contained in 1 block. uint32 request_count = block_size - offset - 10; FileBundleReaderSegment segment(file_pool_, path_base_, segment_names_[segment_id], segment_names_[segment_id], segment_id); std::string buffer; buffer.reserve(block_size); buffer.resize(request_count); char* out_buffer = const_cast<char*>(buffer.data()); // Test read within non-cached (uninitialized) block uint64 stats_bytes_read = 0; uint64 stats_disk_accesses = 0; uint32 read_count = block1.Read(segment, out_buffer, request_count, offset, access_tick++, stats_bytes_read, stats_disk_accesses); TestAssert(block1.LastAccessTick() + 1 == access_tick); TestAssertEquals(stats_bytes_read, static_cast<uint64>(block_size)); TestAssertEquals(stats_disk_accesses, static_cast<uint64>(1)); TestAssertEquals(request_count, read_count); char* expected = test_buffer_ + offset; if (!TestBuffer(out_buffer, expected, read_count, std::string("TestCacheBlock: all data in one non-cached block"))) return false; // Test read from the NOW cached block within the segment. offset = 23; request_count /= 3; buffer.resize(request_count); read_count = block1.Read(segment, out_buffer, request_count, offset, access_tick++, stats_bytes_read, stats_disk_accesses); TestAssertEquals(stats_bytes_read, static_cast<uint64>(block_size)); TestAssertEquals(stats_disk_accesses, static_cast<uint64>(1)); TestAssertEquals(request_count, read_count); expected = test_buffer_ + offset; if (!TestBuffer(out_buffer, expected, read_count, std::string("TestCacheBlock: all data in one cached block"))) return false; // Test a PARTIAL reads from a block (i.e., a request past the block // boundary. // create a new CacheBlock to start with an empty cache. block1 = CachedReadAccessor::CacheBlock(address1, block_size); request_count = 200; buffer.resize(request_count); uint32 expected_read_count = 133; offset = block_size - expected_read_count; // Read from a non-cached block read_count = block1.Read(segment, out_buffer, request_count, offset, access_tick++, stats_bytes_read, stats_disk_accesses); TestAssertEquals(stats_bytes_read, static_cast<uint64>(2*block_size)); TestAssertEquals(stats_disk_accesses, static_cast<uint64>(2)); TestAssertEquals(expected_read_count, read_count); expected = test_buffer_ + offset; if (!TestBuffer(out_buffer, expected, read_count, std::string("TestCacheBlock: partial data in cached block"))) return false; // Read from a cached block read_count = block1.Read(segment, out_buffer, request_count, offset, access_tick++, stats_bytes_read, stats_disk_accesses); TestAssertEquals(expected_read_count, read_count); expected = test_buffer_ + offset; if (!TestBuffer(out_buffer, expected, read_count, std::string("TestCacheBlock: partial data in cached block"))) return false; return true; }
// Test the CachedReadAccessor utility methods BlockAddress(), // AddCacheBlock(), GetCacheBlock() and FindBlocks(). bool TestCachedReadAccessorBasics() { uint32 offset = 13; uint32 segment_id = 0; uint32 block_size = file_bundle_size_ / 4; uint64 access_tick = 1033; CachedReadAccessor accessor(2, block_size); // Test BlockAddress CachedReadAccessor::CacheBlockAddress address1 = accessor.BlockAddress(segment_id, offset); TestAssertEquals(address1.Offset(), static_cast<uint32>(0)); uint32 n = 7; CachedReadAccessor::CacheBlockAddress address2 = accessor.BlockAddress(segment_id, offset + n * block_size); TestAssertEquals(address2.Offset(), n * block_size); CachedReadAccessor::CacheBlockAddress address3 = accessor.BlockAddress(segment_id, (n+2) * block_size - 10); TestAssertEquals(address3.Offset(), (n+1) * block_size); // Test that AddCacheBlock and GetCacheBlock are compatible. CachedReadAccessor::CacheBlock* block1 = accessor.AddCacheBlock(address1); CachedReadAccessor::CacheBlock* block1_b = accessor.GetCacheBlock(address1); TestAssertEquals(block1, block1_b); block1->SetLastAccessTick(access_tick); // Test getting a new block twice. CachedReadAccessor::CacheBlock* block2 = accessor.GetCacheBlock(address2); CachedReadAccessor::CacheBlock* block2_b = accessor.GetCacheBlock(address2); TestAssertEquals(block2, block2_b); TestAssert(block1 != block2); block2->SetLastAccessTick(access_tick+1); uint64 block1_access_tick = block1->LastAccessTick(); // Ok, we have block1 and block2 // Now we try to get block3, should force one of the cached blocks out of // the cache (namely block1). CachedReadAccessor::CacheBlock* block3 = accessor.GetCacheBlock(address3); TestAssert(block3 != block1); TestAssert(block3 != block2); block3->SetLastAccessTick(0); // mark this as older for fun. // We should have block2 and block3 // Getting block2 again should be the same object as before. block2_b = accessor.GetCacheBlock(address2); TestAssertEquals(block2, block2_b); // Getting block1 again should be a newly created object and block3 should // be flushed. block1_b = accessor.GetCacheBlock(address1); TestAssert(block1_b->LastAccessTick() != block1_access_tick); block1 = block1_b; // block1 was deleted...better be safe here. // Getting block2 yet again should be the same object as before. block2_b = accessor.GetCacheBlock(address2); TestAssertEquals(block2, block2_b); // Test FindBlocks // case 1: request spans a single block // case 2: request spans two blocks uint32 in_block_offset = 33; offset = block_size + in_block_offset; address1 = accessor.BlockAddress(segment_id, offset); address2 = accessor.BlockAddress(segment_id, offset + block_size); // test these cases when not cached and then again when cached. uint32 request_size = block_size - 2 * in_block_offset; // set request size to guarantee to be within 1 block. uint32 blocks_returned = accessor.FindBlocks(segment_id, request_size, offset, &block1, &block2); TestAssertEquals(blocks_returned, static_cast<uint32>(1)); TestAssert(block2 == NULL); TestAssert(block1 != NULL); block1->SetLastAccessTick(access_tick++); // Make sure it stays in the cache. // Try again, slightly different request in the same block, block1 // should be cached. blocks_returned = accessor.FindBlocks(segment_id, request_size - 3 * in_block_offset, offset + in_block_offset, &block1_b, &block2); TestAssertEquals(blocks_returned, static_cast<uint32>(1)); TestAssert(block2 == NULL); TestAssert(block1 == block1_b); // Now try the same FindBlocks test on requests that span 2 CacheBlocks. request_size = block_size - in_block_offset / 2; blocks_returned = accessor.FindBlocks(segment_id, request_size, offset, &block1_b, &block2); TestAssertEquals(blocks_returned, static_cast<uint32>(2)); TestAssert(block2 != NULL); // Make sure it stays in the cache. block2->SetLastAccessTick(access_tick++); TestAssert(block1 == block1_b); // Try again, slightly different request in the same block, block1 // should be cached. blocks_returned = accessor.FindBlocks(segment_id, request_size, offset + in_block_offset, &block1_b, &block2_b); TestAssertEquals(blocks_returned, static_cast<uint32>(2)); TestAssert(block2 == block2_b); TestAssert(block1 == block1_b); return true; }