static void tstVDSnapSegmentsDice(PVDSNAPTEST pTest, PVDDISKSEG paDiskSeg, uint32_t cDiskSegments, uint8_t *pbTestPattern, size_t cbTestPattern) { for (uint32_t i = 0; i < cDiskSegments; i++) { /* Do we want to change the current segment? */ if (tstVDSnapIsTrue(pTest->uChangeSegChance)) paDiskSeg[i].pbDataDiff = pbTestPattern + RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 0, cbTestPattern - paDiskSeg[i].cbSeg - 512), 512); } }
static int tstRandAdv(RTRAND hRand) { /* * Test distribution. */ #if 1 /* unsigned 32-bit */ static const struct { uint32_t u32First; uint32_t u32Last; } s_aU32Tests[] = { { 0, UINT32_MAX }, { 0, UINT32_MAX / 2 + UINT32_MAX / 4 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 8 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 16 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 64 }, { 0, UINT32_MAX / 2 }, { UINT32_MAX / 4, UINT32_MAX / 4 * 3 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { 1234, 1234 + TST_RAND_SAMPLE_RANGES - 1 }, }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aU32Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; uint32_t const uFirst = s_aU32Tests[iTest].u32First; uint32_t const uLast = s_aU32Tests[iTest].u32Last; uint32_t const uRange = uLast - uFirst; Assert(uLast >= uFirst); uint32_t const uDivisor = uRange / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvU32Ex(,%#RX32, %#RX32) distribution... [div=%#RX32 range=%#RX32]\n", uFirst, uLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { uint32_t uRand = RTRandAdvU32Ex(hRand, uFirst, uLast); CHECK_EXPR_MSG(uRand >= uFirst, ("%#RX32 %#RX32\n", uRand, uFirst)); CHECK_EXPR_MSG(uRand <= uLast, ("%#RX32 %#RX32\n", uRand, uLast)); uint32_t off = uRand - uFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif #if 1 /* unsigned 64-bit */ static const struct { uint64_t u64First; uint64_t u64Last; } s_aU64Tests[] = { { 0, UINT64_MAX }, { 0, UINT64_MAX / 2 + UINT64_MAX / 4 }, { 0, UINT64_MAX / 2 + UINT64_MAX / 8 }, { 0, UINT64_MAX / 2 + UINT64_MAX / 16 }, { 0, UINT64_MAX / 2 + UINT64_MAX / 64 }, { 0, UINT64_MAX / 2 }, { UINT64_MAX / 4, UINT64_MAX / 4 * 3 }, { 0, UINT32_MAX }, { 0, UINT32_MAX / 2 + UINT32_MAX / 4 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 8 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 16 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 64 }, { 0, UINT32_MAX / 2 }, { UINT32_MAX / 4, UINT32_MAX / 4 * 3 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { 1234, 1234 + TST_RAND_SAMPLE_RANGES - 1 }, }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aU64Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; uint64_t const uFirst = s_aU64Tests[iTest].u64First; uint64_t const uLast = s_aU64Tests[iTest].u64Last; uint64_t const uRange = uLast - uFirst; Assert(uLast >= uFirst); uint64_t const uDivisor = uRange / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvU64Ex(,%#RX64, %#RX64) distribution... [div=%#RX64 range=%#RX64]\n", uFirst, uLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { uint64_t uRand = RTRandAdvU64Ex(hRand, uFirst, uLast); CHECK_EXPR_MSG(uRand >= uFirst, ("%#RX64 %#RX64\n", uRand, uFirst)); CHECK_EXPR_MSG(uRand <= uLast, ("%#RX64 %#RX64\n", uRand, uLast)); uint64_t off = uRand - uFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif #if 1 /* signed 32-bit */ static const struct { int32_t i32First; int32_t i32Last; } s_aS32Tests[] = { { -429496729, 429496729 }, { INT32_MIN, INT32_MAX }, { INT32_MIN, INT32_MAX / 2 }, { -0x20000000, INT32_MAX }, { -0x10000000, INT32_MAX }, { -0x08000000, INT32_MAX }, { -0x00800000, INT32_MAX }, { -0x00080000, INT32_MAX }, { -0x00008000, INT32_MAX }, { -0x00000800, INT32_MAX }, { 2, INT32_MAX / 2 }, { 4000000, INT32_MAX / 2 }, { -4000000, INT32_MAX / 2 }, { INT32_MIN / 2, INT32_MAX / 2 }, { INT32_MIN / 3, INT32_MAX / 2 }, { INT32_MIN / 3, INT32_MAX / 3 }, { INT32_MIN / 3, INT32_MAX / 4 }, { INT32_MIN / 4, INT32_MAX / 4 }, { INT32_MIN / 5, INT32_MAX / 5 }, { INT32_MIN / 6, INT32_MAX / 6 }, { INT32_MIN / 7, INT32_MAX / 6 }, { INT32_MIN / 7, INT32_MAX / 7 }, { INT32_MIN / 7, INT32_MAX / 8 }, { INT32_MIN / 8, INT32_MAX / 8 }, { INT32_MIN / 9, INT32_MAX / 9 }, { INT32_MIN / 9, INT32_MAX / 12 }, { INT32_MIN / 12, INT32_MAX / 12 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { -TST_RAND_SAMPLE_RANGES / 2, TST_RAND_SAMPLE_RANGES / 2 - 1 }, }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aS32Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; int32_t const iFirst = s_aS32Tests[iTest].i32First; int32_t const iLast = s_aS32Tests[iTest].i32Last; uint32_t const uRange = iLast - iFirst; AssertMsg(iLast >= iFirst, ("%d\n", iTest)); uint32_t const uDivisor = (uRange ? uRange : UINT32_MAX) / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvS32Ex(,%#RI32, %#RI32) distribution... [div=%#RX32 range=%#RX32]\n", iFirst, iLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { int32_t iRand = RTRandAdvS32Ex(hRand, iFirst, iLast); CHECK_EXPR_MSG(iRand >= iFirst, ("%#RI32 %#RI32\n", iRand, iFirst)); CHECK_EXPR_MSG(iRand <= iLast, ("%#RI32 %#RI32\n", iRand, iLast)); uint32_t off = iRand - iFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif #if 1 /* signed 64-bit */ static const struct { int64_t i64First; int64_t i64Last; } s_aS64Tests[] = { { INT64_MIN, INT64_MAX }, { INT64_MIN, INT64_MAX / 2 }, { INT64_MIN / 2, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 4, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 8, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 16, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 64, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 64, INT64_MAX / 2 + INT64_MAX / 64 }, { INT64_MIN / 2, INT64_MAX / 2 + INT64_MAX / 64 }, { INT64_MIN / 2, INT64_MAX / 2 + INT64_MAX / 8 }, { INT64_MIN / 2, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 4, INT64_MAX / 2 - INT64_MAX / 4 }, { INT64_MIN / 2 - INT64_MIN / 4, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 8, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 16, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 16, INT64_MAX / 2 - INT64_MAX / 16 }, { INT64_MIN / 2 - INT64_MIN / 32, INT64_MAX / 2 - INT64_MAX / 16 }, { INT64_MIN / 2 - INT64_MIN / 32, INT64_MAX / 2 - INT64_MAX / 32 }, { INT64_MIN / 2 - INT64_MIN / 64, INT64_MAX / 2 - INT64_MAX / 64 }, { INT64_MIN / 2 - INT64_MIN / 8, INT64_MAX / 2 }, { INT64_MIN / 4, INT64_MAX / 4 }, { INT64_MIN / 5, INT64_MAX / 5 }, { INT64_MIN / 6, INT64_MAX / 6 }, { INT64_MIN / 7, INT64_MAX / 7 }, { INT64_MIN / 8, INT64_MAX / 8 }, { INT32_MIN, INT32_MAX }, { INT32_MIN, INT32_MAX / 2 }, { -0x20000000, INT32_MAX }, { -0x10000000, INT32_MAX }, { -0x7f000000, INT32_MAX }, { -0x08000000, INT32_MAX }, { -0x00800000, INT32_MAX }, { -0x00080000, INT32_MAX }, { -0x00008000, INT32_MAX }, { 2, INT32_MAX / 2 }, { 4000000, INT32_MAX / 2 }, { -4000000, INT32_MAX / 2 }, { INT32_MIN / 2, INT32_MAX / 2 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { -TST_RAND_SAMPLE_RANGES / 2, TST_RAND_SAMPLE_RANGES / 2 - 1 } }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aS64Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; int64_t const iFirst = s_aS64Tests[iTest].i64First; int64_t const iLast = s_aS64Tests[iTest].i64Last; uint64_t const uRange = iLast - iFirst; AssertMsg(iLast >= iFirst, ("%d\n", iTest)); uint64_t const uDivisor = (uRange ? uRange : UINT64_MAX) / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvS64Ex(,%#RI64, %#RI64) distribution... [div=%#RX64 range=%#016RX64]\n", iFirst, iLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { int64_t iRand = RTRandAdvS64Ex(hRand, iFirst, iLast); CHECK_EXPR_MSG(iRand >= iFirst, ("%#RI64 %#RI64\n", iRand, iFirst)); CHECK_EXPR_MSG(iRand <= iLast, ("%#RI64 %#RI64\n", iRand, iLast)); uint64_t off = iRand - iFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif /* * Test saving and restoring the state. */ RTPrintf("tstRand: TESTING RTRandAdvSave/RestoreSave\n"); char szState[256]; size_t cbState = sizeof(szState); int rc = RTRandAdvSaveState(hRand, szState, &cbState); if (rc != VERR_NOT_SUPPORTED) { CHECK_EXPR_MSG(rc == VINF_SUCCESS, ("RTRandAdvSaveState(%p,,256) -> %Rrc (%d)\n", (uintptr_t)hRand, rc, rc)); uint32_t const u32A1 = RTRandAdvU32(hRand); uint32_t const u32B1 = RTRandAdvU32(hRand); RTPrintf("tstRand: state:\"%s\" A=%RX32 B=%RX32\n", szState, u32A1, u32B1); rc = RTRandAdvRestoreState(hRand, szState); CHECK_EXPR_MSG(rc == VINF_SUCCESS, ("RTRandAdvRestoreState(%p,\"%s\") -> %Rrc (%d)\n", (uintptr_t)hRand, szState, rc, rc)); uint32_t const u32A2 = RTRandAdvU32(hRand); uint32_t const u32B2 = RTRandAdvU32(hRand); CHECK_EXPR_MSG(u32A1 == u32A2, ("u32A1=%RX32 u32A2=%RX32\n", u32A1, u32A2)); CHECK_EXPR_MSG(u32B1 == u32B2, ("u32B1=%RX32 u32B2=%RX32\n", u32B1, u32B2)); } else { szState[0] = '\0'; rc = RTRandAdvRestoreState(hRand, szState); CHECK_EXPR_MSG(rc == VERR_NOT_SUPPORTED, ("RTRandAdvRestoreState(%p,\"\") -> %Rrc (%d)\n", (uintptr_t)hRand, rc, rc)); } /* * Destroy it. */ rc = RTRandAdvDestroy(hRand); CHECK_EXPR_MSG(rc == VINF_SUCCESS, ("RTRandAdvDestroy(%p) -> %Rrc (%d)\n", (uintptr_t)hRand, rc, rc)); return 0; }
static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) { int rc; PVBOXHDD pVD = NULL; VDGEOMETRY PCHS = { 0, 0, 0 }; VDGEOMETRY LCHS = { 0, 0, 0 }; PVDINTERFACE pVDIfs = NULL; VDINTERFACEERROR VDIfError; /** Buffer storing the random test pattern. */ uint8_t *pbTestPattern = NULL; /** Number of disk segments */ uint32_t cDiskSegments; /** Array of disk segments */ PVDDISKSEG paDiskSeg = NULL; unsigned cDiffs = 0; unsigned idDiff = 0; /* Diff ID counter for the filename */ /* Delete all images from a previous run. */ RTFileDelete(pTest->pcszBaseImage); for (unsigned i = 0; i < pTest->cIterations; i++) { char *pszDiffFilename = NULL; rc = RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff); if (RT_SUCCESS(rc)) { if (RTFileExists(pszDiffFilename)) RTFileDelete(pszDiffFilename); RTStrFree(pszDiffFilename); } } /* Create the virtual disk test data */ pbTestPattern = (uint8_t *)RTMemAlloc(pTest->cbTestPattern); RTRandAdvBytes(g_hRand, pbTestPattern, pTest->cbTestPattern); cDiskSegments = RTRandAdvU32Ex(g_hRand, pTest->cDiskSegsMin, pTest->cDiskSegsMax); uint64_t cbDisk = 0; paDiskSeg = (PVDDISKSEG)RTMemAllocZ(cDiskSegments * sizeof(VDDISKSEG)); if (!paDiskSeg) { RTPrintf("Failed to allocate memory for random disk segments\n"); g_cErrors++; return VERR_NO_MEMORY; } for (unsigned i = 0; i < cDiskSegments; i++) { paDiskSeg[i].off = cbDisk; paDiskSeg[i].cbSeg = RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 512, pTest->cbTestPattern), 512); if (tstVDSnapIsTrue(pTest->uAllocatedBlocks)) paDiskSeg[i].pbData = pbTestPattern + RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 0, pTest->cbTestPattern - paDiskSeg[i].cbSeg - 512), 512); else paDiskSeg[i].pbData = NULL; /* Not allocated initially */ cbDisk += paDiskSeg[i].cbSeg; } RTPrintf("Disk size is %llu bytes\n", cbDisk); #define CHECK(str) \ do \ { \ RTPrintf("%s rc=%Rrc\n", str, rc); \ if (RT_FAILURE(rc)) \ { \ if (pbTestPattern) \ RTMemFree(pbTestPattern); \ if (paDiskSeg) \ RTMemFree(paDiskSeg); \ VDDestroy(pVD); \ g_cErrors++; \ return rc; \ } \ } while (0) #define CHECK_BREAK(str) \ do \ { \ RTPrintf("%s rc=%Rrc\n", str, rc); \ if (RT_FAILURE(rc)) \ { \ g_cErrors++; \ break; \ } \ } while (0) /* Create error interface. */ /* Create error interface. */ VDIfError.pfnError = tstVDError; VDIfError.pfnMessage = tstVDMessage; rc = VDInterfaceAdd(&VDIfError.Core, "tstVD_Error", VDINTERFACETYPE_ERROR, NULL, sizeof(VDINTERFACEERROR), &pVDIfs); AssertRC(rc); rc = VDCreate(pVDIfs, VDTYPE_HDD, &pVD); CHECK("VDCreate()"); rc = VDCreateBase(pVD, pTest->pcszBackend, pTest->pcszBaseImage, cbDisk, VD_IMAGE_FLAGS_NONE, "Test image", &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL); CHECK("VDCreateBase()"); bool fInit = true; uint32_t cIteration = 0; /* Do the real work now */ while ( RT_SUCCESS(rc) && cIteration < pTest->cIterations) { /* Write */ rc = tstVDSnapWrite(pVD, paDiskSeg, cDiskSegments, cbDisk, fInit); CHECK_BREAK("tstVDSnapWrite()"); fInit = false; /* Write returned, do we want to create a new diff or merge them? */ bool fCreate = cDiffs < pTest->cDiffsMinBeforeMerge ? true : tstVDSnapIsTrue(pTest->uCreateDiffChance); if (fCreate) { char *pszDiffFilename = NULL; RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", idDiff, pTest->pcszDiffSuff); CHECK("RTStrAPrintf()"); idDiff++; cDiffs++; rc = VDCreateDiff(pVD, pTest->pcszBackend, pszDiffFilename, VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL); CHECK_BREAK("VDCreateDiff()"); RTStrFree(pszDiffFilename); VDDumpImages(pVD); /* Change data */ tstVDSnapSegmentsDice(pTest, paDiskSeg, cDiskSegments, pbTestPattern, pTest->cbTestPattern); } else { uint32_t uStartMerge = RTRandAdvU32Ex(g_hRand, 1, cDiffs - 1); uint32_t uEndMerge = RTRandAdvU32Ex(g_hRand, uStartMerge + 1, cDiffs); RTPrintf("Merging %u diffs from %u to %u...\n", uEndMerge - uStartMerge, uStartMerge, uEndMerge); if (pTest->fForward) rc = VDMerge(pVD, uStartMerge, uEndMerge, NULL); else rc = VDMerge(pVD, uEndMerge, uStartMerge, NULL); CHECK_BREAK("VDMerge()"); cDiffs -= uEndMerge - uStartMerge; VDDumpImages(pVD); /* Go through the disk segments and reset pointers. */ for (uint32_t i = 0; i < cDiskSegments; i++) { if (paDiskSeg[i].pbDataDiff) { paDiskSeg[i].pbData = paDiskSeg[i].pbDataDiff; paDiskSeg[i].pbDataDiff = NULL; } } /* Now compare the result with our test pattern */ rc = tstVDSnapReadVerify(pVD, paDiskSeg, cDiskSegments, cbDisk); CHECK_BREAK("tstVDSnapReadVerify()"); } cIteration++; } VDDumpImages(pVD); VDDestroy(pVD); if (paDiskSeg) RTMemFree(paDiskSeg); if (pbTestPattern) RTMemFree(pbTestPattern); RTFileDelete(pTest->pcszBaseImage); for (unsigned i = 0; i < idDiff; i++) { char *pszDiffFilename = NULL; RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff); RTFileDelete(pszDiffFilename); RTStrFree(pszDiffFilename); } #undef CHECK return rc; }