Improve the PDFDataRangeTransport unit-tests

- Add a new test using only streaming, since that was missing and the lack of which most likely contributed to previous bugs in the `PDFDataRangeTransport` implementation (see PR 10675 and 20634).

 - Improve the "ranges and streaming" test, to utilize both ranges *and* streaming properly, since the way it was written seemed somewhat unrealistic given how data will normally arrive when `PDFDataRangeTransport` is being used.

 - Provide more `initialData`, in relevant tests, since a length smaller than `rangeChunkSize` seem pretty pointless.

 - Test the `contentDispositionFilename`, and `contentLength`, handling in the `PDFDataRangeTransport` implementation.
This commit is contained in:
Jonas Jenwald 2026-02-27 13:06:18 +01:00
parent 218a687a3b
commit fecb0aab1d

View File

@ -5188,6 +5188,11 @@ have written that much by now. So, heres to squashing bugs.`);
});
describe("PDFDataRangeTransport", function () {
async function streamDelay() {
return new Promise(resolve => {
setTimeout(resolve, 250);
});
}
let dataPromise;
beforeAll(function () {
@ -5200,20 +5205,26 @@ have written that much by now. So, heres to squashing bugs.`);
dataPromise = null;
});
it("should fetch document info and page using ranges", async function () {
const initialDataLength = 4000;
it("should fetch document info and page using only ranges", async function () {
const initialDataLength = 80000; // Larger than `rangeChunkSize`, since otherwise it's pretty pointless.
const subArrays = [];
let initialProgress = null;
let fetches = 0;
const data = await dataPromise;
const dataLength = data.length;
const initialData = new Uint8Array(data.subarray(0, initialDataLength));
subArrays.push(initialData);
const transport = new PDFDataRangeTransport(data.length, initialData);
transport.requestDataRange = function (begin, end) {
const transport = new PDFDataRangeTransport(
dataLength,
initialData,
/* progressiveDone = */ undefined,
/* contentDispositionFilename = */ "aaa.pdf"
);
transport.requestDataRange = (begin, end) => {
fetches++;
waitSome(function () {
waitSome(() => {
const chunk = new Uint8Array(data.subarray(begin, end));
subArrays.push(chunk);
@ -5221,7 +5232,10 @@ have written that much by now. So, heres to squashing bugs.`);
});
};
const loadingTask = getDocument({ range: transport });
const loadingTask = getDocument({
range: transport,
rangeChunkSize: 65536,
});
loadingTask.onProgress = evt => {
initialProgress = evt;
loadingTask.onProgress = null;
@ -5232,13 +5246,176 @@ have written that much by now. So, heres to squashing bugs.`);
const pdfPage = await pdfDocument.getPage(10);
expect(pdfPage.rotate).toEqual(0);
const { contentDispositionFilename, contentLength } =
await pdfDocument.getMetadata();
expect(contentDispositionFilename).toEqual("aaa.pdf");
expect(contentLength).toEqual(dataLength);
expect(fetches).toBeGreaterThan(4);
expect(initialProgress).toEqual({
loaded: initialDataLength,
total: dataLength,
percent: 8,
});
// Check that the TypedArrays were transferred.
for (const array of subArrays) {
expect(array.length).toEqual(0);
}
await loadingTask.destroy();
});
it("should fetch document info and page using only streaming", async function () {
const initialDataLength = 80000; // Larger than `rangeChunkSize`, since otherwise it's pretty pointless.
const subArrays = [];
let initialProgress = null;
let fetches = 0;
const data = await dataPromise;
const dataLength = data.length;
const initialData = new Uint8Array(data.subarray(0, initialDataLength));
subArrays.push(initialData);
const transport = new PDFDataRangeTransport(
dataLength,
initialData,
/* progressiveDone = */ undefined,
/* contentDispositionFilename = */ "BBB.PDF"
);
transport.requestDataRange = (begin, end) => {
fetches++; // There should be no range requests, since `disableRange` is used.
};
async function streamAllData() {
const streamChunkSize = 131072;
let pos = initialDataLength;
while (pos < dataLength) {
const begin = pos,
end = Math.min(pos + streamChunkSize, dataLength);
pos = end;
const chunk = new Uint8Array(data.subarray(begin, end));
subArrays.push(chunk);
transport.onDataProgressiveRead(chunk);
await streamDelay();
}
transport.onDataProgressiveDone();
}
streamAllData();
const loadingTask = getDocument({
range: transport,
rangeChunkSize: 65536,
disableRange: true,
});
loadingTask.onProgress = evt => {
initialProgress = evt;
loadingTask.onProgress = null;
};
const pdfDocument = await loadingTask.promise;
expect(pdfDocument.numPages).toEqual(14);
const pdfPage = await pdfDocument.getPage(10);
expect(pdfPage.rotate).toEqual(0);
const { contentDispositionFilename, contentLength } =
await pdfDocument.getMetadata();
expect(contentDispositionFilename).toEqual("BBB.PDF");
expect(contentLength).toEqual(dataLength);
expect(fetches).toEqual(0);
expect(initialProgress.loaded).toBeGreaterThan(initialDataLength);
expect(initialProgress.total).toEqual(dataLength);
expect(initialProgress.percent).toBeGreaterThan(8);
// Check that the TypedArrays were transferred.
for (const array of subArrays) {
expect(array.length).toEqual(0);
}
await loadingTask.destroy();
});
it("should fetch document info and page using ranges and streaming", async function () {
const initialDataLength = 80000; // Larger than `rangeChunkSize`, since otherwise it's pretty pointless.
const subArrays = [];
let initialProgress = null;
let fetches = 0;
const data = await dataPromise;
const dataLength = data.length;
const initialData = new Uint8Array(data.subarray(0, initialDataLength));
subArrays.push(initialData);
const transport = new PDFDataRangeTransport(
dataLength,
initialData,
/* progressiveDone = */ undefined,
/* contentDispositionFilename = */ ""
);
transport.requestDataRange = (begin, end) => {
fetches++;
waitSome(() => {
const chunk = new Uint8Array(data.subarray(begin, end));
subArrays.push(chunk);
transport.onDataRange(begin, chunk);
});
};
async function streamPartialData() {
const MAX_CHUNKS = 2;
let numChunks = 0;
const streamChunkSize = 131072;
let pos = initialDataLength;
while (pos < dataLength) {
const begin = pos,
end = Math.min(pos + streamChunkSize, dataLength);
pos = end;
const chunk = new Uint8Array(data.subarray(begin, end));
subArrays.push(chunk);
transport.onDataProgressiveRead(chunk);
if (++numChunks >= MAX_CHUNKS) {
break;
}
await streamDelay();
}
}
streamPartialData();
const loadingTask = getDocument({
range: transport,
rangeChunkSize: 65536,
});
loadingTask.onProgress = evt => {
initialProgress = evt;
loadingTask.onProgress = null;
};
const pdfDocument = await loadingTask.promise;
expect(pdfDocument.numPages).toEqual(14);
const pdfPage = await pdfDocument.getPage(10);
expect(pdfPage.rotate).toEqual(0);
const { contentDispositionFilename, contentLength } =
await pdfDocument.getMetadata();
expect(contentDispositionFilename).toEqual(null);
expect(contentLength).toEqual(dataLength);
expect(fetches).toBeGreaterThan(2);
expect(initialProgress).toEqual({
loaded: initialDataLength,
total: data.length,
percent: 0,
});
expect(initialProgress.loaded).toBeGreaterThan(initialDataLength);
expect(initialProgress.total).toEqual(dataLength);
expect(initialProgress.percent).toBeGreaterThan(8);
// Check that the TypedArrays were transferred.
for (const array of subArrays) {
@ -5248,35 +5425,30 @@ have written that much by now. So, heres to squashing bugs.`);
await loadingTask.destroy();
});
it("should fetch document info and page using range and streaming", async function () {
const initialDataLength = 4000;
it("should fetch document info and page, without ranges, using complete initialData", async function () {
const subArrays = [];
let initialProgress = null;
let fetches = 0;
const data = await dataPromise;
const initialData = new Uint8Array(data.subarray(0, initialDataLength));
const dataLength = data.length;
const initialData = new Uint8Array(data);
subArrays.push(initialData);
const transport = new PDFDataRangeTransport(data.length, initialData);
transport.requestDataRange = function (begin, end) {
fetches++;
if (fetches === 1) {
const chunk = new Uint8Array(data.subarray(initialDataLength));
subArrays.push(chunk);
// Send rest of the data on first range request.
transport.onDataProgressiveRead(chunk);
}
waitSome(function () {
const chunk = new Uint8Array(data.subarray(begin, end));
subArrays.push(chunk);
transport.onDataRange(begin, chunk);
});
const transport = new PDFDataRangeTransport(
dataLength,
initialData,
/* progressiveDone = */ true,
/* contentDispositionFilename = */ "pdf.txt"
);
transport.requestDataRange = (begin, end) => {
fetches++; // There should be no range requests, since `initialData` is complete.
};
const loadingTask = getDocument({ range: transport });
const loadingTask = getDocument({
range: transport,
rangeChunkSize: 65536,
});
loadingTask.onProgress = evt => {
initialProgress = evt;
loadingTask.onProgress = null;
@ -5287,16 +5459,18 @@ have written that much by now. So, heres to squashing bugs.`);
const pdfPage = await pdfDocument.getPage(10);
expect(pdfPage.rotate).toEqual(0);
expect(fetches).toEqual(1);
const { contentDispositionFilename, contentLength } =
await pdfDocument.getMetadata();
expect(contentDispositionFilename).toEqual(null);
expect(contentLength).toEqual(dataLength);
expect(fetches).toEqual(0);
expect(initialProgress).toEqual({
loaded: initialDataLength,
total: data.length,
percent: 0,
});
await new Promise(resolve => {
waitSome(resolve);
loaded: dataLength,
total: dataLength,
percent: 100,
});
// Check that the TypedArrays were transferred.
@ -5306,58 +5480,6 @@ have written that much by now. So, heres to squashing bugs.`);
await loadingTask.destroy();
});
it(
"should fetch document info and page, without range, " +
"using complete initialData",
async function () {
const subArrays = [];
let initialProgress = null;
let fetches = 0;
const data = await dataPromise;
const initialData = new Uint8Array(data);
subArrays.push(initialData);
const transport = new PDFDataRangeTransport(
data.length,
initialData,
/* progressiveDone = */ true
);
transport.requestDataRange = function (begin, end) {
fetches++;
};
const loadingTask = getDocument({
disableRange: true,
range: transport,
});
loadingTask.onProgress = evt => {
initialProgress = evt;
loadingTask.onProgress = null;
};
const pdfDocument = await loadingTask.promise;
expect(pdfDocument.numPages).toEqual(14);
const pdfPage = await pdfDocument.getPage(10);
expect(pdfPage.rotate).toEqual(0);
expect(fetches).toEqual(0);
expect(initialProgress).toEqual({
loaded: data.length,
total: data.length,
percent: 100,
});
// Check that the TypedArrays were transferred.
for (const array of subArrays) {
expect(array.length).toEqual(0);
}
await loadingTask.destroy();
}
);
});
describe("Annotations", function () {