mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
336 lines
9.6 KiB
JavaScript
336 lines
9.6 KiB
JavaScript
import { vi, expect, describe, beforeEach, it } from 'vitest'
|
|
import sinon from 'sinon'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
const MODULE_PATH = path.join(
|
|
import.meta.dirname,
|
|
'../../../app/js/LatexRunner'
|
|
)
|
|
|
|
describe('LatexRunner', () => {
|
|
beforeEach(async ctx => {
|
|
ctx.Settings = {
|
|
docker: {
|
|
socketPath: '/var/run/docker.sock',
|
|
},
|
|
}
|
|
ctx.commandRunnerOutput = {
|
|
stdout: 'this is stdout',
|
|
stderr: 'this is stderr',
|
|
}
|
|
ctx.CommandRunner = {
|
|
run: sinon.stub().yields(null, ctx.commandRunnerOutput),
|
|
}
|
|
ctx.fs = {
|
|
writeFile: sinon.stub().yields(),
|
|
unlink: sinon
|
|
.stub()
|
|
.yields(new Error('ENOENT: no such file or directory, unlink ...')),
|
|
}
|
|
|
|
vi.doMock('@overleaf/settings', () => ({
|
|
default: ctx.Settings,
|
|
}))
|
|
|
|
vi.doMock('../../../app/js/CommandRunner', () => ({
|
|
default: ctx.CommandRunner,
|
|
}))
|
|
|
|
vi.doMock('fs', () => ({
|
|
default: ctx.fs,
|
|
}))
|
|
|
|
ctx.LatexRunner = (await import(MODULE_PATH)).default
|
|
|
|
ctx.directory = '/local/compile/directory'
|
|
ctx.mainFile = 'main-file.tex'
|
|
ctx.compiler = 'pdflatex'
|
|
ctx.image = 'example.com/image'
|
|
ctx.compileGroup = 'compile-group'
|
|
ctx.callback = sinon.stub()
|
|
ctx.project_id = 'project-id-123'
|
|
ctx.env = { foo: '123' }
|
|
ctx.timeout = 42000
|
|
ctx.flags = []
|
|
ctx.stopOnFirstError = false
|
|
ctx.stats = {}
|
|
ctx.timings = {}
|
|
|
|
ctx.call = function (callback) {
|
|
this.LatexRunner.runLatex(
|
|
this.project_id,
|
|
{
|
|
directory: this.directory,
|
|
mainFile: this.mainFile,
|
|
compiler: this.compiler,
|
|
timeout: this.timeout,
|
|
image: this.image,
|
|
environment: this.env,
|
|
compileGroup: this.compileGroup,
|
|
flags: this.flags,
|
|
stopOnFirstError: this.stopOnFirstError,
|
|
timings: this.timings,
|
|
stats: this.stats,
|
|
},
|
|
callback
|
|
)
|
|
}
|
|
})
|
|
|
|
describe('runLatex', () => {
|
|
describe('normally', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should run the latex command', ctx => {
|
|
ctx.CommandRunner.run.should.have.been.calledWith(
|
|
ctx.project_id,
|
|
[
|
|
'latexmk',
|
|
'-cd',
|
|
'-jobname=output',
|
|
'-auxdir=$COMPILE_DIR',
|
|
'-outdir=$COMPILE_DIR',
|
|
'-synctex=1',
|
|
'-interaction=batchmode',
|
|
'-time',
|
|
'-f',
|
|
'-pdf',
|
|
'$COMPILE_DIR/main-file.tex',
|
|
],
|
|
ctx.directory,
|
|
ctx.image,
|
|
ctx.timeout,
|
|
ctx.env,
|
|
ctx.compileGroup
|
|
)
|
|
})
|
|
|
|
it('should record the stdout and stderr', ctx => {
|
|
ctx.fs.writeFile.should.have.been.calledWith(
|
|
ctx.directory + '/' + 'output.stdout',
|
|
'this is stdout',
|
|
{ flag: 'wx' }
|
|
)
|
|
ctx.fs.writeFile.should.have.been.calledWith(
|
|
ctx.directory + '/' + 'output.stderr',
|
|
'this is stderr',
|
|
{ flag: 'wx' }
|
|
)
|
|
ctx.fs.unlink.should.have.been.calledWith(
|
|
ctx.directory + '/' + 'output.stdout'
|
|
)
|
|
ctx.fs.unlink.should.have.been.calledWith(
|
|
ctx.directory + '/' + 'output.stderr'
|
|
)
|
|
})
|
|
|
|
it('should not record cpu metrics', ctx => {
|
|
expect(ctx.timings['cpu-percent']).to.not.exist
|
|
expect(ctx.timings['cpu-time']).to.not.exist
|
|
expect(ctx.timings['sys-time']).to.not.exist
|
|
})
|
|
})
|
|
|
|
describe('with a different compiler', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.compiler = 'lualatex'
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should set the appropriate latexmk flag', ctx => {
|
|
ctx.CommandRunner.run.should.have.been.calledWith(ctx.project_id, [
|
|
'latexmk',
|
|
'-cd',
|
|
'-jobname=output',
|
|
'-auxdir=$COMPILE_DIR',
|
|
'-outdir=$COMPILE_DIR',
|
|
'-synctex=1',
|
|
'-interaction=batchmode',
|
|
'-time',
|
|
'-f',
|
|
'-lualatex',
|
|
'$COMPILE_DIR/main-file.tex',
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('with time -v', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.commandRunnerOutput.stderr =
|
|
'\tCommand being timed: "sh -c timeout 1 yes > /dev/null"\n' +
|
|
'\tUser time (seconds): 0.28\n' +
|
|
'\tSystem time (seconds): 0.70\n' +
|
|
'\tPercent of CPU this job got: 98%\n'
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should record cpu metrics', ctx => {
|
|
expect(ctx.timings['cpu-percent']).to.equal(98)
|
|
expect(ctx.timings['cpu-time']).to.equal(0.28)
|
|
expect(ctx.timings['sys-time']).to.equal(0.7)
|
|
})
|
|
})
|
|
|
|
describe('with an .Rtex main file', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.mainFile = 'main-file.Rtex'
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should run the latex command on the equivalent .tex file', ctx => {
|
|
const command = ctx.CommandRunner.run.args[0][1]
|
|
const mainFile = command.slice(-1)[0]
|
|
mainFile.should.equal('$COMPILE_DIR/main-file.tex')
|
|
})
|
|
})
|
|
|
|
describe('with a flags option', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.flags = ['-shell-restricted', '-halt-on-error']
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should include the flags in the command', ctx => {
|
|
const command = ctx.CommandRunner.run.args[0][1]
|
|
const flags = command.filter(
|
|
arg => arg === '-shell-restricted' || arg === '-halt-on-error'
|
|
)
|
|
flags.length.should.equal(2)
|
|
flags[0].should.equal('-shell-restricted')
|
|
flags[1].should.equal('-halt-on-error')
|
|
})
|
|
})
|
|
|
|
describe('with the stopOnFirstError option', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.stopOnFirstError = true
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should set the appropriate flags', ctx => {
|
|
ctx.CommandRunner.run.should.have.been.calledWith(ctx.project_id, [
|
|
'latexmk',
|
|
'-cd',
|
|
'-jobname=output',
|
|
'-auxdir=$COMPILE_DIR',
|
|
'-outdir=$COMPILE_DIR',
|
|
'-synctex=1',
|
|
'-interaction=batchmode',
|
|
'-time',
|
|
'-halt-on-error',
|
|
'-pdf',
|
|
'$COMPILE_DIR/main-file.tex',
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('with old latexmk timing output', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.commandRunnerOutput.stdout = fs.readFileSync(
|
|
path.join(import.meta.dirname, 'fixtures', 'latexmk1.txt'),
|
|
'utf-8'
|
|
)
|
|
// pass in the `latexmk` property to signal that we want to receive parsed stats
|
|
ctx.stats.latexmk = {}
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should parse latexmk 4.52c (2017) timing information', ctx => {
|
|
expect(ctx.stats.latexmk).to.deep.equal({
|
|
'latexmk-rule-times': [
|
|
{ rule: 'makeindex', time_ms: 30 },
|
|
{ rule: 'bibtex', time_ms: 40 },
|
|
{ rule: 'latex', time_ms: 690 },
|
|
{ rule: 'makeindex', time_ms: 40 },
|
|
{ rule: 'bibtex', time_ms: 39 },
|
|
{ rule: 'latex', time_ms: 750 },
|
|
{ rule: 'makeindex', time_ms: 39 },
|
|
{ rule: 'bibtex', time_ms: 20 },
|
|
{ rule: 'latex', time_ms: 770 },
|
|
],
|
|
'latexmk-rule-signature':
|
|
'makeindex,bibtex,latex,makeindex,bibtex,latex,makeindex,bibtex,latex',
|
|
'latexmk-rules-run': 9,
|
|
'latexmk-time': { total: 2930 },
|
|
'latexmk-img-times': [],
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with modern latexmk timing output', () => {
|
|
beforeEach(async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.commandRunnerOutput.stdout = fs.readFileSync(
|
|
path.join(import.meta.dirname, 'fixtures', 'latexmk2.txt'),
|
|
'utf-8'
|
|
)
|
|
// pass in the `latexmk` property to signal that we want to receive parsed stats
|
|
ctx.stats.latexmk = {}
|
|
ctx.call(err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should parse latexmk 4.83 (2024) timing information', ctx => {
|
|
expect(ctx.stats.latexmk).to.deep.equal({
|
|
'latexmk-rule-times': [
|
|
{ rule: 'latex', time_ms: 1880 },
|
|
{ rule: 'makeindex', time_ms: 50 },
|
|
{ rule: 'bibtex', time_ms: 50 },
|
|
{ rule: 'latex', time_ms: 2180 },
|
|
],
|
|
'latexmk-rule-signature': 'latex,makeindex,bibtex,latex',
|
|
'latexmk-time': {
|
|
total: 4770,
|
|
invoked: 4160,
|
|
other: 610,
|
|
},
|
|
'latexmk-clock-time': 4870,
|
|
'latexmk-rules-run': 4,
|
|
'latexmk-img-times': [],
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|