mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-13 13:43:41 +01:00
Merge pull request #28909 from overleaf/em-compile-metrics
Use histograms to track CLSI compile times GitOrigin-RevId: cf25f1e6d2094186f419acc70748f0c71b6c3240
This commit is contained in:
@@ -2,7 +2,7 @@ const Path = require('node:path')
|
||||
const RequestParser = require('./RequestParser')
|
||||
const CompileManager = require('./CompileManager')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const Metrics = require('./Metrics')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
const ProjectPersistenceManager = require('./ProjectPersistenceManager')
|
||||
const logger = require('@overleaf/logger')
|
||||
const Errors = require('./Errors')
|
||||
|
||||
@@ -6,12 +6,13 @@ const { callbackify } = require('node:util')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const logger = require('@overleaf/logger')
|
||||
const OError = require('@overleaf/o-error')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
|
||||
const ResourceWriter = require('./ResourceWriter')
|
||||
const LatexRunner = require('./LatexRunner')
|
||||
const OutputFileFinder = require('./OutputFileFinder')
|
||||
const OutputCacheManager = require('./OutputCacheManager')
|
||||
const Metrics = require('./Metrics')
|
||||
const ClsiMetrics = require('./Metrics')
|
||||
const DraftModeManager = require('./DraftModeManager')
|
||||
const TikzManager = require('./TikzManager')
|
||||
const LockManager = require('./LockManager')
|
||||
@@ -67,12 +68,8 @@ async function doCompile(request, stats, timings) {
|
||||
const { project_id: projectId, user_id: userId } = request
|
||||
const compileDir = getCompileDir(request.project_id, request.user_id)
|
||||
|
||||
const timerE2E = new Metrics.Timer(
|
||||
'compile-e2e-v2',
|
||||
1,
|
||||
request.metricsOpts,
|
||||
COMPILE_TIME_BUCKETS
|
||||
)
|
||||
const e2eCompileStart = Date.now()
|
||||
|
||||
if (request.isInitialCompile) {
|
||||
stats.isInitialCompile = 1
|
||||
request.metricsOpts.compile = 'initial'
|
||||
@@ -92,11 +89,8 @@ async function doCompile(request, stats, timings) {
|
||||
} else {
|
||||
request.metricsOpts.compile = 'recompile'
|
||||
}
|
||||
const writeToDiskTimer = new Metrics.Timer(
|
||||
'write-to-disk',
|
||||
1,
|
||||
request.metricsOpts
|
||||
)
|
||||
|
||||
const syncStart = Date.now()
|
||||
logger.debug(
|
||||
{ projectId: request.project_id, userId: request.user_id },
|
||||
'syncing resources to disk'
|
||||
@@ -123,15 +117,16 @@ async function doCompile(request, stats, timings) {
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
timings.sync = Date.now() - syncStart
|
||||
logger.debug(
|
||||
{
|
||||
projectId: request.project_id,
|
||||
userId: request.user_id,
|
||||
timeTaken: Date.now() - writeToDiskTimer.start,
|
||||
timeTaken: timings.sync,
|
||||
},
|
||||
'written files to disk'
|
||||
)
|
||||
timings.sync = writeToDiskTimer.done()
|
||||
|
||||
// set up environment variables for chktex
|
||||
const env = {
|
||||
@@ -177,21 +172,20 @@ async function doCompile(request, stats, timings) {
|
||||
)
|
||||
}
|
||||
|
||||
const compileTimer = new Metrics.Timer('run-compile', 1, request.metricsOpts)
|
||||
// find the image tag to log it as a metric, e.g. 2015.1 (convert . to - for graphite)
|
||||
const compileStart = Date.now()
|
||||
|
||||
// find the image tag to log it as a metric, e.g. 2015.1
|
||||
let tag = 'default'
|
||||
if (request.imageName != null) {
|
||||
const match = request.imageName.match(/:(.*)/)
|
||||
if (match != null) {
|
||||
tag = match[1].replace(/\./g, '-')
|
||||
tag = match[1]
|
||||
}
|
||||
}
|
||||
// exclude smoke test
|
||||
if (!request.project_id.match(/^[0-9a-f]{24}$/)) {
|
||||
tag = 'other'
|
||||
}
|
||||
Metrics.inc('compiles', 1, request.metricsOpts)
|
||||
Metrics.inc(`compiles-with-image.${tag}`, 1, request.metricsOpts)
|
||||
const compileName = getCompileName(request.project_id, request.user_id)
|
||||
|
||||
// Record latexmk -time stats for a subset of users
|
||||
@@ -241,11 +235,6 @@ async function doCompile(request, stats, timings) {
|
||||
error.validate = 'fail'
|
||||
}
|
||||
|
||||
// record timeout errors as a separate counter, success is recorded later
|
||||
if (error.timedout) {
|
||||
Metrics.inc('compiles-timeout', 1, request.metricsOpts)
|
||||
}
|
||||
|
||||
const { outputFiles, allEntries, buildId } = await _saveOutputFiles({
|
||||
request,
|
||||
compileDir,
|
||||
@@ -264,49 +253,59 @@ async function doCompile(request, stats, timings) {
|
||||
)
|
||||
}
|
||||
|
||||
if (!_shouldSkipMetrics(request)) {
|
||||
const status = error.timedout
|
||||
? 'timeout'
|
||||
: error.terminated
|
||||
? 'terminated'
|
||||
: 'failure'
|
||||
timings.compile = Date.now() - compileStart
|
||||
ClsiMetrics.compilesTotal.inc({
|
||||
status,
|
||||
engine: request.compiler,
|
||||
image: tag,
|
||||
compile: request.metricsOpts.compile,
|
||||
group: request.compileGroup,
|
||||
draft: request.draft ? 'true' : 'false',
|
||||
stop_on_first_error: request.stopOnFirstError ? 'true' : 'false',
|
||||
})
|
||||
ClsiMetrics.compileDurationSeconds.observe(
|
||||
{
|
||||
status,
|
||||
engine: request.compiler,
|
||||
compile: request.metricsOpts.compile,
|
||||
group: request.compileGroup,
|
||||
},
|
||||
timings.compile / 1000
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
// compile completed normally
|
||||
Metrics.inc('compiles-succeeded', 1, request.metricsOpts)
|
||||
for (const metricKey in stats) {
|
||||
const metricValue = stats[metricKey]
|
||||
if (typeof metricValue === 'number') {
|
||||
Metrics.count(metricKey, metricValue, 1, request.metricsOpts)
|
||||
}
|
||||
}
|
||||
for (const metricKey in timings) {
|
||||
const metricValue = timings[metricKey]
|
||||
if (typeof metricValue === 'number') {
|
||||
Metrics.timing(metricKey, metricValue, 1, request.metricsOpts)
|
||||
}
|
||||
}
|
||||
const loadavg = typeof os.loadavg === 'function' ? os.loadavg() : undefined
|
||||
if (loadavg != null) {
|
||||
Metrics.gauge('load-avg', loadavg[0])
|
||||
}
|
||||
const ts = compileTimer.done()
|
||||
timings.compile = Date.now() - compileStart
|
||||
const status = stats['latexmk-errors'] ? 'error' : 'success'
|
||||
|
||||
logger.debug(
|
||||
{
|
||||
projectId: request.project_id,
|
||||
userId: request.user_id,
|
||||
timeTaken: ts,
|
||||
timeTaken: timings.compile,
|
||||
stats,
|
||||
timings,
|
||||
loadavg,
|
||||
},
|
||||
'done compile'
|
||||
)
|
||||
|
||||
if (stats['latex-runs'] > 0) {
|
||||
Metrics.histogram(
|
||||
'avg-compile-per-pass-v2',
|
||||
ts / stats['latex-runs'],
|
||||
timings.compile / stats['latex-runs'],
|
||||
COMPILE_TIME_BUCKETS,
|
||||
request.metricsOpts
|
||||
)
|
||||
Metrics.timing(
|
||||
'avg-compile-per-pass-v2',
|
||||
ts / stats['latex-runs'],
|
||||
timings.compile / stats['latex-runs'],
|
||||
1,
|
||||
request.metricsOpts
|
||||
)
|
||||
@@ -319,8 +318,6 @@ async function doCompile(request, stats, timings) {
|
||||
request.metricsOpts
|
||||
)
|
||||
}
|
||||
// Emit compile time.
|
||||
timings.compile = ts
|
||||
|
||||
const { outputFiles, buildId } = await _saveOutputFiles({
|
||||
request,
|
||||
@@ -329,10 +326,37 @@ async function doCompile(request, stats, timings) {
|
||||
stats,
|
||||
timings,
|
||||
})
|
||||
timings.compileE2E = Date.now() - e2eCompileStart
|
||||
|
||||
// Emit e2e compile time.
|
||||
timings.compileE2E = timerE2E.done()
|
||||
Metrics.timing('compile-e2e-v2', timings.compileE2E, 1, request.metricsOpts)
|
||||
if (!_shouldSkipMetrics(request)) {
|
||||
ClsiMetrics.compilesTotal.inc({
|
||||
status,
|
||||
engine: request.compiler,
|
||||
image: tag,
|
||||
compile: request.metricsOpts.compile,
|
||||
group: request.compileGroup,
|
||||
draft: request.draft ? 'true' : 'false',
|
||||
stop_on_first_error: request.stopOnFirstError ? 'true' : 'false',
|
||||
})
|
||||
ClsiMetrics.compileDurationSeconds.observe(
|
||||
{
|
||||
status,
|
||||
engine: request.compiler,
|
||||
compile: request.metricsOpts.compile,
|
||||
group: request.compileGroup,
|
||||
},
|
||||
timings.compile / 1000
|
||||
)
|
||||
ClsiMetrics.syncResourcesDurationSeconds.observe(
|
||||
{
|
||||
type: request.syncType,
|
||||
compile: request.metricsOpts.compile,
|
||||
group: request.compileGroup,
|
||||
},
|
||||
timings.sync / 1000
|
||||
)
|
||||
ClsiMetrics.e2eCompileDurationSeconds.observe(timings.compileE2E / 1000)
|
||||
}
|
||||
|
||||
if (stats['pdf-size']) {
|
||||
emitPdfStats(stats, timings, request)
|
||||
@@ -353,11 +377,14 @@ async function doCompile(request, stats, timings) {
|
||||
'error reading fdb file for performance metrics'
|
||||
)
|
||||
}
|
||||
|
||||
const loadavg = typeof os.loadavg === 'function' ? os.loadavg() : undefined
|
||||
|
||||
logger.info(
|
||||
{
|
||||
userId: request.user_id,
|
||||
projectId: request.project_id,
|
||||
timeTaken: ts,
|
||||
timeTaken: timings.compile,
|
||||
clsiRequest: request,
|
||||
stats,
|
||||
timings,
|
||||
@@ -382,11 +409,7 @@ async function _saveOutputFiles({
|
||||
stats,
|
||||
timings,
|
||||
}) {
|
||||
const timer = new Metrics.Timer(
|
||||
'process-output-files',
|
||||
1,
|
||||
request.metricsOpts
|
||||
)
|
||||
const start = Date.now()
|
||||
const outputDir = getOutputDir(request.project_id, request.user_id)
|
||||
|
||||
const { outputFiles: rawOutputFiles, allEntries } =
|
||||
@@ -400,7 +423,16 @@ async function _saveOutputFiles({
|
||||
outputDir
|
||||
)
|
||||
|
||||
timings.output = timer.done()
|
||||
timings.output = Date.now() - start
|
||||
if (!_shouldSkipMetrics(request)) {
|
||||
ClsiMetrics.processOutputFilesDurationSeconds.observe(
|
||||
{
|
||||
compile: request.metricsOpts.compile,
|
||||
group: request.compileGroup,
|
||||
},
|
||||
timings.output / 1000
|
||||
)
|
||||
}
|
||||
return { outputFiles, allEntries, buildId }
|
||||
}
|
||||
|
||||
@@ -770,6 +802,10 @@ function _isImageNameAllowed(imageName) {
|
||||
return !ALLOWED_IMAGES || ALLOWED_IMAGES.includes(imageName)
|
||||
}
|
||||
|
||||
function _shouldSkipMetrics(request) {
|
||||
return ['clsi-perf', 'health-check'].includes(request.metricsOpts.path)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
doCompileWithLock: callbackify(doCompileWithLock),
|
||||
stopCompile: callbackify(stopCompile),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const logger = require('@overleaf/logger')
|
||||
const Metrics = require('./Metrics')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
const os = require('node:os')
|
||||
|
||||
let CACHED_LOAD = {
|
||||
|
||||
@@ -1,3 +1,55 @@
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Sanity-check the conversion and remove this comment.
|
||||
module.exports = require('@overleaf/metrics')
|
||||
const { prom } = require('@overleaf/metrics')
|
||||
|
||||
const COMPILE_TIME_BUCKETS = [
|
||||
0.5, 1, 1.5, 2, 3, 4, 5, 6, 8, 10, 15, 20, 25, 30, 45, 60, 75, 90, 120, 150,
|
||||
180, 210, 240,
|
||||
]
|
||||
|
||||
const compilesTotal = new prom.Counter({
|
||||
name: 'clsi_compiles_total',
|
||||
help: 'Number of compiles',
|
||||
labelNames: [
|
||||
'status',
|
||||
'engine',
|
||||
'compile',
|
||||
'group',
|
||||
'image',
|
||||
'draft',
|
||||
'stop_on_first_error',
|
||||
],
|
||||
})
|
||||
|
||||
const compileDurationSeconds = new prom.Histogram({
|
||||
name: 'clsi_compile_duration_seconds',
|
||||
help: 'Duration of the latexmkrc invocation',
|
||||
buckets: COMPILE_TIME_BUCKETS,
|
||||
labelNames: ['status', 'engine', 'compile', 'group'],
|
||||
})
|
||||
|
||||
const e2eCompileDurationSeconds = new prom.Histogram({
|
||||
name: 'clsi_e2e_compile_duration_seconds',
|
||||
help: 'Duration of the entire compile request in clsi (sync, latexmk, output)',
|
||||
buckets: COMPILE_TIME_BUCKETS,
|
||||
})
|
||||
|
||||
const syncResourcesDurationSeconds = new prom.Histogram({
|
||||
name: 'clsi_sync_resources_duration_seconds',
|
||||
help: 'Time it takes to prepare files for a compile',
|
||||
buckets: [0.1, 0.25, 0.5, 0.75, 1, 2, 3, 5, 10, 15, 30, 45, 60],
|
||||
labelNames: ['type', 'compile', 'group'],
|
||||
})
|
||||
|
||||
const processOutputFilesDurationSeconds = new prom.Histogram({
|
||||
name: 'clsi_process_output_files_duration_seconds',
|
||||
help: 'Time it takes to process output files of a compile',
|
||||
buckets: [0.1, 0.25, 0.5, 0.75, 1, 2, 3, 5, 7.5, 10],
|
||||
labelNames: ['compile', 'group'],
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
compilesTotal,
|
||||
compileDurationSeconds,
|
||||
e2eCompileDurationSeconds,
|
||||
syncResourcesDurationSeconds,
|
||||
processOutputFilesDurationSeconds,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ const logger = require('@overleaf/logger')
|
||||
const _ = require('lodash')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const crypto = require('node:crypto')
|
||||
const Metrics = require('./Metrics')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
|
||||
const OutputFileOptimiser = require('./OutputFileOptimiser')
|
||||
const ContentCacheManager = require('./ContentCacheManager')
|
||||
|
||||
@@ -17,7 +17,7 @@ const fs = require('node:fs')
|
||||
const Path = require('node:path')
|
||||
const { spawn } = require('node:child_process')
|
||||
const logger = require('@overleaf/logger')
|
||||
const Metrics = require('./Metrics')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
const _ = require('lodash')
|
||||
|
||||
module.exports = OutputFileOptimiser = {
|
||||
|
||||
@@ -20,7 +20,7 @@ const fs = require('node:fs')
|
||||
const async = require('async')
|
||||
const OutputFileFinder = require('./OutputFileFinder')
|
||||
const ResourceStateManager = require('./ResourceStateManager')
|
||||
const Metrics = require('./Metrics')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
const logger = require('@overleaf/logger')
|
||||
const settings = require('@overleaf/settings')
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const Settings = require('@overleaf/settings')
|
||||
const fs = require('node:fs')
|
||||
const Path = require('node:path')
|
||||
const { callbackify } = require('node:util')
|
||||
const Metrics = require('./Metrics')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
|
||||
const PENDING_DOWNLOADS = new Map()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const {
|
||||
} = require('@overleaf/fetch-utils')
|
||||
const { URL } = require('node:url')
|
||||
const { pipeline } = require('node:stream/promises')
|
||||
const Metrics = require('./Metrics')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
|
||||
const MAX_CONNECT_TIME = 1000
|
||||
const httpAgent = new CustomHttpAgent({ connectTimeout: MAX_CONNECT_TIME })
|
||||
|
||||
@@ -1,97 +1,95 @@
|
||||
const Client = require('./helpers/Client')
|
||||
const { fetchString, fetchNothing } = require('@overleaf/fetch-utils')
|
||||
const { fetchNothing } = require('@overleaf/fetch-utils')
|
||||
const ClsiApp = require('./helpers/ClsiApp')
|
||||
const Settings = require('@overleaf/settings')
|
||||
|
||||
describe('Simple LaTeX file', function () {
|
||||
before(async function () {
|
||||
this.project_id = Client.randomId()
|
||||
this.request = {
|
||||
resources: [
|
||||
{
|
||||
path: 'main.tex',
|
||||
content: `\
|
||||
const content = `\
|
||||
\\documentclass{article}
|
||||
\\begin{document}
|
||||
Hello world
|
||||
\\end{document}\
|
||||
`,
|
||||
},
|
||||
],
|
||||
options: {
|
||||
metricsPath: 'clsi-perf',
|
||||
metricsMethod: 'priority',
|
||||
`
|
||||
const scenarios = [
|
||||
{
|
||||
description: 'simple file',
|
||||
request: {
|
||||
resources: [{ path: 'main.tex', content }],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'clsi-perf request',
|
||||
request: {
|
||||
resources: [{ path: 'main.tex', content }],
|
||||
options: {
|
||||
metricsPath: 'clsi-perf',
|
||||
metricsMethod: 'priority',
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
await ClsiApp.ensureRunning()
|
||||
try {
|
||||
this.body = await Client.compile(this.project_id, this.request)
|
||||
} catch (error) {
|
||||
this.error = error
|
||||
}
|
||||
})
|
||||
for (const scenario of scenarios) {
|
||||
describe(scenario.description, function () {
|
||||
before(async function () {
|
||||
this.project_id = Client.randomId()
|
||||
this.request = scenario.request
|
||||
|
||||
it('should return the PDF', function () {
|
||||
const pdf = Client.getOutputFile(this.body, 'pdf')
|
||||
pdf.type.should.equal('pdf')
|
||||
})
|
||||
await ClsiApp.ensureRunning()
|
||||
try {
|
||||
this.body = await Client.compile(this.project_id, this.request)
|
||||
} catch (error) {
|
||||
this.error = error
|
||||
}
|
||||
})
|
||||
|
||||
it('should return the log', function () {
|
||||
const log = Client.getOutputFile(this.body, 'log')
|
||||
log.type.should.equal('log')
|
||||
})
|
||||
it('should return the PDF', function () {
|
||||
const pdf = Client.getOutputFile(this.body, 'pdf')
|
||||
pdf.type.should.equal('pdf')
|
||||
})
|
||||
|
||||
it('should provide the pdf for download', async function () {
|
||||
const pdf = Client.getOutputFile(this.body, 'pdf')
|
||||
const response = await fetchNothing(pdf.url)
|
||||
response.status.should.equal(200)
|
||||
})
|
||||
it('should return the log', function () {
|
||||
const log = Client.getOutputFile(this.body, 'log')
|
||||
log.type.should.equal('log')
|
||||
})
|
||||
|
||||
it('should provide the log for download', async function () {
|
||||
const log = Client.getOutputFile(this.body, 'pdf')
|
||||
const response = await fetchNothing(log.url)
|
||||
response.status.should.equal(200)
|
||||
})
|
||||
it('should provide the pdf for download', async function () {
|
||||
const pdf = Client.getOutputFile(this.body, 'pdf')
|
||||
const response = await fetchNothing(pdf.url)
|
||||
response.status.should.equal(200)
|
||||
})
|
||||
|
||||
it('should gather personalized metrics', async function () {
|
||||
const body = await fetchString(`${Settings.apis.clsi.url}/metrics`)
|
||||
body
|
||||
.split('\n')
|
||||
.some(line => {
|
||||
return (
|
||||
line.startsWith('compile') &&
|
||||
line.includes('path="clsi-perf"') &&
|
||||
line.includes('method="priority"')
|
||||
it('should provide the log for download', async function () {
|
||||
const log = Client.getOutputFile(this.body, 'pdf')
|
||||
const response = await fetchNothing(log.url)
|
||||
response.status.should.equal(200)
|
||||
})
|
||||
|
||||
it('should return only the expected keys for stats and timings', function () {
|
||||
const { stats, timings } = this.body.compile
|
||||
// Note: chai's all.keys assertion rejects extra keys
|
||||
stats.should.have.all.keys(
|
||||
'isInitialCompile',
|
||||
'latexmk-errors',
|
||||
'latex-runs',
|
||||
'latex-runs-with-errors',
|
||||
'latex-runs-2',
|
||||
'latex-runs-with-errors-2',
|
||||
'pdf-caching-total-ranges-size',
|
||||
'pdf-caching-reclaimed-space',
|
||||
'pdf-caching-new-ranges-size',
|
||||
'pdf-caching-n-ranges',
|
||||
'pdf-caching-n-new-ranges',
|
||||
'pdf-size'
|
||||
)
|
||||
timings.should.have.all.keys(
|
||||
'sync',
|
||||
'compile',
|
||||
'output',
|
||||
'compileE2E',
|
||||
'compute-pdf-caching',
|
||||
'pdf-caching-overhead-delete-stale-hashes'
|
||||
)
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should return only the expected keys for stats and timings', function () {
|
||||
const { stats, timings } = this.body.compile
|
||||
// Note: chai's all.keys assertion rejects extra keys
|
||||
stats.should.have.all.keys(
|
||||
'isInitialCompile',
|
||||
'latexmk-errors',
|
||||
'latex-runs',
|
||||
'latex-runs-with-errors',
|
||||
'latex-runs-2',
|
||||
'latex-runs-with-errors-2',
|
||||
'pdf-caching-total-ranges-size',
|
||||
'pdf-caching-reclaimed-space',
|
||||
'pdf-caching-new-ranges-size',
|
||||
'pdf-caching-n-ranges',
|
||||
'pdf-caching-n-new-ranges',
|
||||
'pdf-size'
|
||||
)
|
||||
timings.should.have.all.keys(
|
||||
'sync',
|
||||
'compile',
|
||||
'output',
|
||||
'compileE2E',
|
||||
'compute-pdf-caching',
|
||||
'pdf-caching-overhead-delete-stale-hashes'
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ const Path = require('node:path')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const Metrics = require('../../../app/js/Metrics')
|
||||
|
||||
const MODULE_PATH = require('node:path').join(
|
||||
__dirname,
|
||||
@@ -177,6 +178,7 @@ describe('CompileManager', function () {
|
||||
'./CLSICacheHandler': this.CLSICacheHandler,
|
||||
'./LatexMetrics': this.LatexMetrics,
|
||||
'./StatsManager': this.StatsManager,
|
||||
'./Metrics': Metrics,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('DockerRunner', function () {
|
||||
},
|
||||
}),
|
||||
}),
|
||||
'./Metrics': {
|
||||
'@overleaf/metrics': {
|
||||
Timer: (Timer = class Timer {
|
||||
done() {}
|
||||
}),
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('OutputFileOptimiser', function () {
|
||||
fs: (this.fs = {}),
|
||||
path: (this.Path = {}),
|
||||
child_process: { spawn: (this.spawn = sinon.stub()) },
|
||||
'./Metrics': {},
|
||||
'@overleaf/metrics': {},
|
||||
},
|
||||
globals: { Math }, // used by lodash
|
||||
})
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('ResourceWriter', function () {
|
||||
createProjectDir: sinon.stub().yields(),
|
||||
}),
|
||||
'./OutputFileFinder': (this.OutputFileFinder = {}),
|
||||
'./Metrics': (this.Metrics = {
|
||||
'@overleaf/metrics': (this.Metrics = {
|
||||
inc: sinon.stub(),
|
||||
Timer: (Timer = (function () {
|
||||
Timer = class Timer {
|
||||
|
||||
Reference in New Issue
Block a user