Skip to content

Commit

Permalink
First batch of build unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Yu committed Sep 8, 2024
1 parent 44f0282 commit 5559e5a
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 46 deletions.
13 changes: 6 additions & 7 deletions src/compile/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as vscode from 'vscode'
import * as path from 'path'
import * as cs from 'cross-spawn'
import { pickRootPath } from '../utils/quick-pick'
import { lw } from '../lw'
import type { ProcessEnv, RecipeStep, Step } from '../types'
Expand Down Expand Up @@ -208,20 +207,20 @@ function spawnProcess(step: Step): ProcessEnv {
const args = step.args
if (args && !step.name.endsWith(lw.constant.MAGIC_PROGRAM_ARGS_SUFFIX)) {
// All optional arguments are given as a unique string (% !TeX options) if any, so we use {shell: true}
lw.compile.process = cs.spawn(`${step.command} ${args[0]}`, [], {cwd: path.dirname(step.rootFile), env, shell: true})
lw.compile.process = lw.external.spawn(`${step.command} ${args[0]}`, [], {cwd: path.dirname(step.rootFile), env, shell: true})
} else {
lw.compile.process = cs.spawn(step.command, args, {cwd: path.dirname(step.rootFile), env})
lw.compile.process = lw.external.spawn(step.command, args ?? [], {cwd: path.dirname(step.rootFile), env})
}
} else if (!step.isExternal) {
let cwd = path.dirname(step.rootFile)
if (step.command === 'latexmk' && step.rootFile === lw.root.subfiles.path && lw.root.dir.path) {
cwd = lw.root.dir.path
}
logger.log(`cwd: ${cwd}`)
lw.compile.process = cs.spawn(step.command, step.args, {cwd, env})
lw.compile.process = lw.external.spawn(step.command, step.args ?? [], {cwd, env})
} else {
logger.log(`cwd: ${step.cwd}`)
lw.compile.process = cs.spawn(step.command, step.args, {cwd: step.cwd})
lw.compile.process = lw.external.spawn(step.command, step.args ?? [], {cwd: step.cwd})
}
logger.log(`LaTeX build process spawned with PID ${lw.compile.process.pid}.`)
return env
Expand All @@ -245,13 +244,13 @@ async function monitorProcess(step: Step, env: ProcessEnv): Promise<boolean> {
return false
}
let stdout = ''
lw.compile.process.stdout.on('data', (msg: Buffer | string) => {
lw.compile.process.stdout?.on('data', (msg: Buffer | string) => {
stdout += msg
logger.logCompiler(msg.toString())
})

let stderr = ''
lw.compile.process.stderr.on('data', (msg: Buffer | string) => {
lw.compile.process.stderr?.on('data', (msg: Buffer | string) => {
stderr += msg
logger.logCompiler(msg.toString())
})
Expand Down
4 changes: 2 additions & 2 deletions src/compile/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ChildProcessWithoutNullStreams } from 'child_process'
import type { ChildProcess } from 'child_process'
import type { Step } from '../types'
import { build, autoBuild } from './build'
import { terminate } from './terminate'
Expand All @@ -11,5 +11,5 @@ export const compile = {
lastAutoBuildTime: 0,
compiledPDFPath: '',
compiledPDFWriting: 0,
process: undefined as ChildProcessWithoutNullStreams | undefined
process: undefined as ChildProcess | undefined
}
49 changes: 24 additions & 25 deletions test/units/06_compile_recipe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { build, initialize } from '../../src/compile/recipe'
import { queue } from '../../src/compile/queue'

describe(path.basename(__filename).split('.')[0] + ':', () => {
const fixture = path.basename(__filename).split('.')[0]
let getOutDirStub: sinon.SinonStub
let getIncludedTeXStub: sinon.SinonStub
let mkdirStub: sinon.SinonStub
Expand Down Expand Up @@ -62,7 +61,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
describe('lw.compile->recipe.build', () => {
it('should call `saveAll` before building', async () => {
const stub = sinon.stub(vscode.workspace, 'saveAll') as sinon.SinonStub
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build(rootFile, 'latex', async () => {})
stub.restore()
Expand All @@ -71,8 +70,8 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should call `createOutputSubFolders` with correct args', async () => {
const rootFile = set.root(fixture, 'main.tex')
const subPath = get.path(fixture, 'sub', 'main.tex')
const rootFile = set.root('main.tex')
const subPath = get.path('sub', 'main.tex')
await set.config('latex.tools', [{ name: 'latexmk', command: 'latexmk' }])
await set.config('latex.recipes', [{ name: 'Recipe1', tools: ['latexmk'] }])
lw.root.subfiles.path = subPath
Expand All @@ -83,8 +82,8 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should call `createOutputSubFolders` with correct args with subfiles package', async () => {
const rootFile = set.root(fixture, 'main.tex')
const subPath = get.path(fixture, 'sub', 'main.tex')
const rootFile = set.root('main.tex')
const subPath = get.path('sub', 'main.tex')
await set.config('latex.tools', [{ name: 'latexmk', command: 'latexmk' }])
await set.config('latex.recipes', [{ name: 'Recipe1', tools: ['latexmk'] }])
lw.root.subfiles.path = subPath
Expand All @@ -95,7 +94,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should not call buildLoop if no tool is created', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
await set.config('latex.tools', [])
await set.config('latex.recipes', [{ name: 'Recipe1', tools: ['nonexistentTool'] }])

Expand All @@ -106,7 +105,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should set lw.compile.compiledPDFPath', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
await set.config('latex.tools', [{ name: 'latexmk', command: 'latexmk' }])
await set.config('latex.recipes', [{ name: 'Recipe1', tools: ['latexmk'] }])

Expand All @@ -128,7 +127,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should do nothing but log an error if no recipe is found', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
await set.config('latex.recipes', [])

await build(rootFile, 'latex', async () => {})
Expand All @@ -137,7 +136,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should create build tools based on magic comments when enabled', async () => {
const rootFile = set.root(fixture, 'magic.tex')
const rootFile = set.root('magic.tex')
readStub.resolves('% !TEX program = pdflatex\n')
await set.config('latex.recipes', [])
await set.config('latex.build.forceRecipeUsage', false)
Expand All @@ -153,7 +152,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should do nothing but log an error with magic comments but disabled', async () => {
const rootFile = set.root(fixture, 'magic.tex')
const rootFile = set.root('magic.tex')
await set.config('latex.recipes', [])
await set.config('latex.build.forceRecipeUsage', true)

Expand All @@ -163,7 +162,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should skip undefined tools in the recipe and log an error', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
await set.config('latex.tools', [{ name: 'existingTool', command: 'pdflatex' }])
await set.config('latex.recipes', [{ name: 'Recipe1', tools: ['nonexistentTool', 'existingTool'] }])

Expand All @@ -178,7 +177,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should do nothing but log an error if no tools are prepared', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
await set.config('latex.tools', [])
await set.config('latex.recipes', [{ name: 'Recipe1', tools: ['nonexistentTool'] }])

Expand All @@ -191,15 +190,15 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
describe('lw.compile->recipe.createOutputSubFolders', () => {

beforeEach(() => {
getIncludedTeXStub.returns([ set.root(fixture, 'main.tex') ])
getIncludedTeXStub.returns([ set.root('main.tex') ])
})

afterEach(() => {
getOutDirStub.returns('.')
})

it('should resolve the output directory relative to the root directory if not absolute', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
const relativeOutDir = 'output'
const expectedOutDir = path.resolve(path.dirname(rootFile), relativeOutDir)
getOutDirStub.returns(relativeOutDir)
Expand All @@ -210,7 +209,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should use the absolute output directory as is', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
const absoluteOutDir = '/absolute/output'
getOutDirStub.returns(absoluteOutDir)

Expand All @@ -220,7 +219,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should create the output directory if it does not exist', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
const relativeOutDir = 'output'
const expectedOutDir = path.resolve(path.dirname(rootFile), relativeOutDir)
const stub = sinon.stub(lw.file, 'exists').resolves(false)
Expand All @@ -234,7 +233,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should not create the output directory if it already exists', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
const relativeOutDir = 'output'
const stub = sinon.stub(lw.file, 'exists').resolves({ type: vscode.FileType.Directory, ctime: 0, mtime: 0, size: 0 })
getOutDirStub.returns(relativeOutDir)
Expand Down Expand Up @@ -659,15 +658,15 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {

it('should replace argument placeholders', async () => {
await set.config('latex.tools', [{ name: 'latexmk', command: 'latexmk', args: ['%DOC%', '%DOC%', '%DIR%'], env: {} }])
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build(rootFile, 'latex', async () => {})

const step = queue.getStep()
assert.ok(step)
assert.pathStrictEqual(step.args?.[0], rootFile.replace('.tex', ''))
assert.pathStrictEqual(step.args?.[1], rootFile.replace('.tex', ''))
assert.pathStrictEqual(step.args?.[2], get.path(fixture))
assert.pathStrictEqual(step.args?.[2], get.path(''))
})

it('should set TeX directories correctly', async () => {
Expand All @@ -679,7 +678,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
env: {},
},
])
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

const stub = sinon.stub(lw.file, 'setTeXDirs')
await build(rootFile, 'latex', async () => {})
Expand All @@ -697,7 +696,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
env: { DOC: '%DOC%' },
},
])
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build(rootFile, 'latex', async () => {})

Expand All @@ -710,7 +709,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
await set.config('latex.option.maxPrintLine.enabled', true)
await set.config('latex.tools', [{ name: 'latexmk', command: 'latexmk' }])
syncStub.returns({ stdout: 'pdfTeX 3.14159265-2.6-1.40.21 (MiKTeX 2.9.7350 64-bit)' })
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build(rootFile, 'latex', async () => {})

Expand Down Expand Up @@ -758,7 +757,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {

it('should not consider MikTeX logic when pdflatex command fails', async () => {
syncStub.throws(new Error('Command failed'))
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build(rootFile, 'latex', async () => {})

Expand All @@ -769,7 +768,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should not execute compile program again to determine MikTeX if already executed and cached', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')
syncStub.returns({ stdout: 'pdfTeX 3.14159265-2.6-1.40.21 (MiKTeX 2.9.7350 64-bit)' })

await build(rootFile, 'latex', async () => {})
Expand Down
12 changes: 5 additions & 7 deletions test/units/07_compile_external.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { build } from '../../src/compile/external'
import type { ExternalStep } from '../../src/types'

describe(path.basename(__filename).split('.')[0] + ':', () => {
const fixture = path.basename(__filename).split('.')[0]

before(() => {
mock.object(lw, 'file', 'root')
})
Expand All @@ -33,7 +31,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should create a Tool object representing the build command and arguments', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build('command', ['arg1', 'arg2'], '/cwd', sinon.stub(), rootFile)

Expand Down Expand Up @@ -67,7 +65,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
const stub = sinon.stub().returnsArg(0)
const replaceStub = sinon.stub(lwUtils, 'replaceArgumentPlaceholders').returns(stub)
const pathStub = sinon.stub(lw.file, 'getPdfPath').returns('main.pdf')
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build('command', ['arg1', 'arg2'], '/cwd', sinon.stub(), rootFile)
replaceStub.restore()
Expand All @@ -92,11 +90,11 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should set the compiledPDFPath if a root file is provided', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build('command', ['arg1', 'arg2'], '/cwd', sinon.stub(), rootFile)

assert.pathStrictEqual(lw.compile.compiledPDFPath, get.path(fixture, 'main.pdf'))
assert.pathStrictEqual(lw.compile.compiledPDFPath, get.path('main.pdf'))
})

it('should not set the compiledPDFPath if no root file is provided', async () => {
Expand All @@ -114,7 +112,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
})

it('should add the build tool to the queue for execution', async () => {
const rootFile = set.root(fixture, 'main.tex')
const rootFile = set.root('main.tex')

await build('command', ['arg1', 'arg2'], '/cwd', sinon.stub(), rootFile)

Expand Down
Loading

0 comments on commit 5559e5a

Please sign in to comment.