From 294088fb273fc531b972d29ee39a52216a4d668b Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 31 May 2021 10:20:25 +0200 Subject: [PATCH] [ContentCacheManager] use PDF.js Xref table instead of stream detection (#242) * make the content cache manager tests configurable * extend stream content in unit tests * [ContentCacheManagerTests] prepare for full object caching * filesystem stream for pdfjs * working?? * cleaning up * handle overflow * [misc] install pdfjs-dist * [misc] move pdfjs code into app/lib/ and scripts/, also use CamelCase * [misc] abstract the file loading and parsing of xRef tables into helper * [misc] pdfjsTests: add snapshot based tests for the Xref table parser * [misc] FSStream: throw proper error and drop commented code * [misc] FSStream: integrate throwing of MissingDataException into getter * [misc] pdfjs: fix eslint errors * [misc] pdfjs: run format_fix * [misc] pdfjs: allocate very small non empty dummy buffers explicitly * [misc] install @overleaf/o-error * [ContentCacheManager] use PDF.js Xref table instead of stream detection Co-Authored-By: Brian Gough * [pdfjs] parseXrefTable: handle empty PDF files gracefully Co-authored-by: Brian Gough --- services/clsi/app/js/ContentCacheManager.js | 174 ++++---- services/clsi/app/js/OutputCacheManager.js | 57 +-- services/clsi/app/lib/pdfjs/FSPdfManager.js | 43 ++ services/clsi/app/lib/pdfjs/FSStream.js | 138 +++++++ services/clsi/app/lib/pdfjs/parseXrefTable.js | 24 ++ services/clsi/package-lock.json | 21 +- services/clsi/package.json | 4 +- services/clsi/scripts/demo-pdfjs-Xref.js | 12 + .../clsi/test/acceptance/fixtures/minimal.pdf | Bin 0 -> 12313 bytes .../test/unit/js/ContentCacheManagerTests.js | 371 ++++++------------ ...71616916a7ba0434fcfb56d1fc7e27cd139eaa2f71 | 7 + ...17923e3714927bbf35e63ea88bd93c7a8076cf1fcd | Bin 0 -> 10161 bytes services/clsi/test/unit/lib/pdfjsTests.js | 78 ++++ .../lib/snapshots/asymptote/XrefTable.json | 356 +++++++++++++++++ .../biber_bibliography/XrefTable.json | 128 ++++++ .../lib/snapshots/epstopdf/XrefTable.json | 125 ++++++ .../unit/lib/snapshots/feynmf/XrefTable.json | 115 ++++++ .../unit/lib/snapshots/feynmp/XrefTable.json | 102 +++++ .../lib/snapshots/fontawesome/XrefTable.json | 93 +++++ .../fontawesome_xelatex/XrefTable.json | 126 ++++++ .../lib/snapshots/glossaries/XrefTable.json | 94 +++++ .../unit/lib/snapshots/gnuplot/XrefTable.json | 89 +++++ .../unit/lib/snapshots/hebrew/XrefTable.json | 81 ++++ .../unit/lib/snapshots/knitr/XrefTable.json | 145 +++++++ .../lib/snapshots/knitr_utf8/XrefTable.json | 179 +++++++++ .../snapshots/latex_compiler/XrefTable.json | 132 +++++++ .../lualatex_compiler/XrefTable.json | 74 ++++ .../makeindex-custom-style/XrefTable.json | 107 +++++ .../lib/snapshots/makeindex/XrefTable.json | 90 +++++ .../unit/lib/snapshots/minted/XrefTable.json | 77 ++++ .../multibib_bibliography/XrefTable.json | 133 +++++++ .../lib/snapshots/nomenclature/XrefTable.json | 94 +++++ .../references_in_include/XrefTable.json | 90 +++++ .../simple_bibliography/XrefTable.json | 94 +++++ .../snapshots/subdirectories/XrefTable.json | 108 +++++ .../lib/snapshots/tikz_feynman/XrefTable.json | 145 +++++++ .../snapshots/xelatex_compiler/XrefTable.json | 73 ++++ 37 files changed, 3406 insertions(+), 373 deletions(-) create mode 100644 services/clsi/app/lib/pdfjs/FSPdfManager.js create mode 100644 services/clsi/app/lib/pdfjs/FSStream.js create mode 100644 services/clsi/app/lib/pdfjs/parseXrefTable.js create mode 100644 services/clsi/scripts/demo-pdfjs-Xref.js create mode 100644 services/clsi/test/acceptance/fixtures/minimal.pdf create mode 100644 services/clsi/test/unit/js/snapshots/minimalCompile/chunks/896749b8343851b0dc385f71616916a7ba0434fcfb56d1fc7e27cd139eaa2f71 create mode 100644 services/clsi/test/unit/js/snapshots/minimalCompile/chunks/d7cfc73ad2fba4578a437517923e3714927bbf35e63ea88bd93c7a8076cf1fcd create mode 100644 services/clsi/test/unit/lib/pdfjsTests.js create mode 100644 services/clsi/test/unit/lib/snapshots/asymptote/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/biber_bibliography/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/epstopdf/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/feynmf/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/feynmp/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/fontawesome/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/fontawesome_xelatex/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/glossaries/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/gnuplot/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/hebrew/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/knitr/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/knitr_utf8/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/latex_compiler/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/lualatex_compiler/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/makeindex-custom-style/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/makeindex/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/minted/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/multibib_bibliography/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/nomenclature/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/references_in_include/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/simple_bibliography/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/subdirectories/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/tikz_feynman/XrefTable.json create mode 100644 services/clsi/test/unit/lib/snapshots/xelatex_compiler/XrefTable.json diff --git a/services/clsi/app/js/ContentCacheManager.js b/services/clsi/app/js/ContentCacheManager.js index d424681615..ab73c61d93 100644 --- a/services/clsi/app/js/ContentCacheManager.js +++ b/services/clsi/app/js/ContentCacheManager.js @@ -7,45 +7,97 @@ const fs = require('fs') const crypto = require('crypto') const Path = require('path') const Settings = require('settings-sharelatex') +const OError = require('@overleaf/o-error') const pLimit = require('p-limit') - -const MIN_CHUNK_SIZE = Settings.pdfCachingMinChunkSize - -const START_OF_STREAM_MARKER = 'stream' -const END_OF_STREAM_MARKER = 'endstream' -const START_OF_STREAM_MARKER_LENGTH = START_OF_STREAM_MARKER.length -const END_OF_STREAM_MARKER_LENGTH = END_OF_STREAM_MARKER.length +const { parseXrefTable } = require('../lib/pdfjs/parseXrefTable') /** * * @param {String} contentDir path to directory where content hash files are cached * @param {String} filePath the pdf file to scan for streams + * @param {number} size the pdf size */ -async function update(contentDir, filePath) { - const stream = fs.createReadStream(filePath) - const extractor = new PdfStreamsExtractor() +async function update(contentDir, filePath, size) { const ranges = [] const newRanges = [] // keep track of hashes expire old ones when they reach a generation > N. const tracker = await HashFileTracker.from(contentDir) tracker.updateAge() - for await (const chunk of stream) { - const pdfStreams = extractor.consume(chunk) - for (const pdfStream of pdfStreams) { - if (pdfStream.end - pdfStream.start < MIN_CHUNK_SIZE) continue - const hash = pdfStreamHash(pdfStream.buffers) + const rawTable = await parseXrefTable(filePath, size) + rawTable.sort((a, b) => { + return a.offset - b.offset + }) + rawTable.forEach((obj, idx) => { + obj.idx = idx + }) - const range = { start: pdfStream.start, end: pdfStream.end, hash } + const uncompressedObjects = [] + for (const object of rawTable) { + if (!object.uncompressed) { + continue + } + const nextObject = rawTable[object.idx + 1] + if (!nextObject) { + // Ignore this possible edge case. + // The last object should be part of the xRef table. + continue + } else { + object.endOffset = nextObject.offset + } + const size = object.endOffset - object.offset + object.size = size + if (size < Settings.pdfCachingMinChunkSize) { + continue + } + uncompressedObjects.push(object) + } + + const handle = await fs.promises.open(filePath) + try { + for (const object of uncompressedObjects) { + let buffer = Buffer.alloc(object.size, 0) + const { bytesRead } = await handle.read( + buffer, + 0, + object.size, + object.offset + ) + if (bytesRead !== object.size) { + throw new OError('could not read full chunk', { + object, + bytesRead + }) + } + const idxObj = buffer.indexOf('obj') + if (idxObj > 100) { + throw new OError('objectId is too large', { + object, + idxObj + }) + } + const objectIdRaw = buffer.subarray(0, idxObj) + buffer = buffer.subarray(objectIdRaw.byteLength) + + const hash = pdfStreamHash(buffer) + const range = { + objectId: objectIdRaw.toString(), + start: object.offset + objectIdRaw.byteLength, + end: object.endOffset, + hash + } ranges.push(range) // Optimization: Skip writing of duplicate streams. if (tracker.track(range)) continue - await writePdfStream(contentDir, hash, pdfStream.buffers) + await writePdfStream(contentDir, hash, buffer) newRanges.push(range) } + } finally { + await handle.close() } + const reclaimedSpace = await tracker.deleteStaleHashes(5) await tracker.flush() return [ranges, newRanges, reclaimedSpace] @@ -142,94 +194,21 @@ class HashFileTracker { } } -class PdfStreamsExtractor { - constructor() { - this.fileIndex = 0 - this.inStream = false - this.streamStartIndex = 0 - this.buffers = [] - this.lastChunk = Buffer.alloc(0) - } - - consume(chunk) { - let chunkIndex = 0 - const pdfStreams = [] - chunk = Buffer.concat([this.lastChunk, chunk]) - while (true) { - if (!this.inStream) { - // Not in a stream, look for stream start - const index = chunk.indexOf(START_OF_STREAM_MARKER, chunkIndex) - if (index === -1) { - // Couldn't find stream start - break - } - // Found stream start, start a stream - this.inStream = true - this.streamStartIndex = this.fileIndex + index - chunkIndex = index - } else { - // In a stream, look for stream end - const index = chunk.indexOf(END_OF_STREAM_MARKER, chunkIndex) - if (index === -1) { - break - } - // add "endstream" part - const endIndex = index + END_OF_STREAM_MARKER_LENGTH - this.buffers.push(chunk.slice(chunkIndex, endIndex)) - pdfStreams.push({ - start: this.streamStartIndex, - end: this.fileIndex + endIndex, - buffers: this.buffers - }) - this.inStream = false - this.buffers = [] - chunkIndex = endIndex - } - } - - const remaining = chunk.length - chunkIndex - const nextMarkerLength = this.inStream - ? END_OF_STREAM_MARKER_LENGTH - : START_OF_STREAM_MARKER_LENGTH - if (remaining > nextMarkerLength) { - const retainMarkerSection = chunk.length - nextMarkerLength - if (this.inStream) { - this.buffers.push(chunk.slice(chunkIndex, retainMarkerSection)) - } - this.lastChunk = chunk.slice(retainMarkerSection) - this.fileIndex += retainMarkerSection - } else { - this.lastChunk = chunk.slice(chunkIndex) - this.fileIndex += chunkIndex - } - return pdfStreams - } -} - -function pdfStreamHash(buffers) { +function pdfStreamHash(buffer) { const hash = crypto.createHash('sha256') - for (const buffer of buffers) { - hash.update(buffer) - } + hash.update(buffer) return hash.digest('hex') } -async function writePdfStream(dir, hash, buffers) { +async function writePdfStream(dir, hash, buffer) { const filename = Path.join(dir, hash) const atomicWriteFilename = filename + '~' - const file = await fs.promises.open(atomicWriteFilename, 'w') if (Settings.enablePdfCachingDark) { // Write an empty file in dark mode. - buffers = [] + buffer = Buffer.alloc(0) } try { - try { - for (const buffer of buffers) { - await file.write(buffer) - } - } finally { - await file.close() - } + await fs.promises.writeFile(atomicWriteFilename, buffer) await fs.promises.rename(atomicWriteFilename, filename) } catch (err) { try { @@ -247,5 +226,8 @@ function promiseMapWithLimit(concurrency, array, fn) { module.exports = { HASH_REGEX: /^[0-9a-f]{64}$/, - update: callbackify(update) + update: callbackify(update), + promises: { + update + } } diff --git a/services/clsi/app/js/OutputCacheManager.js b/services/clsi/app/js/OutputCacheManager.js index e6167570f8..4d2e64132b 100644 --- a/services/clsi/app/js/OutputCacheManager.js +++ b/services/clsi/app/js/OutputCacheManager.js @@ -275,36 +275,39 @@ module.exports = OutputCacheManager = { outputDir, OutputCacheManager.path(outputFile.build, outputFile.path) ) + const pdfSize = outputFile.size const timer = new Metrics.Timer('compute-pdf-ranges') - ContentCacheManager.update(contentDir, outputFilePath, function ( - err, - result - ) { - if (err) return callback(err, outputFiles) - const [contentRanges, newContentRanges, reclaimedSpace] = result + ContentCacheManager.update( + contentDir, + outputFilePath, + pdfSize, + function (err, result) { + if (err) return callback(err, outputFiles) + const [contentRanges, newContentRanges, reclaimedSpace] = result - if (Settings.enablePdfCachingDark) { - // In dark mode we are doing the computation only and do not emit - // any ranges to the frontend. - } else { - outputFile.contentId = Path.basename(contentDir) - outputFile.ranges = contentRanges + if (Settings.enablePdfCachingDark) { + // In dark mode we are doing the computation only and do not emit + // any ranges to the frontend. + } else { + outputFile.contentId = Path.basename(contentDir) + outputFile.ranges = contentRanges + } + + timings['compute-pdf-caching'] = timer.done() + stats['pdf-caching-n-ranges'] = contentRanges.length + stats['pdf-caching-total-ranges-size'] = contentRanges.reduce( + (sum, next) => sum + (next.end - next.start), + 0 + ) + stats['pdf-caching-n-new-ranges'] = newContentRanges.length + stats['pdf-caching-new-ranges-size'] = newContentRanges.reduce( + (sum, next) => sum + (next.end - next.start), + 0 + ) + stats['pdf-caching-reclaimed-space'] = reclaimedSpace + callback(null, outputFiles) } - - timings['compute-pdf-caching'] = timer.done() - stats['pdf-caching-n-ranges'] = contentRanges.length - stats['pdf-caching-total-ranges-size'] = contentRanges.reduce( - (sum, next) => sum + (next.end - next.start), - 0 - ) - stats['pdf-caching-n-new-ranges'] = newContentRanges.length - stats['pdf-caching-new-ranges-size'] = newContentRanges.reduce( - (sum, next) => sum + (next.end - next.start), - 0 - ) - stats['pdf-caching-reclaimed-space'] = reclaimedSpace - callback(null, outputFiles) - }) + ) } else { callback(null, outputFiles) } diff --git a/services/clsi/app/lib/pdfjs/FSPdfManager.js b/services/clsi/app/lib/pdfjs/FSPdfManager.js new file mode 100644 index 0000000000..a0450c1e85 --- /dev/null +++ b/services/clsi/app/lib/pdfjs/FSPdfManager.js @@ -0,0 +1,43 @@ +const { PDFDocument } = require('pdfjs-dist/lib/core/document') +const { LocalPdfManager } = require('pdfjs-dist/lib/core/pdf_manager') +const { MissingDataException } = require('pdfjs-dist/lib/core/core_utils') +const { FSStream } = require('./FSStream') + +class FSPdfManager extends LocalPdfManager { + constructor(docId, options) { + const nonEmptyDummyBuffer = Buffer.alloc(1, 0) + super(docId, nonEmptyDummyBuffer) + this.stream = new FSStream(options.fh, 0, options.size) + this.pdfDocument = new PDFDocument(this, this.stream) + } + + async ensure(obj, prop, args) { + try { + const value = obj[prop] + if (typeof value === 'function') { + return value.apply(obj, args) + } + return value + } catch (ex) { + if (!(ex instanceof MissingDataException)) { + throw ex + } + await this.requestRange(ex.begin, ex.end) + return this.ensure(obj, prop, args) + } + } + + requestRange(begin, end) { + return this.stream.requestRange(begin, end) + } + + requestLoadedStream() {} + + onLoadedStream() {} + + terminate(reason) {} +} + +module.exports = { + FSPdfManager +} diff --git a/services/clsi/app/lib/pdfjs/FSStream.js b/services/clsi/app/lib/pdfjs/FSStream.js new file mode 100644 index 0000000000..9179e83f0e --- /dev/null +++ b/services/clsi/app/lib/pdfjs/FSStream.js @@ -0,0 +1,138 @@ +const { Stream } = require('pdfjs-dist/lib/core/stream') +const { MissingDataException } = require('pdfjs-dist/lib/core/core_utils') + +const BUF_SIZE = 1024 // read from the file in 1024 byte pages + +class FSStream extends Stream { + constructor(fh, start, length, dict, cachedBytes) { + const nonEmptyDummyBuffer = Buffer.alloc(1, 0) + super(nonEmptyDummyBuffer, start, length, dict) + delete this.bytes + this.fh = fh + this.cachedBytes = cachedBytes || [] + } + + get length() { + return this.end - this.start + } + + get isEmpty() { + return this.length === 0 + } + + // Manage cached reads from the file + + requestRange(begin, end) { + // expand small ranges to read a larger amount + if (end - begin < BUF_SIZE) { + end = begin + BUF_SIZE + } + end = Math.min(end, this.length) + // keep a cache of previous reads with {begin,end,buffer} values + const result = { + begin: begin, + end: end, + buffer: Buffer.alloc(end - begin, 0) + } + this.cachedBytes.push(result) + return this.fh.read(result.buffer, 0, end - begin, begin) + } + + _ensureGetPos(pos) { + const found = this.cachedBytes.find((x) => { + return x.begin <= pos && pos < x.end + }) + if (!found) { + throw new MissingDataException(pos, pos + 1) + } + return found + } + + _ensureGetRange(begin, end) { + end = Math.min(end, this.length) // BG: handle overflow case + const found = this.cachedBytes.find((x) => { + return x.begin <= begin && end <= x.end + }) + if (!found) { + throw new MissingDataException(begin, end) + } + return found + } + + _readByte(found, pos) { + return found.buffer[pos - found.begin] + } + + _readBytes(found, pos, end) { + return found.buffer.subarray(pos - found.begin, end - found.begin) + } + + // handle accesses to the bytes + + ensureByte(pos) { + this._ensureGetPos(pos) // may throw a MissingDataException + } + + getByte() { + const pos = this.pos + if (this.pos >= this.end) { + return -1 + } + const found = this._ensureGetPos(pos) + return this._readByte(found, this.pos++) + } + + // BG: for a range, end is not included (see Buffer.subarray for example) + + ensureBytes(length, forceClamped = false) { + const pos = this.pos + this._ensureGetRange(pos, pos + length) + } + + getBytes(length, forceClamped = false) { + const pos = this.pos + const strEnd = this.end + + const found = this._ensureGetRange(pos, pos + length) + if (!length) { + const subarray = this._readBytes(found, pos, strEnd) + // `this.bytes` is always a `Uint8Array` here. + return forceClamped ? new Uint8ClampedArray(subarray) : subarray + } + let end = pos + length + if (end > strEnd) { + end = strEnd + } + this.pos = end + const subarray = this._readBytes(found, pos, end) + // `this.bytes` is always a `Uint8Array` here. + return forceClamped ? new Uint8ClampedArray(subarray) : subarray + } + + getByteRange() { + // BG: this isn't needed as far as I can tell + throw new Error('not implemented') + } + + reset() { + this.pos = this.start + } + + moveStart() { + this.start = this.pos + } + + makeSubStream(start, length, dict = null) { + // BG: had to add this check for null length, it is being called with only + // the start value at one point in the xref decoding. The intent is clear + // enough + // - a null length means "to the end of the file" -- not sure how it is + // working in the existing pdfjs code without this. + if (!length) { + length = this.end - start + } + return new FSStream(this.fh, start, length, dict, this.cachedBytes) + } +} + +module.exports = { FSStream } diff --git a/services/clsi/app/lib/pdfjs/parseXrefTable.js b/services/clsi/app/lib/pdfjs/parseXrefTable.js new file mode 100644 index 0000000000..4db1e0cfc7 --- /dev/null +++ b/services/clsi/app/lib/pdfjs/parseXrefTable.js @@ -0,0 +1,24 @@ +const fs = require('fs') +const { FSPdfManager } = require('./FSPdfManager') + +async function parseXrefTable(path, size) { + if (size === 0) { + return [] + } + + const file = await fs.promises.open(path) + try { + const manager = new FSPdfManager(0, { fh: file, size }) + + await manager.ensureDoc('checkHeader') + await manager.ensureDoc('parseStartXRef') + await manager.ensureDoc('parse') + return manager.pdfDocument.catalog.xref.entries + } finally { + file.close() + } +} + +module.exports = { + parseXrefTable +} diff --git a/services/clsi/package-lock.json b/services/clsi/package-lock.json index 773d9f4319..52571c6cc7 100644 --- a/services/clsi/package-lock.json +++ b/services/clsi/package-lock.json @@ -1008,9 +1008,19 @@ } }, "@overleaf/o-error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-3.0.0.tgz", - "integrity": "sha512-LsM2s6Iy9G97ktPo0ys4VxtI/m3ahc1ZHwjo5XnhXtjeIkkkVAehsrcRRoV/yWepPjymB0oZonhcfojpjYR/tg==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-3.3.1.tgz", + "integrity": "sha512-1FRBYZO0lbJ0U+FRGZVS8ou6RhEw3e2B86WW/NbtBw554g0h5iC8ESf+juIfPMU/WDf/JDIFbg3eB/LnP2RSow==", + "requires": { + "core-js": "^3.8.3" + }, + "dependencies": { + "core-js": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.13.0.tgz", + "integrity": "sha512-iWDbiyha1M5vFwPFmQnvRv+tJzGbFAm6XimJUT0NgHYW3xZEs1SkCAcasWSVFxpI2Xb/V1DDJckq3v90+bQnog==" + } + } }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -5594,6 +5604,11 @@ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, + "pdfjs-dist": { + "version": "2.7.570", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.7.570.tgz", + "integrity": "sha512-/ZkA1FwkEOyDaq11JhMLazdwQAA0F9uwrP7h/1L9Akt9KWh1G5/tkzS+bPuUELq2s2GDFnaT+kooN/aSjT7DXQ==" + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", diff --git a/services/clsi/package.json b/services/clsi/package.json index dac3ba9dbb..51870b025a 100644 --- a/services/clsi/package.json +++ b/services/clsi/package.json @@ -10,7 +10,7 @@ "start": "node $NODE_APP_OPTIONS app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", "test:acceptance": "npm run test:acceptance:_run -- --grep=$MOCHA_GREP", - "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js test/unit/lib", "test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP", "nodemon": "nodemon --config nodemon.json", "lint": "node_modules/.bin/eslint --max-warnings 0 .", @@ -20,6 +20,7 @@ "author": "James Allen ", "dependencies": { "@overleaf/metrics": "^3.5.1", + "@overleaf/o-error": "^3.3.1", "async": "3.2.0", "body-parser": "^1.19.0", "diskusage": "^1.1.3", @@ -32,6 +33,7 @@ "logger-sharelatex": "^2.2.0", "mysql": "^2.18.1", "p-limit": "^3.1.0", + "pdfjs-dist": "^2.7.570", "request": "^2.88.2", "sequelize": "^5.21.5", "settings-sharelatex": "^1.1.0", diff --git a/services/clsi/scripts/demo-pdfjs-Xref.js b/services/clsi/scripts/demo-pdfjs-Xref.js new file mode 100644 index 0000000000..86be134ef5 --- /dev/null +++ b/services/clsi/scripts/demo-pdfjs-Xref.js @@ -0,0 +1,12 @@ +const fs = require('fs') +const { parseXrefTable } = require('../app/lib/pdfjs/parseXrefTable') + +const pdfPath = process.argv[2] + +async function main() { + const size = (await fs.promises.stat(pdfPath)).size + const xRefEntries = await parseXrefTable(pdfPath, size) + console.log('Xref entries', xRefEntries) +} + +main().catch(console.error) diff --git a/services/clsi/test/acceptance/fixtures/minimal.pdf b/services/clsi/test/acceptance/fixtures/minimal.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d578e90567e15149674581235b0d67a2f9340924 GIT binary patch literal 12313 zcmch-Wl)^awyul2y9F9|Y24l2f&`bw-5r9vy9WvG?m>gQI|&xtT@G1muU+fhwNKUk zaqexYuV>FO=N#`lMtxmf^-wB{OELnPI1ng%?hD=!*Z?d5dt)mEetrP6yrmt;$jQ`eb|2l)T{fx!N6LexDSKmcYfRgl?-43M3I$jR0j@Ye(}dmC3U$Hf1M}sildFz0-%h4{ZQu%?~Gl0M?K9hYvD#rXY`x zFW3MdCjDbSU066~nS?$A60Fd(^+qwP2D7ODFhgn7(pu-R3-~@rp zc(_?jO+j2HCY&ZfP8K5*6Cevan+Z3Ev5_D@7nhMShY`COrzr=Q8M`qE#L4n831Ve8 zVFPlQaT^Qj{jGtsixbGm7QrLi#MpG(+|byN8=fNld!Ux2a9|z803A$SDj5826_R|k z=7NYZQnUpWSid(k{om64*UJA}MMU^(!N0mo*~lE^{MW00IP}jFi5a;V+1Q)^TjOy4 z+lv3OUTOfg4=?{!Rkr_Kuc4u#k)dJc6d2SO68vqjQ8P2Jv3_vGY=iM1u*s>N)2$KU zU)*~5v$t~r*||6axc`}}!bckziQ4}yHp|D8gNK8Om5UR=%?@PZ=J{Lt zkM8-1|8H%oAZL45CzHQw|EnMVsrIh}|0ebC?*B69f0X)PB6VX{29TK zQlr5X!NL7fg?7N;Ve{FBXNe$`f$mMvy!_`DmMAb5qKqQKnl}XbtAc)$mh`wU)Q0gaZQnZ-o)-4f$p#nx;YyG-Gc8fFI=MgyHHwR z8T7z$gbC509)(u&CWMcBF0S|Zg80EhiKbS6Lpi*>y?TRQ7?|m4piXW{p7NeSnks&U z`7{$Rnm^y~WMoAJzy&M$iNG|N^ijYL>=Q19n0?;)V%m8Q&spElE5PbPA)wyM?;g|N zKG!{OK^Q)GbK`-(=g46{Rek{k>PG3-q3*oImi&Mz9``QEeD9T zp}gep-70=eU>z^^b0jw%5N;Lz!ir#g7bF)P0iRw-9x6@T4tq%Bey|0;1Wk-U8oIy1 z`rLWHe0^UCD9&oQzD9aaSax=Q@AD@{0!(9CM0BzsHen&uRUlh-yith!>TMUGW0mNr z${HFzYS>;sbBl^i@6*b`Z`}8xO<-LPY3rozzmW4u_|eCe(W6pNVZGOdx;ZW?gk6y)w&fNu=54t*_`&N|+`BhY&#pKOwf%RV^RtS{cF zU9pYHu@aO@T``j0`H+A@!c5>_Pq_;9xr za6Lp2=2*9mi*1u0`-?8qCvkRhm|# z2IKluN055Zrnm?}hxK|GZnuNo-ghw3LzyRs=0q!5LPv$1viXa09fd*%UF+73Dr<+Mk2=ab@5b}k8)`(%)U7yw$c&d<4ZtL_zy6oGCZHQJ+ZWb7Ejo}ry^)bc)-lzuqyjKNcxTOJ6BlU;5S5L&7{OZfphw@jc>CS}#LH*ktK3pd( ze^58qTg>aaQ*KMj4Hir4_J0sB5A`bb`jji-7;NR`;-IZKjirM^fxDn#m+mp0n8GEr za_B`R*tEL5YRz8_GHG5aG}Lw%lXnh_rwe!gxSWM5Jb8z+2_6;d)(-QV#JOJ zLFn+`J4NdWs?q?>`A>T}e(dM;_NsON2aBGY*Ct$hW;43B37bfHRZ>Vw& zRE#*^d@rng4sw|(&1cn^lptpf6t74VEfCce__DGUc(}e+jzfzno20*jA0L=~6CwvS zx|ZsrCGHVxU}21Zzd#Z=6SmboS|KEIwaefiMbqddVDPHE{pkqrA`xxH`4E!(g?3N{ zPhyD`uicKsFW94-!1PEs=kn|_wm(Dr5kIjgiso*X)MbT-5S31QbC)}gZ2~T{WkKny zGV;#M#>_np4W^kNa-a5YHc_#C!fal!&iyCFBLe$AX6TR#;mloaBTL#NG0u66p6^G8tt$|`Sz#CPr)_7>N1o(M zM&z_t$Mh(5E>w4HYGuUjETavSF) zldOxaC>D}cSlUb7c=)LFOUL$P>h4z~R9CF(t&*U;4qFDx`iTVqv}F3%*LUfOu{vz+MIpw8u0`C+c22#FP#1hv8=wX zsw3Nt%BfTWPB4KOOvRNAry+1uVs(Rg5ia3kCztJM^375{Obr<=;akNTD4|HG= zAYI2giTTBGND~lf5vY)ic7g_N6N7^-SqGNkvQFjM#^d)l|2W+aaID;qk*NQ=E z``XK_3&b@Ykjc}|=;}hl^4)5Rfg+TLeh&i~I|tFiT*MEu!=8t-1f7`g#cwxb0^Ij7 z_}l}DhYzjNL?1 z8$?$cH93=buO|+l`(t?GV5oabjD&>SeuO3|8mV16kOF1~2i_hEqdwcfKyC1S!-TJ6 z^=}b&NLA-q%Npj!Ic^-sr>ws)(XQ<%ze%IP=J5k3fCf#I>m?3!B9FMkrB5Mp>Rx`% zuKe=aRy$j9Dt4Oy(WC_r-$#01OYy!Sb0O8f@nrpa+Q$17qJp0mIo)b*DcwWj4O1%j#&BZ_aZjWgbkym!0^gT)^V_@7uz5k3_r#T1 z`4XYc8Z~=feaH?)ZBRxNC}>89a6 z-ODqtotTVK3=E{NPTeiR6WXfVpOA@*WDLqxBd?o(HjT?>Kb7xtjJI}KJ-8&#QZKL5 zetU@x`a|K6f5Hd}G3wKJBw>nPIsJ4mTJbZoy2E67Kz96@<9oC8PIp0P@`QdiS~e?p zS}Ym(j#nEuJ1u^La?Wd9^YLkrK%)nNOksV9^6g!241A@be`fuITRFVlSu?%sSDvrr zL3p@d9kjd}CS;u&$qytp2t^il`wGF+$_!pdVz7R}E%ugXP?@&Ve{<30CtfZ#v`_w& zY00#Knf)a$WxAW313aqa#g?wAfGGPL`i32qw#{$sJ2JMKdW)D$P4u7b%kD^R2a$BDszvh!9Ll#=D~f%tKMhi$lOg!`kWW2cju15YumqXs5?>O3WGV92Ty5Gsis7N;Sm|V)CT44w0jt4>Zz9Wxs}- zI@-2|gaDe06^V~DBa1bRIho7mT9s}hC@&Xr6Ui0VF$t;Th>h2VS>C>9Kt|v*0XD=_xMtfnrC-S*<1Ww;>hVaX7eQww#37d6kF9L@I=`;{{ zO|ZjVSr2yL^;aW6r`XoKz?v=N+{jD~9FThKO_rs97m5Rnebtk;Fql8%(xX)0(N38G zl&PE#ZYyYBD#(ka**IVxzt&$q{|Yu99~O!sNu!Yot;xLt_a@*?qbq7m9& zrvHzGjirBm7mn|>CFghLW`|BpcvHVI#%b(IhQW-YCCKehq<(QH$t+91*|qU#nz1>~ zCTNRC)6iKjc^3^=~btf*K=bfLYQ%9m6wQ+*@L54*);HRmtWCc_8$p? zk#DiOF;CQFN{yCiR1>l*eZFoTw0QPAJzY6{(+&)%Ku*tpNiENFashJ|<3X+nV6W=Y zV@(k&67MkArcv8)5%)_YC2M1;G?d54kNDvUo!^~q)mtw?!Z{D8*339(zJYQz=i+z6 zdH`KodOVgg8I72L*cnaA;it_jl8+ihf=xF`7~fjY*()6Flam36OkDW1&P~H;kNbx1 zJO@36&phnRgY6ksCFkPk)7;K$KmO*sK-MZ1&eW9o#l!uwzr@*)zwmY-wbDIQ+r+%8wDcwJuiEt@MUq*(9H8|s-2D`cWPN?xtUz+nXQ!HCGa^6wqphMudWFIA_2L!{#|+`@@6&S&eivOmmG(BX z=Vfs~5%~1n!Y0B$CCTqj7{yfY!R*`^azOPOqP5c)ld z0@>tEfVYV&>)hg7KkY`MpBW!Q__R~B5xXF`?|c2bLY+F!1kaa}y_TjEl&++pGL9NC z6I7(G1u&C;c&>?Ycyl*Y4w2wz#V)m!rqpb7rdnoCscAB)uE$sTGUl{rc=C0<(2!_E zo7daPRP)77H)+!u^|bD@o&`YdO*!|JhTJg#)iWR#Anr#@UAy^tW5~CoG+Ixq?rTTk zC4GdBFHt5@CgQnR3S#C2-SW-@NTmqxH`z&b->L9_OAzLlH_godU=-CVc5h``+z+To z_lTL|9sJ}Z0kfK&gliCs+DMG(S8MOp@0Naf+?ykqzx5S4%TL_7L9X4^1N_8QNn|)L z_#OT8`%>ln!Ki-Nt5;H}6%UubS{u^9@e}l90Uwg)-Jxaj&)f8!7`0v3Tz8J_lz6x~ zK1&q?DX9HV$E<|lIt2A=iTYY5mzXJp4W3COW<14mq?=B`5OHt)i)lItlZwXfRp>$`}gLKz!h^KoC+kH7DMj=(65-Eez^9StxUjB2gYuu9M+Lu)i4 z<30#Fh_RW_1R`{9&`wD@vm+QnHbR_KOW=!a@xB+k4l!qt46;>kC?PX{p>+0B&~=wS z9ZQj5(~8j*9c~(}i`L?EcN67#zRX4H=8qZTd>%r?EK9)RC-{jYEcdp92AnlrJy5i*hy)H|D+2tKy!IsTIZ@J8wWqZw)W zh7sm|)|E#+;(?qCsr_AwQqN7kd}q)#8ib8O$2X;ppkMBf32WV5Z@GAU)a}R@;eXcR z9nzNBQQ$D!QN1{{#`EW&$>>0Ee6o#}_qV_h@`@{{4dJ&>>k)|=G(ReB`0CqpA<@WiRGh( z{MM9Vx%v~=uT(Q6BI5T;fmF0FH140P_=gi*M#>b77xibsStI-4l{Q@xb+2dt%~l&+ z==Bvun!{K5#!2Rh?0_{70`FFnA#Ix3Bpy1&K_WxG2gMcOrov!RYtcyDW?iY0evEiK ztiN$fu%skc8=$Tvd{nm^^RlT88{QjTXIHb_YLgQ=&%27ME>&=m?})OGZtq@+zi?2< zZIIP)=#V+Cy50T-1Yha0n{2L~?tZCvLAOhcxTvt4C7W~9Zh)fSw<>vWtiHaE7q96N zCY{zES?FW{UMFO_dr~$Y6}1XPI9|=PEo+nr7o6}V^0|6O(G^-u(8%_D?k<)ZF!?junvhMnmeyhhqaE2mDwK%a>LR3Dc7h_ z9SI+PQXuAOr6j~UlI^Mi*%8hkBg8@87{O|nz3Z|_2piB~x9#!GfCbt8dI%K4qOJZ% z_RvwdI6;!5oFE6`;A)Os=qD}jKqFM#-~mVp&h2xaG8)G%uO_!uFg>1*?UJiGI>ef1 zWfrKI(Drxg^1hIDmm&L1aGoJ0YKX^!U~RrQUMc6uc8px_TV=QCj8udtBx>dTS!Or? zo?RBZs9n0dfS=F&`=m9F6yVrAb7ut~6R9NUl`~SGxvpD?UtEz$%o`_334Ab|Ou*Fr zQT0aOLHXy+*Tj%dKY@i6hHASe9K*d`_@YSTB^-=j`a&skwfO&rbJT7 zjEC?gJdyj=kc<}M=+7mb+zDG3+25g;s!mD~vntXq$!ySbo>UOn)^zhNDkpwL49hw= zy*Os6r`U50^&6ZdZ`eU1O?K&RhQd7V-_uOZTmBNqL4eUpo8WH5c9(I;8<@zoDsZ?B zN244k@N2fpR%+KAXaDjRt)wsG6>IO=D2?afA9RVclXZ;CHyP256rp$UQ@XP}Hlkdr zz)0er35PPWrM71BSoEi=i^lKYYzcE`Z0w>(NNJiOO?h zda3if*l-;vXD>*EYN-kf0&7W+D`ra8J+`2+fdcgwTzI31Z>bS5Xs2J59lL&pFVe7B;{wmX!*;z$2Vy-n+(lf ze@0|e>s5s4OK@}!M4*ixl%9#05qIdBckoI$=S(O`FVPn!G}l&rQjR6g*Lp(^0)4?mSf8B=%_OpgAfGw1SwJVHd~F)1dg>m9Qc*W zlUO;P8P%|In`PMg&eV$w6|yOst_vuL7d@Lq8|1>c3)JByI5naksx@>q!e{h>=j57T z4rZL!1DO8VQn|j-M_JTPNWs<$*g*vhnMo8<{*T{(FaN%Ex(&*FB~-uqBc__QalQ?i z(f!G~FB5eI>E@a4RMvY*%87Yx#~yQDaBH|S&4<_?-O{j0V$*=UICEKe>)3>WLSB1? zook$w>tIHGm%V+rz|Nb_dDvGw@>?5WvC7Xhd-JlIar1yZuleeNyLbySI;<& z2tlg~Wx;fU=G~2!IClPf=_1D17?S1-ad^9#H#c?je$*6NI`%uM*J}Gz$~V*Xro5ErKZhJFqrbO!;@PYy}?JM6~TLD8L5=Y6NS2VW4rDB}R+ay|geu3d4y@T&*1>wzojrD&=jR zIxUIw*5F;b!aP&`f>7gRC~z%cwnf4qqd(SlH@&wdi6I49Vvn*@Usom$*WO{M=+JoF z_WamYp_m#)(G!0q5fu`ZvK?d~If7hVYtxwN)^!;^wTo%3-WoFosJhYP8)>s4kCyXv ze&qTbBf3x#Hu*amw`0^*nco7CQur;o2RHh8x;g2B6>6U9eR6DztfaK}ZF6zb60zC) zjN+YjGgnGZr2-9O=YS$l*l7-PN#CkOF;zqO@Y=euboxahaC8m#d(iHLP*5zwZ*j3R z&d0Tot7WspWr2OG`%(ca3WANL7DP5P5Qub)}ev5JeqqFIukB$oQ&g)`h!= zPhs+Xf`4^z)UP(M>*!U4h^vBKgi~d<$h>3L&qCkl#MN1?;`jvSeD&VbF{b=7VRN88 z67dK(SKui&gpp`tYj8NOqL`M2H;C26hL?&O-OuYtU?CX>J2n)O%Vwl1b02$avaI4L zX0|Y*D(WoWs}#;hXe!T%vE-G3p9x}FOps#wHMjN<)V|ODkx}%5-4D6pj`)bL^p;G* z1?`cnnIhKm58D1sht?l<=B_) z+MABhACTCSMVm!$u{9cN7&UJ2A?>QV>my+~zF6yha-2!GG{^}*5s1bjEV1oA5JgIE zPTgaKRXN7T9uG$UBgC8SKyN*Esrht7t+v+rK7tN=V87Q%tBNrsB&@{-6B#JPO*xZn z<78r|liabzk1EZ`@xmfIcQ_~cA{}f`;44KAcTc7DZU$HU-a8efC}&(o%XU!yYo018 zPwb(cQs4kWmXew%qMy<%yLU0Bdv+8}$GBe1XmSEmhMGVRYbB}nHgQjDS=L%%$WW;w zp|ZU5d;eV)p_1tU3&|iYg#u3{L05od9gD7S7P(&Et0Zf9bcUMXpU#XqfBBi%O1R^ffzBiTt0%XKZyL5bF-sXJ22j4QH(>n9{O z*ivU4wykbS#fQDS25yF!l=ZCxtLOsD0wnG~8HNO zQDL=8(IN(GIPu66c3va~ELwD!N$J`&jkXmmO8QZBx+V3$iK8GxwtUkwp{{)|NZ7s) ziWuH6UF>C~zNRD&^W&mn+!a!O>-0_Rs_HYE4?H~%*^HHr#UIC)z^(E30}&%?T+5s6 zB%1S$>fSu>o?Iy_d!M}&kbhlm6zhH!2|I7Q8FRf&2tAWQz4gs=%$%3IQzHHfFvkEw ztY^Xyl$Sb`l?U3iFRZ+>B-a1I#k*hG6?GbLhaW5FMDi39gvM5A=IU21ipZmg3XaCy zOZ9HBU7R&AWI9+3qlmKp_I$SY$7%%Gpm8j53tV)bk}X`F+VHYo%^MX(9pfbU6839UVQ_NlP=b5JPWb{_V@rrF8hy>k z5qn|}ABtR$zFXEK%je3HM$ubYXuQ29%iVQxD$Z%Gw1CF}*4!9z!DB5yd(T|Q;ge;?BL z_2@V$Hjn{6QZZP|PbxZl`#CbSdUAGkPGJ8qyoR}v+yCJ{Oec`J{Rr1`dN?gssEW{Ql)*IeBc-rFE=)`nF2)?BK#!9mZ@Gd3kq1-OX=N&^6-QFn zR;0j`s#sw!Nb6o7EU7WBrnzBguN-`{#7VH3-;I&O1i*ngyq=%REy$;DjZv_E^$aW5 zCdN1#^QBXNmOXXZmJ0j*T2I|K6(K#iEdwRuPx$*6%rF>wRc$ceFV#t$O&S!etGZv# zG2v7OqLz*N+m103kBY}wSz5+Z%yE!sjrMeqkg>Cg4(;P2^J1TAdAl&o%)>_w)m3#P z_oQz<&hV$ToCLKQ)Y*PAd&|*ob?Y*n2DFxKowi`3B6KGMOXJ$0*)D)ZxPJI~exA&@ z`liPt<^9B#>*KBCo~rm{o;t|Sv?*Z4bBv;2yuG?q*}wDXN#7Zw7*G)y@4rEqRFBv%het&?LCv52K)qOiy<-Fy z56KkU>KGOCU-a2~4LpmBv0RF<2@7=GAIZy7lFHnZo)Lef3OG$014Lw}?RpGZaIyN% zt>gBcGI2%ci+YRXBo{O8Jmx(SJIt@rIw89tY6_U%!tY%p0fnH6&E=RCaUNHgGs-;$ zb5Y|j_6G>#`DczL>DFHw4pxdtUt;l zA$)E;&RLiug$?8F6foXWVZDH)bNx-tBh*-Cg%Y7HTp;vm0@n~K(Ir3AjfAX474M93 z7@aUhd$8kk_zB5;-#OEoH8DXx_V3bj2M{xRC8@Gp8ng>Fjr0E2Ffo9umoc759g8Ex zZQjKRFRDD0Gve3gp1OXe71F?3E=n)#V&~7Ubg*CqzPf}J1Er((019woBy_zc)3OAj zMrZa*cvx|_-LNfdS!lQu)cmGQ+C-k434(Eg2$k)+T0O|2Ch>fu-}whX-VV89#qZ?SS-6Z1VtiE4M(TMEjT zm~Q^(#yd3A&Kamric*GNxTt07;t-{JCl^eOGE?tIx!B}si&-!2^6pLib{=7b z!L-U;Zg(g=M)3~NO>MM2n(ng**0hXjkFcR;F6(J)sQWdY4Dm8fMF6Nw^)pl5oV!Rm z&Xz}yGNBL$>8C$TB7!_ftvpaLzAkVm6+7DmTyyl=MXBOhDcS?Z1v_!34OI7(KshAO&mqI|nyTE9?^R#^)fcoJ{% zr67yF_;N!fQYVXv2_Keat(YV5O%Sf_swivX!pHFnj!Ji$zMHY_hfl)kkv0`n6dedO zd1g)^Gt4>b-BbvlgXqPSG)b7m8dhc(tydG_mrV=?i?yNvZUxwVu{)P5~ivAn%0YcNLgS0+|3ihV|=U8QR2`wf`R~s8+BRjhf-kL_;!qOS=@p3RV z`&iBZ&;U6(f1CzjV*;`RIe1t(IT%@)I6t6tb`~a9AVBx;Apm(xHxS?>5f)uqfVG2> zi-j}D2;lZ#D><3im{>pZPP|)u4j&{tv!s!Y^S@)atp6tb ze*ncl68OIiivI%u|KRBVzhb<>AgCk-P++KFhVqcWATPnW|FhWt58(WR5I1si@o)l} QA+Q2D5hy7olq3=U7fM=w*#H0l literal 0 HcmV?d00001 diff --git a/services/clsi/test/unit/js/ContentCacheManagerTests.js b/services/clsi/test/unit/js/ContentCacheManagerTests.js index 35f22fffae..76c517d8ce 100644 --- a/services/clsi/test/unit/js/ContentCacheManagerTests.js +++ b/services/clsi/test/unit/js/ContentCacheManagerTests.js @@ -1,201 +1,123 @@ +const fs = require('fs') const Path = require('path') -const crypto = require('crypto') -const { Readable } = require('stream') -const SandboxedModule = require('sandboxed-module') -const sinon = require('sinon') const { expect } = require('chai') const MODULE_PATH = '../../../app/js/ContentCacheManager' -class FakeFile { - constructor() { - this.closed = false - this.contents = [] - } - - async write(blob) { - this.contents.push(blob) - return this - } - - async close() { - this.closed = true - return this - } - - toJSON() { - return { - contents: Buffer.concat(this.contents).toString(), - closed: this.closed - } - } -} - -function hash(blob) { - const hash = crypto.createHash('sha256') - hash.update(blob) - return hash.digest('hex') -} - describe('ContentCacheManager', function () { let contentDir, pdfPath - let ContentCacheManager, fs, files, Settings - function load() { - ContentCacheManager = SandboxedModule.require(MODULE_PATH, { - requires: { - fs, - 'settings-sharelatex': Settings - } - }) - } + let ContentCacheManager, files, Settings + before(function () { + Settings = require('settings-sharelatex') + ContentCacheManager = require(MODULE_PATH) + }) let contentRanges, newContentRanges, reclaimed - function run(filePath, done) { - ContentCacheManager.update(contentDir, filePath, (err, ranges) => { - if (err) return done(err) - let newlyReclaimed - ;[contentRanges, newContentRanges, newlyReclaimed] = ranges - reclaimed += newlyReclaimed - done() - }) - } + async function run(filePath, size) { + const result = await ContentCacheManager.promises.update( + contentDir, + filePath, + size + ) + let newlyReclaimed + ;[contentRanges, newContentRanges, newlyReclaimed] = result + reclaimed += newlyReclaimed - beforeEach(function () { - reclaimed = 0 + const fileNames = await fs.promises.readdir(contentDir) + files = {} + for (const fileName of fileNames) { + const path = Path.join(contentDir, fileName) + files[path] = await fs.promises.readFile(path) + } + } + before(function () { contentDir = '/app/output/602cee6f6460fca0ba7921e6/content/1797a7f48f9-5abc1998509dea1f' pdfPath = '/app/output/602cee6f6460fca0ba7921e6/generated-files/1797a7f48ea-8ac6805139f43351/output.pdf' - Settings = { - pdfCachingMinChunkSize: 1024, - enablePdfCachingDark: false - } - files = {} - fs = { - createReadStream: sinon.stub().returns(Readable.from([])), - promises: { - async writeFile(name, blob) { - const file = new FakeFile() - await file.write(Buffer.from(blob)) - await file.close() - files[name] = file - }, - async readFile(name) { - if (!files[name]) { - throw new Error() - } - return files[name].toJSON().contents - }, - async open(name) { - files[name] = new FakeFile() - return files[name] - }, - async stat(name) { - if (!files[name]) { - throw new Error() - } - }, - async rename(oldName, newName) { - if (!files[oldName]) { - throw new Error() - } - files[newName] = files[oldName] - delete files[oldName] - }, - async unlink(name) { - if (!files[name]) { - throw new Error() - } - delete files[name] - } - } - } + + reclaimed = 0 + Settings.pdfCachingMinChunkSize = 1024 }) - describe('with a small minChunkSize', function () { - beforeEach(function () { - Settings.pdfCachingMinChunkSize = 1 - load() + before(async function () { + await fs.promises.rmdir(contentDir, { recursive: true }) + await fs.promises.mkdir(contentDir, { recursive: true }) + await fs.promises.mkdir(Path.dirname(pdfPath), { recursive: true }) + }) + + describe('minimal', function () { + const PATH_MINIMAL = 'test/acceptance/fixtures/minimal.pdf' + const OBJECT_ID_1 = '9 0 ' + const HASH_LARGE = + 'd7cfc73ad2fba4578a437517923e3714927bbf35e63ea88bd93c7a8076cf1fcd' + const OBJECT_ID_2 = '10 0 ' + const HASH_SMALL = + '896749b8343851b0dc385f71616916a7ba0434fcfb56d1fc7e27cd139eaa2f71' + function getChunkPath(hash) { + return Path.join('test/unit/js/snapshots/minimalCompile/chunks', hash) + } + let MINIMAL_SIZE, RANGE_1, RANGE_2, h1, h2, START_1, START_2, END_1, END_2 + before(async function () { + await fs.promises.copyFile(PATH_MINIMAL, pdfPath) + const MINIMAL = await fs.promises.readFile(PATH_MINIMAL) + MINIMAL_SIZE = (await fs.promises.stat(PATH_MINIMAL)).size + RANGE_1 = await fs.promises.readFile(getChunkPath(HASH_LARGE)) + RANGE_2 = await fs.promises.readFile(getChunkPath(HASH_SMALL)) + h1 = HASH_LARGE + h2 = HASH_SMALL + START_1 = MINIMAL.indexOf(RANGE_1) + END_1 = START_1 + RANGE_1.byteLength + START_2 = MINIMAL.indexOf(RANGE_2) + END_2 = START_2 + RANGE_2.byteLength }) + async function runWithMinimal() { + await run(pdfPath, MINIMAL_SIZE) + } - describe('when the ranges are split across chunks', function () { - const RANGE_1 = 'stream123endstream' - const RANGE_2 = 'stream(||)endstream' - const RANGE_3 = 'stream!$%/=endstream' - const h1 = hash(RANGE_1) - const h2 = hash(RANGE_2) - const h3 = hash(RANGE_3) - function runWithSplitStream(done) { - fs.createReadStream - .withArgs(pdfPath) - .returns( - Readable.from([ - Buffer.from('abcstr'), - Buffer.from('eam123endstreamABC'), - Buffer.from('str'), - Buffer.from('eam(||'), - Buffer.from(')end'), - Buffer.from('stream-_~stream!$%/=endstream') - ]) - ) - run(pdfPath, done) - } - beforeEach(function (done) { - runWithSplitStream(done) + describe('with two ranges qualifying', function () { + before(function () { + Settings.pdfCachingMinChunkSize = 500 }) - - it('should produce three ranges', function () { - expect(contentRanges).to.have.length(3) + before(async function () { + await runWithMinimal() + }) + it('should produce two ranges', function () { + expect(contentRanges).to.have.length(2) }) it('should find the correct offsets', function () { expect(contentRanges).to.deep.equal([ { - start: 3, - end: 21, - hash: hash(RANGE_1) + objectId: OBJECT_ID_1, + start: START_1, + end: END_1, + hash: h1 }, { - start: 24, - end: 43, - hash: hash(RANGE_2) - }, - { - start: 46, - end: 66, - hash: hash(RANGE_3) + objectId: OBJECT_ID_2, + start: START_2, + end: END_2, + hash: h2 } ]) }) it('should store the contents', function () { - expect(JSON.parse(JSON.stringify(files))).to.deep.equal({ - [Path.join(contentDir, h1)]: { - contents: RANGE_1, - closed: true - }, - [Path.join(contentDir, h2)]: { - contents: RANGE_2, - closed: true - }, - [Path.join(contentDir, h3)]: { - contents: RANGE_3, - closed: true - }, - [Path.join(contentDir, '.state.v0.json')]: { - contents: JSON.stringify({ + expect(files).to.deep.equal({ + [Path.join(contentDir, h1)]: RANGE_1, + [Path.join(contentDir, h2)]: RANGE_2, + [Path.join(contentDir, '.state.v0.json')]: Buffer.from( + JSON.stringify({ hashAge: [ [h1, 0], - [h2, 0], - [h3, 0] + [h2, 0] ], hashSize: [ - [h1, 18], - [h2, 19], - [h3, 20] + [h1, RANGE_1.byteLength], + [h2, RANGE_2.byteLength] ] - }), - closed: true - } + }) + ) }) }) @@ -203,71 +125,46 @@ describe('ContentCacheManager', function () { expect(contentRanges).to.deep.equal(newContentRanges) }) - describe('when re-running with one stream removed', function () { - function runWithOneSplitStreamRemoved(done) { - fs.createReadStream - .withArgs(pdfPath) - .returns( - Readable.from([ - Buffer.from('abcstr'), - Buffer.from('eam123endstreamABC'), - Buffer.from('stream!$%/=endstream') - ]) - ) - run(pdfPath, done) - } - beforeEach(function (done) { - runWithOneSplitStreamRemoved(done) + describe('when re-running with one range too small', function () { + before(function () { + Settings.pdfCachingMinChunkSize = 1024 }) - it('should produce two ranges', function () { - expect(contentRanges).to.have.length(2) + before(async function () { + await runWithMinimal() + }) + + it('should produce one range', function () { + expect(contentRanges).to.have.length(1) }) it('should find the correct offsets', function () { expect(contentRanges).to.deep.equal([ { - start: 3, - end: 21, - hash: hash(RANGE_1) - }, - { - start: 24, - end: 44, - hash: hash(RANGE_3) + objectId: OBJECT_ID_1, + start: START_1, + end: END_1, + hash: h1 } ]) }) it('should update the age of the 2nd range', function () { - expect(JSON.parse(JSON.stringify(files))).to.deep.equal({ - [Path.join(contentDir, h1)]: { - contents: RANGE_1, - closed: true - }, - [Path.join(contentDir, h2)]: { - contents: RANGE_2, - closed: true - }, - [Path.join(contentDir, h3)]: { - contents: RANGE_3, - closed: true - }, - [Path.join(contentDir, '.state.v0.json')]: { - contents: JSON.stringify({ + expect(files).to.deep.equal({ + [Path.join(contentDir, h1)]: RANGE_1, + [Path.join(contentDir, h2)]: RANGE_2, + [Path.join(contentDir, '.state.v0.json')]: Buffer.from( + JSON.stringify({ hashAge: [ [h1, 0], - [h2, 1], - [h3, 0] + [h2, 1] ], hashSize: [ - [h1, 18], - [h2, 19], - [h3, 20] + [h1, RANGE_1.byteLength], + [h2, RANGE_2.byteLength] ] - }), - closed: true - } + }) + ) }) }) @@ -277,53 +174,35 @@ describe('ContentCacheManager', function () { describe('when re-running 5 more times', function () { for (let i = 0; i < 5; i++) { - beforeEach(function (done) { - runWithOneSplitStreamRemoved(done) + before(async function () { + await runWithMinimal() }) } - it('should still produce two ranges', function () { - expect(contentRanges).to.have.length(2) + it('should still produce one range', function () { + expect(contentRanges).to.have.length(1) }) it('should still find the correct offsets', function () { expect(contentRanges).to.deep.equal([ { - start: 3, - end: 21, - hash: hash(RANGE_1) - }, - { - start: 24, - end: 44, - hash: hash(RANGE_3) + objectId: OBJECT_ID_1, + start: START_1, + end: END_1, + hash: h1 } ]) }) it('should delete the 2nd range', function () { - expect(JSON.parse(JSON.stringify(files))).to.deep.equal({ - [Path.join(contentDir, h1)]: { - contents: RANGE_1, - closed: true - }, - [Path.join(contentDir, h3)]: { - contents: RANGE_3, - closed: true - }, - [Path.join(contentDir, '.state.v0.json')]: { - contents: JSON.stringify({ - hashAge: [ - [h1, 0], - [h3, 0] - ], - hashSize: [ - [h1, 18], - [h3, 20] - ] - }), - closed: true - } + expect(files).to.deep.equal({ + [Path.join(contentDir, h1)]: RANGE_1, + [Path.join(contentDir, '.state.v0.json')]: Buffer.from( + JSON.stringify({ + hashAge: [[h1, 0]], + hashSize: [[h1, RANGE_1.byteLength]] + }) + ) }) }) @@ -332,7 +211,7 @@ describe('ContentCacheManager', function () { }) it('should yield the reclaimed space', function () { - expect(reclaimed).to.equal(RANGE_2.length) + expect(reclaimed).to.equal(RANGE_2.byteLength) }) }) }) diff --git a/services/clsi/test/unit/js/snapshots/minimalCompile/chunks/896749b8343851b0dc385f71616916a7ba0434fcfb56d1fc7e27cd139eaa2f71 b/services/clsi/test/unit/js/snapshots/minimalCompile/chunks/896749b8343851b0dc385f71616916a7ba0434fcfb56d1fc7e27cd139eaa2f71 new file mode 100644 index 0000000000..bb9f891be7 --- /dev/null +++ b/services/clsi/test/unit/js/snapshots/minimalCompile/chunks/896749b8343851b0dc385f71616916a7ba0434fcfb56d1fc7e27cd139eaa2f71 @@ -0,0 +1,7 @@ +obj +<< /Type /ObjStm /Length 447 /Filter /FlateDecode /N 5 /First 32 >> +stream +xœ…RQkÛ0~߯¸Ç�K'É’ ¥Ð$„vkGIJ[(yð‘Ò8Ø*ëþýî$¯IûR°déîû>ÝI"H@¥¤Ï9@J!` Vú/gg fí>BZx†J˜Ãù9ˆÛ®]-B„'ZNg îÂk„%¥i\÷!ÅÍõüêÇôûäfŽ’4š®�“mÝ�©²ô4ô«®9ĶãCY]\ב@‹—ßñï!Ð 4cþ U‰‡f·=�ËJgOÚÊòg³î©>—’ËÿÌÛzzÆ>A)å§C9Wøw³K³qªPÜ#®”à¤/48•¢VX/ ¶TŒð¦p §-%2"³­*¡B;ä‡XâêÚ2£,9’õG¥z;ž†E–Î¥øØJj/‰c +ón­%¯�ˆÌ¢ê¨áµ¦f3æˆÎ]!µ¢«¤ç=µyšÍ ,Ós]ô«@ïe+COW.C³ÙÒÖkÚ’ Øc_Å¥X‹ v¢í>ˆNüù‚�ÃÌ2u¶«7=˜ìŒñ¸}¥— #•®HVér–9?kvªì6ÞÿªŸÃG«^Åz׬.ö‚ÒvÃó=UyjÀïŠÇ¡p£‘ÍõzìË2 +endstream +endobj diff --git a/services/clsi/test/unit/js/snapshots/minimalCompile/chunks/d7cfc73ad2fba4578a437517923e3714927bbf35e63ea88bd93c7a8076cf1fcd b/services/clsi/test/unit/js/snapshots/minimalCompile/chunks/d7cfc73ad2fba4578a437517923e3714927bbf35e63ea88bd93c7a8076cf1fcd new file mode 100644 index 0000000000000000000000000000000000000000..d503090d3a7389ff13f6c9a2106c66e0ecfbd73b GIT binary patch literal 10161 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 literal 0 HcmV?d00001 diff --git a/services/clsi/test/unit/lib/pdfjsTests.js b/services/clsi/test/unit/lib/pdfjsTests.js new file mode 100644 index 0000000000..64f062c0b8 --- /dev/null +++ b/services/clsi/test/unit/lib/pdfjsTests.js @@ -0,0 +1,78 @@ +const fs = require('fs') +const Path = require('path') +const { expect } = require('chai') +const { parseXrefTable } = require('../../../app/lib/pdfjs/parseXrefTable') +const PATH_EXAMPLES = 'test/acceptance/fixtures/examples/' +const PATH_SNAPSHOTS = 'test/unit/lib/snapshots/' +const EXAMPLES = fs.readdirSync(PATH_EXAMPLES) + +function snapshotPath(example) { + return Path.join(PATH_SNAPSHOTS, example, 'XrefTable.json') +} + +function pdfPath(example) { + return Path.join(PATH_EXAMPLES, example, 'output.pdf') +} + +async function loadContext(example) { + const size = (await fs.promises.stat(pdfPath(example))).size + + let blob + try { + blob = await fs.promises.readFile(snapshotPath(example)) + } catch (e) { + if (e.code !== 'ENOENT') { + throw e + } + } + const snapshot = blob ? JSON.parse(blob) : null + return { + size, + snapshot + } +} + +async function backFillSnapshot(example, size) { + const table = await parseXrefTable(pdfPath(example), size) + await fs.promises.mkdir(Path.dirname(snapshotPath(example)), { + recursive: true + }) + await fs.promises.writeFile( + snapshotPath(example), + JSON.stringify(table, null, 2) + ) + return table +} + +describe('pdfjs', function () { + describe('when the pdf is an empty file', function () { + it('should yield no entries', async function () { + const path = 'does/not/matter.pdf' + const table = await parseXrefTable(path, 0) + expect(table).to.deep.equal([]) + }) + }) + + for (const example of EXAMPLES) { + describe(example, function () { + let size, snapshot + before('load snapshot', async function () { + const ctx = await loadContext(example) + size = ctx.size + snapshot = ctx.snapshot + }) + + before('back fill new snapshot', async function () { + if (snapshot === null) { + console.error('back filling snapshot for', example) + snapshot = await backFillSnapshot(example, size) + } + }) + + it('should produce the expected xRef table', async function () { + const table = await parseXrefTable(pdfPath(example), size) + expect(table).to.deep.equal(snapshot) + }) + }) + } +}) diff --git a/services/clsi/test/unit/lib/snapshots/asymptote/XrefTable.json b/services/clsi/test/unit/lib/snapshots/asymptote/XrefTable.json new file mode 100644 index 0000000000..be9f29164d --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/asymptote/XrefTable.json @@ -0,0 +1,356 @@ +[ + { + "offset": 0, + "gen": 0, + "free": true + }, + { + "offset": 123086, + "gen": 0, + "uncompressed": true + }, + { + "offset": 123405, + "gen": 0, + "uncompressed": true + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 216, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1084, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1244, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4001, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4155, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4297, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4932, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5307, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5495, + "gen": 0, + "uncompressed": true + }, + { + "offset": 30246, + "gen": 0, + "uncompressed": true + }, + { + "offset": 31466, + "gen": 0, + "uncompressed": true + }, + { + "offset": 38398, + "gen": 0, + "uncompressed": true + }, + { + "offset": 39039, + "gen": 0, + "uncompressed": true + }, + { + "offset": 40158, + "gen": 0, + "uncompressed": true + }, + { + "offset": 40897, + "gen": 0, + "uncompressed": true + }, + { + "offset": 65550, + "gen": 0, + "uncompressed": true + }, + { + "offset": 74691, + "gen": 0, + "uncompressed": true + }, + { + "offset": 81693, + "gen": 0, + "uncompressed": true + }, + { + "offset": 97169, + "gen": 0, + "uncompressed": true + }, + { + "offset": 104103, + "gen": 0, + "uncompressed": true + }, + { + "offset": 111180, + "gen": 0, + "uncompressed": true + }, + { + "offset": 118555, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 0 + }, + { + "offset": 6, + "gen": 1 + }, + { + "offset": 6, + "gen": 2 + }, + { + "offset": 6, + "gen": 3 + }, + { + "offset": 6, + "gen": 4 + }, + { + "offset": 6, + "gen": 5 + }, + { + "offset": 6, + "gen": 6 + }, + { + "offset": 6, + "gen": 7 + }, + { + "offset": 6, + "gen": 8 + }, + { + "offset": 6, + "gen": 9 + }, + { + "offset": 6, + "gen": 10 + }, + { + "offset": 6, + "gen": 11 + }, + { + "offset": 6, + "gen": 12 + }, + { + "offset": 6, + "gen": 13 + }, + { + "offset": 6, + "gen": 14 + }, + { + "offset": 6, + "gen": 15 + }, + { + "offset": 6, + "gen": 16 + }, + { + "offset": 6, + "gen": 17 + }, + { + "offset": 6, + "gen": 18 + }, + { + "offset": 6, + "gen": 19 + }, + { + "offset": 6, + "gen": 20 + }, + { + "offset": 6, + "gen": 21 + }, + { + "offset": 6, + "gen": 22 + }, + { + "offset": 6, + "gen": 23 + }, + { + "offset": 6, + "gen": 24 + }, + { + "offset": 6, + "gen": 25 + }, + { + "offset": 6, + "gen": 26 + }, + { + "offset": 6, + "gen": 27 + }, + { + "offset": 6, + "gen": 28 + }, + { + "offset": 6, + "gen": 29 + }, + { + "offset": 6, + "gen": 30 + }, + { + "offset": 6, + "gen": 31 + }, + { + "offset": 6, + "gen": 32 + }, + { + "offset": 6, + "gen": 33 + }, + { + "offset": 6, + "gen": 34 + }, + { + "offset": 6, + "gen": 35 + }, + { + "offset": 6, + "gen": 36 + }, + { + "offset": 6, + "gen": 37 + }, + { + "offset": 6, + "gen": 38 + }, + { + "offset": 6, + "gen": 39 + }, + { + "offset": 6, + "gen": 40 + }, + { + "offset": 6, + "gen": 41 + }, + { + "offset": 6, + "gen": 42 + }, + { + "offset": 6, + "gen": 43 + }, + { + "offset": 6, + "gen": 44 + }, + { + "offset": 6, + "gen": 45 + }, + { + "offset": 6, + "gen": 46 + }, + { + "offset": 6, + "gen": 47 + }, + { + "offset": 6, + "gen": 48 + }, + { + "offset": 6, + "gen": 49 + }, + { + "offset": 6, + "gen": 50 + }, + { + "offset": 6, + "gen": 51 + }, + { + "offset": 6, + "gen": 52 + }, + { + "offset": 6, + "gen": 53 + }, + { + "offset": 6, + "gen": 54 + }, + { + "offset": 6, + "gen": 55 + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/biber_bibliography/XrefTable.json b/services/clsi/test/unit/lib/snapshots/biber_bibliography/XrefTable.json new file mode 100644 index 0000000000..94c0fddf42 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/biber_bibliography/XrefTable.json @@ -0,0 +1,128 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 9, + "gen": 1 + }, + { + "offset": 9, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 14 + }, + { + "offset": 9, + "gen": 12 + }, + { + "offset": 9, + "gen": 15 + }, + { + "offset": 9, + "gen": 13 + }, + { + "offset": 9, + "gen": 16 + }, + { + "offset": 57274, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 17 + }, + { + "offset": 9, + "gen": 2 + }, + { + "offset": 9, + "gen": 3 + }, + { + "offset": 9, + "gen": 4 + }, + { + "offset": 9, + "gen": 5 + }, + { + "offset": 9, + "gen": 6 + }, + { + "offset": 522, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 7 + }, + { + "offset": 8788, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 8 + }, + { + "offset": 17289, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 9 + }, + { + "offset": 32619, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 10 + }, + { + "offset": 44596, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 11 + }, + { + "offset": 9, + "gen": 18 + }, + { + "offset": 57027, + "gen": 0, + "uncompressed": true + }, + { + "offset": 58655, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/epstopdf/XrefTable.json b/services/clsi/test/unit/lib/snapshots/epstopdf/XrefTable.json new file mode 100644 index 0000000000..fca6e5447d --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/epstopdf/XrefTable.json @@ -0,0 +1,125 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 208, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 11 + }, + { + "offset": 6, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 14 + }, + { + "offset": 29580, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 15 + }, + { + "offset": 6, + "gen": 1 + }, + { + "offset": 6, + "gen": 2 + }, + { + "offset": 6, + "gen": 3 + }, + { + "offset": 6, + "gen": 4 + }, + { + "offset": 6, + "gen": 5 + }, + { + "offset": 6, + "gen": 6 + }, + { + "offset": 6, + "gen": 7 + }, + { + "offset": 6, + "gen": 8 + }, + { + "offset": 18310, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 9 + }, + { + "offset": 6, + "gen": 10 + }, + { + "offset": 18554, + "gen": 0, + "uncompressed": true + }, + { + "offset": 18945, + "gen": 0, + "uncompressed": true + }, + { + "offset": 19674, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 12 + }, + { + "offset": 22318, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 13 + }, + { + "offset": 6, + "gen": 16 + }, + { + "offset": 29321, + "gen": 0, + "uncompressed": true + }, + { + "offset": 30697, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/feynmf/XrefTable.json b/services/clsi/test/unit/lib/snapshots/feynmf/XrefTable.json new file mode 100644 index 0000000000..0da87fc092 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/feynmf/XrefTable.json @@ -0,0 +1,115 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 8, + "gen": 1 + }, + { + "offset": 8, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 5 + }, + { + "offset": 8, + "gen": 12 + }, + { + "offset": 8, + "gen": 14 + }, + { + "offset": 8, + "gen": 13 + }, + { + "offset": 25628, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 15 + }, + { + "offset": 8, + "gen": 2 + }, + { + "offset": 8, + "gen": 3 + }, + { + "offset": 8, + "gen": 4 + }, + { + "offset": 250, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 6 + }, + { + "offset": 8, + "gen": 7 + }, + { + "offset": 8, + "gen": 8 + }, + { + "offset": 3854, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 9 + }, + { + "offset": 11227, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 10 + }, + { + "offset": 18230, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 11 + }, + { + "offset": 8, + "gen": 16 + }, + { + "offset": 25381, + "gen": 0, + "uncompressed": true + }, + { + "offset": 26423, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/feynmp/XrefTable.json b/services/clsi/test/unit/lib/snapshots/feynmp/XrefTable.json new file mode 100644 index 0000000000..34048b037d --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/feynmp/XrefTable.json @@ -0,0 +1,102 @@ +[ + { + "offset": 0, + "gen": 65535, + "free": true + }, + { + "offset": 1094, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5512, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1035, + "gen": 0, + "uncompressed": true + }, + { + "offset": 875, + "gen": 0, + "uncompressed": true + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 856, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1159, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1593, + "gen": 0, + "uncompressed": true + }, + { + "offset": 3149, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1436, + "gen": 0, + "uncompressed": true + }, + { + "offset": 2412, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1282, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1768, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1200, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1230, + "gen": 0, + "uncompressed": true + }, + { + "offset": 2006, + "gen": 0, + "uncompressed": true + }, + { + "offset": 2625, + "gen": 0, + "uncompressed": true + }, + { + "offset": 3381, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4069, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/fontawesome/XrefTable.json b/services/clsi/test/unit/lib/snapshots/fontawesome/XrefTable.json new file mode 100644 index 0000000000..1951e17f7f --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/fontawesome/XrefTable.json @@ -0,0 +1,93 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 7, + "gen": 1 + }, + { + "offset": 7, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 11 + }, + { + "offset": 7, + "gen": 10 + }, + { + "offset": 7, + "gen": 9 + }, + { + "offset": 29476, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 12 + }, + { + "offset": 7, + "gen": 7 + }, + { + "offset": 7, + "gen": 2 + }, + { + "offset": 7, + "gen": 8 + }, + { + "offset": 7, + "gen": 3 + }, + { + "offset": 7, + "gen": 4 + }, + { + "offset": 255, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 5 + }, + { + "offset": 17910, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 6 + }, + { + "offset": 7, + "gen": 13 + }, + { + "offset": 29228, + "gen": 0, + "uncompressed": true + }, + { + "offset": 30448, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/fontawesome_xelatex/XrefTable.json b/services/clsi/test/unit/lib/snapshots/fontawesome_xelatex/XrefTable.json new file mode 100644 index 0000000000..e6315a28e7 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/fontawesome_xelatex/XrefTable.json @@ -0,0 +1,126 @@ +[ + { + "offset": 0, + "gen": 0, + "free": true + }, + { + "offset": 6338, + "gen": 0, + "uncompressed": true + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 216, + "gen": 0, + "uncompressed": true + }, + { + "offset": 707, + "gen": 0, + "uncompressed": true + }, + { + "offset": 757, + "gen": 0, + "uncompressed": true + }, + { + "offset": 888, + "gen": 0, + "uncompressed": true + }, + { + "offset": 991, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1257, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1678, + "gen": 0, + "uncompressed": true + }, + { + "offset": 2050, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4246, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4339, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5382, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5475, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5513, + "gen": 0, + "uncompressed": true + }, + { + "offset": 15, + "gen": 0 + }, + { + "offset": 15, + "gen": 1 + }, + { + "offset": 15, + "gen": 2 + }, + { + "offset": 15, + "gen": 3 + }, + { + "offset": 15, + "gen": 4 + }, + { + "offset": 15, + "gen": 5 + }, + { + "offset": 15, + "gen": 6 + }, + { + "offset": 15, + "gen": 7 + }, + { + "offset": 15, + "gen": 8 + }, + { + "offset": 15, + "gen": 9 + }, + { + "offset": 15, + "gen": 10 + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/glossaries/XrefTable.json b/services/clsi/test/unit/lib/snapshots/glossaries/XrefTable.json new file mode 100644 index 0000000000..51f7dca8bb --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/glossaries/XrefTable.json @@ -0,0 +1,94 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 7, + "gen": 1 + }, + { + "offset": 7, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 10 + }, + { + "offset": 7, + "gen": 9 + }, + { + "offset": 7, + "gen": 8 + }, + { + "offset": 33085, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 11 + }, + { + "offset": 7, + "gen": 2 + }, + { + "offset": 7, + "gen": 3 + }, + { + "offset": 7, + "gen": 4 + }, + { + "offset": 445, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 5 + }, + { + "offset": 10048, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 6 + }, + { + "offset": 18151, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 7 + }, + { + "offset": 7, + "gen": 12 + }, + { + "offset": 32838, + "gen": 0, + "uncompressed": true + }, + { + "offset": 34157, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/gnuplot/XrefTable.json b/services/clsi/test/unit/lib/snapshots/gnuplot/XrefTable.json new file mode 100644 index 0000000000..5148059666 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/gnuplot/XrefTable.json @@ -0,0 +1,89 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 4, + "gen": 0 + }, + { + "offset": 4, + "gen": 1 + }, + { + "offset": 4, + "gen": 2 + }, + { + "offset": 22047, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4, + "gen": 4 + }, + { + "offset": 4, + "gen": 3 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4, + "gen": 10 + }, + { + "offset": 4, + "gen": 9 + }, + { + "offset": 4, + "gen": 11 + }, + { + "offset": 4, + "gen": 5 + }, + { + "offset": 4, + "gen": 6 + }, + { + "offset": 6451, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4, + "gen": 7 + }, + { + "offset": 14825, + "gen": 0, + "uncompressed": true + }, + { + "offset": 4, + "gen": 8 + }, + { + "offset": 4, + "gen": 12 + }, + { + "offset": 21800, + "gen": 0, + "uncompressed": true + }, + { + "offset": 22696, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/hebrew/XrefTable.json b/services/clsi/test/unit/lib/snapshots/hebrew/XrefTable.json new file mode 100644 index 0000000000..d3e8221a2e --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/hebrew/XrefTable.json @@ -0,0 +1,81 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 6, + "gen": 1 + }, + { + "offset": 6, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 8 + }, + { + "offset": 6, + "gen": 7 + }, + { + "offset": 22744, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 9 + }, + { + "offset": 6, + "gen": 2 + }, + { + "offset": 6, + "gen": 6 + }, + { + "offset": 6, + "gen": 3 + }, + { + "offset": 364, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 4 + }, + { + "offset": 12163, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 5 + }, + { + "offset": 6, + "gen": 10 + }, + { + "offset": 22496, + "gen": 0, + "uncompressed": true + }, + { + "offset": 23856, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/knitr/XrefTable.json b/services/clsi/test/unit/lib/snapshots/knitr/XrefTable.json new file mode 100644 index 0000000000..d57e124754 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/knitr/XrefTable.json @@ -0,0 +1,145 @@ +[ + { + "offset": 0, + "gen": 0, + "free": true + }, + { + "offset": 43543, + "gen": 0, + "uncompressed": true + }, + { + "offset": 43792, + "gen": 0, + "uncompressed": true + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 216, + "gen": 0, + "uncompressed": true + }, + { + "offset": 734, + "gen": 0, + "uncompressed": true + }, + { + "offset": 784, + "gen": 0, + "uncompressed": true + }, + { + "offset": 912, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1020, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1544, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5791, + "gen": 0, + "uncompressed": true + }, + { + "offset": 12911, + "gen": 0, + "uncompressed": true + }, + { + "offset": 23655, + "gen": 0, + "uncompressed": true + }, + { + "offset": 30651, + "gen": 0, + "uncompressed": true + }, + { + "offset": 42597, + "gen": 0, + "uncompressed": true + }, + { + "offset": 14, + "gen": 0 + }, + { + "offset": 14, + "gen": 1 + }, + { + "offset": 14, + "gen": 2 + }, + { + "offset": 14, + "gen": 3 + }, + { + "offset": 14, + "gen": 4 + }, + { + "offset": 14, + "gen": 5 + }, + { + "offset": 14, + "gen": 6 + }, + { + "offset": 14, + "gen": 7 + }, + { + "offset": 14, + "gen": 8 + }, + { + "offset": 14, + "gen": 9 + }, + { + "offset": 14, + "gen": 10 + }, + { + "offset": 14, + "gen": 11 + }, + { + "offset": 14, + "gen": 12 + }, + { + "offset": 14, + "gen": 13 + }, + { + "offset": 14, + "gen": 14 + }, + { + "offset": 14, + "gen": 15 + }, + { + "offset": 14, + "gen": 16 + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/knitr_utf8/XrefTable.json b/services/clsi/test/unit/lib/snapshots/knitr_utf8/XrefTable.json new file mode 100644 index 0000000000..14982555fc --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/knitr_utf8/XrefTable.json @@ -0,0 +1,179 @@ +[ + { + "offset": 0, + "gen": 0, + "free": true + }, + { + "offset": 75291, + "gen": 0, + "uncompressed": true + }, + { + "offset": 75540, + "gen": 0, + "uncompressed": true + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 216, + "gen": 0, + "uncompressed": true + }, + { + "offset": 790, + "gen": 0, + "uncompressed": true + }, + { + "offset": 840, + "gen": 0, + "uncompressed": true + }, + { + "offset": 975, + "gen": 0, + "uncompressed": true + }, + { + "offset": 1083, + "gen": 0, + "uncompressed": true + }, + { + "offset": 2127, + "gen": 0, + "uncompressed": true + }, + { + "offset": 13797, + "gen": 0, + "uncompressed": true + }, + { + "offset": 23679, + "gen": 0, + "uncompressed": true + }, + { + "offset": 31863, + "gen": 0, + "uncompressed": true + }, + { + "offset": 36111, + "gen": 0, + "uncompressed": true + }, + { + "offset": 50346, + "gen": 0, + "uncompressed": true + }, + { + "offset": 61562, + "gen": 0, + "uncompressed": true + }, + { + "offset": 73508, + "gen": 0, + "uncompressed": true + }, + { + "offset": 16, + "gen": 0 + }, + { + "offset": 16, + "gen": 1 + }, + { + "offset": 16, + "gen": 2 + }, + { + "offset": 16, + "gen": 3 + }, + { + "offset": 16, + "gen": 4 + }, + { + "offset": 16, + "gen": 5 + }, + { + "offset": 16, + "gen": 6 + }, + { + "offset": 16, + "gen": 7 + }, + { + "offset": 16, + "gen": 8 + }, + { + "offset": 16, + "gen": 9 + }, + { + "offset": 16, + "gen": 10 + }, + { + "offset": 16, + "gen": 11 + }, + { + "offset": 16, + "gen": 12 + }, + { + "offset": 16, + "gen": 13 + }, + { + "offset": 16, + "gen": 14 + }, + { + "offset": 16, + "gen": 15 + }, + { + "offset": 16, + "gen": 16 + }, + { + "offset": 16, + "gen": 17 + }, + { + "offset": 16, + "gen": 18 + }, + { + "offset": 16, + "gen": 19 + }, + { + "offset": 16, + "gen": 20 + }, + { + "offset": 16, + "gen": 21 + }, + { + "offset": 16, + "gen": 22 + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/latex_compiler/XrefTable.json b/services/clsi/test/unit/lib/snapshots/latex_compiler/XrefTable.json new file mode 100644 index 0000000000..91e596e54f --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/latex_compiler/XrefTable.json @@ -0,0 +1,132 @@ +[ + { + "offset": 0, + "gen": 65535, + "free": true + }, + { + "offset": 17349, + "gen": 0, + "uncompressed": true + }, + { + "offset": 25448, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17290, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17130, + "gen": 0, + "uncompressed": true + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17109, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17414, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17548, + "gen": 0, + "uncompressed": true + }, + { + "offset": 18912, + "gen": 0, + "uncompressed": true + }, + { + "offset": 18381, + "gen": 0, + "uncompressed": true + }, + { + "offset": 22406, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17897, + "gen": 0, + "uncompressed": true + }, + { + "offset": 21796, + "gen": 0, + "uncompressed": true + }, + { + "offset": 18758, + "gen": 0, + "uncompressed": true + }, + { + "offset": 23361, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17455, + "gen": 0, + "uncompressed": true + }, + { + "offset": 17485, + "gen": 0, + "uncompressed": true + }, + { + "offset": 19218, + "gen": 0, + "uncompressed": true + }, + { + "offset": 22021, + "gen": 0, + "uncompressed": true + }, + { + "offset": 22638, + "gen": 0, + "uncompressed": true + }, + { + "offset": 23599, + "gen": 0, + "uncompressed": true + }, + { + "offset": 18051, + "gen": 0, + "uncompressed": true + }, + { + "offset": 18142, + "gen": 0, + "uncompressed": true + }, + { + "offset": 18657, + "gen": 0, + "uncompressed": true + }, + { + "offset": 24005, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/lualatex_compiler/XrefTable.json b/services/clsi/test/unit/lib/snapshots/lualatex_compiler/XrefTable.json new file mode 100644 index 0000000000..a403bb4f6a --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/lualatex_compiler/XrefTable.json @@ -0,0 +1,74 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 6, + "gen": 1 + }, + { + "offset": 6, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 4 + }, + { + "offset": 6, + "gen": 6 + }, + { + "offset": 2320, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 2 + }, + { + "offset": 202, + "gen": 0, + "uncompressed": true + }, + { + "offset": 298, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 3 + }, + { + "offset": 1642, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 5 + }, + { + "offset": 6, + "gen": 7 + }, + { + "offset": 2120, + "gen": 0, + "uncompressed": true + }, + { + "offset": 2892, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/makeindex-custom-style/XrefTable.json b/services/clsi/test/unit/lib/snapshots/makeindex-custom-style/XrefTable.json new file mode 100644 index 0000000000..b2b9dcef35 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/makeindex-custom-style/XrefTable.json @@ -0,0 +1,107 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 5, + "gen": 1 + }, + { + "offset": 5, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 12 + }, + { + "offset": 30470, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 13 + }, + { + "offset": 5, + "gen": 3 + }, + { + "offset": 5, + "gen": 2 + }, + { + "offset": 366, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 11 + }, + { + "offset": 5, + "gen": 10 + }, + { + "offset": 5, + "gen": 4 + }, + { + "offset": 5, + "gen": 5 + }, + { + "offset": 5, + "gen": 6 + }, + { + "offset": 638, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 7 + }, + { + "offset": 7838, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 8 + }, + { + "offset": 15674, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 9 + }, + { + "offset": 5, + "gen": 14 + }, + { + "offset": 30223, + "gen": 0, + "uncompressed": true + }, + { + "offset": 31457, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/makeindex/XrefTable.json b/services/clsi/test/unit/lib/snapshots/makeindex/XrefTable.json new file mode 100644 index 0000000000..9a58bed4cb --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/makeindex/XrefTable.json @@ -0,0 +1,90 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 5, + "gen": 1 + }, + { + "offset": 5, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 9 + }, + { + "offset": 23352, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 10 + }, + { + "offset": 5, + "gen": 3 + }, + { + "offset": 5, + "gen": 2 + }, + { + "offset": 366, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 8 + }, + { + "offset": 5, + "gen": 4 + }, + { + "offset": 5, + "gen": 5 + }, + { + "offset": 589, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 6 + }, + { + "offset": 8425, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 7 + }, + { + "offset": 5, + "gen": 11 + }, + { + "offset": 23105, + "gen": 0, + "uncompressed": true + }, + { + "offset": 24245, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/minted/XrefTable.json b/services/clsi/test/unit/lib/snapshots/minted/XrefTable.json new file mode 100644 index 0000000000..f0d2f2a195 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/minted/XrefTable.json @@ -0,0 +1,77 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 6, + "gen": 1 + }, + { + "offset": 6, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 7 + }, + { + "offset": 6, + "gen": 6 + }, + { + "offset": 19462, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 8 + }, + { + "offset": 6, + "gen": 2 + }, + { + "offset": 6, + "gen": 3 + }, + { + "offset": 340, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 4 + }, + { + "offset": 7343, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 5 + }, + { + "offset": 6, + "gen": 9 + }, + { + "offset": 19215, + "gen": 0, + "uncompressed": true + }, + { + "offset": 20089, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/multibib_bibliography/XrefTable.json b/services/clsi/test/unit/lib/snapshots/multibib_bibliography/XrefTable.json new file mode 100644 index 0000000000..ffd44df50b --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/multibib_bibliography/XrefTable.json @@ -0,0 +1,133 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 6, + "gen": 1 + }, + { + "offset": 6, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 14 + }, + { + "offset": 6, + "gen": 15 + }, + { + "offset": 40591, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 17 + }, + { + "offset": 6, + "gen": 3 + }, + { + "offset": 6, + "gen": 2 + }, + { + "offset": 290, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 16 + }, + { + "offset": 6, + "gen": 5 + }, + { + "offset": 6, + "gen": 4 + }, + { + "offset": 595, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 7 + }, + { + "offset": 6, + "gen": 6 + }, + { + "offset": 844, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 8 + }, + { + "offset": 6, + "gen": 9 + }, + { + "offset": 6, + "gen": 10 + }, + { + "offset": 1107, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 11 + }, + { + "offset": 11816, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 12 + }, + { + "offset": 28180, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6, + "gen": 13 + }, + { + "offset": 6, + "gen": 18 + }, + { + "offset": 40344, + "gen": 0, + "uncompressed": true + }, + { + "offset": 41851, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/nomenclature/XrefTable.json b/services/clsi/test/unit/lib/snapshots/nomenclature/XrefTable.json new file mode 100644 index 0000000000..51286af2bc --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/nomenclature/XrefTable.json @@ -0,0 +1,94 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 7, + "gen": 1 + }, + { + "offset": 7, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 8 + }, + { + "offset": 7, + "gen": 9 + }, + { + "offset": 7, + "gen": 10 + }, + { + "offset": 32363, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 11 + }, + { + "offset": 7, + "gen": 2 + }, + { + "offset": 7, + "gen": 3 + }, + { + "offset": 7, + "gen": 4 + }, + { + "offset": 565, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 5 + }, + { + "offset": 10031, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 6 + }, + { + "offset": 18203, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 7 + }, + { + "offset": 7, + "gen": 12 + }, + { + "offset": 32116, + "gen": 0, + "uncompressed": true + }, + { + "offset": 33492, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/references_in_include/XrefTable.json b/services/clsi/test/unit/lib/snapshots/references_in_include/XrefTable.json new file mode 100644 index 0000000000..b0c6731240 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/references_in_include/XrefTable.json @@ -0,0 +1,90 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 5, + "gen": 1 + }, + { + "offset": 5, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 9 + }, + { + "offset": 15855, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 10 + }, + { + "offset": 5, + "gen": 3 + }, + { + "offset": 5, + "gen": 2 + }, + { + "offset": 167, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 8 + }, + { + "offset": 5, + "gen": 4 + }, + { + "offset": 5, + "gen": 5 + }, + { + "offset": 341, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 6 + }, + { + "offset": 7911, + "gen": 0, + "uncompressed": true + }, + { + "offset": 5, + "gen": 7 + }, + { + "offset": 5, + "gen": 11 + }, + { + "offset": 15608, + "gen": 0, + "uncompressed": true + }, + { + "offset": 16597, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/simple_bibliography/XrefTable.json b/services/clsi/test/unit/lib/snapshots/simple_bibliography/XrefTable.json new file mode 100644 index 0000000000..d633b7ba6f --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/simple_bibliography/XrefTable.json @@ -0,0 +1,94 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 7, + "gen": 1 + }, + { + "offset": 7, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 9 + }, + { + "offset": 7, + "gen": 8 + }, + { + "offset": 7, + "gen": 10 + }, + { + "offset": 35573, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 11 + }, + { + "offset": 7, + "gen": 2 + }, + { + "offset": 7, + "gen": 3 + }, + { + "offset": 7, + "gen": 4 + }, + { + "offset": 373, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 5 + }, + { + "offset": 8639, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 6 + }, + { + "offset": 23349, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7, + "gen": 7 + }, + { + "offset": 7, + "gen": 12 + }, + { + "offset": 35326, + "gen": 0, + "uncompressed": true + }, + { + "offset": 36668, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/subdirectories/XrefTable.json b/services/clsi/test/unit/lib/snapshots/subdirectories/XrefTable.json new file mode 100644 index 0000000000..06ed0fdf7d --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/subdirectories/XrefTable.json @@ -0,0 +1,108 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 548, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 1 + }, + { + "offset": 9, + "gen": 2 + }, + { + "offset": 9, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 10 + }, + { + "offset": 9, + "gen": 9 + }, + { + "offset": 9, + "gen": 11 + }, + { + "offset": 46398, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 12 + }, + { + "offset": 8100, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 3 + }, + { + "offset": 9, + "gen": 4 + }, + { + "offset": 9, + "gen": 5 + }, + { + "offset": 9693, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 6 + }, + { + "offset": 17959, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 7 + }, + { + "offset": 34174, + "gen": 0, + "uncompressed": true + }, + { + "offset": 9, + "gen": 8 + }, + { + "offset": 9, + "gen": 13 + }, + { + "offset": 46151, + "gen": 0, + "uncompressed": true + }, + { + "offset": 47562, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/tikz_feynman/XrefTable.json b/services/clsi/test/unit/lib/snapshots/tikz_feynman/XrefTable.json new file mode 100644 index 0000000000..afeaa84536 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/tikz_feynman/XrefTable.json @@ -0,0 +1,145 @@ +[ + { + "offset": 0, + "gen": 255, + "free": true + }, + { + "offset": 8, + "gen": 8 + }, + { + "offset": 8, + "gen": 9 + }, + { + "offset": 8, + "gen": 10 + }, + { + "offset": 8, + "gen": 1 + }, + { + "offset": 8, + "gen": 0 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 20 + }, + { + "offset": 33577, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 3 + }, + { + "offset": 8, + "gen": 2 + }, + { + "offset": 1151, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 17 + }, + { + "offset": 8, + "gen": 19 + }, + { + "offset": 8, + "gen": 18 + }, + { + "offset": 8, + "gen": 5 + }, + { + "offset": 8, + "gen": 4 + }, + { + "offset": 2721, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 7 + }, + { + "offset": 8, + "gen": 6 + }, + { + "offset": 5757, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 11 + }, + { + "offset": 8, + "gen": 12 + }, + { + "offset": 8, + "gen": 13 + }, + { + "offset": 9558, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 14 + }, + { + "offset": 18967, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 15 + }, + { + "offset": 26388, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 16 + }, + { + "offset": 8, + "gen": 21 + }, + { + "offset": 33354, + "gen": 0, + "uncompressed": true + }, + { + "offset": 34451, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file diff --git a/services/clsi/test/unit/lib/snapshots/xelatex_compiler/XrefTable.json b/services/clsi/test/unit/lib/snapshots/xelatex_compiler/XrefTable.json new file mode 100644 index 0000000000..31080439b5 --- /dev/null +++ b/services/clsi/test/unit/lib/snapshots/xelatex_compiler/XrefTable.json @@ -0,0 +1,73 @@ +[ + { + "offset": 0, + "gen": 65535, + "free": true + }, + { + "offset": 8, + "gen": 4 + }, + { + "offset": 8, + "gen": 3 + }, + { + "offset": 8, + "gen": 1 + }, + { + "offset": 8, + "gen": 6 + }, + { + "offset": 8, + "gen": 8 + }, + { + "offset": 15, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 0 + }, + { + "offset": 6857, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 2 + }, + { + "offset": 265, + "gen": 0, + "uncompressed": true + }, + { + "offset": 8, + "gen": 7 + }, + { + "offset": 8, + "gen": 5 + }, + { + "offset": 701, + "gen": 0, + "uncompressed": true + }, + { + "offset": 6750, + "gen": 0, + "uncompressed": true + }, + { + "offset": 7655, + "gen": 0, + "uncompressed": true + } +] \ No newline at end of file