LatexRunnerTests.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. const SandboxedModule = require('sandboxed-module')
  2. const sinon = require('sinon')
  3. const { expect } = require('chai')
  4. const MODULE_PATH = require('path').join(
  5. __dirname,
  6. '../../../app/js/LatexRunner'
  7. )
  8. describe('LatexRunner', function () {
  9. beforeEach(function () {
  10. this.Settings = {
  11. docker: {
  12. socketPath: '/var/run/docker.sock',
  13. },
  14. }
  15. this.commandRunnerOutput = {
  16. stdout: 'this is stdout',
  17. stderr: 'this is stderr',
  18. }
  19. this.CommandRunner = {
  20. run: sinon.stub().yields(null, this.commandRunnerOutput),
  21. }
  22. this.fs = {
  23. writeFile: sinon.stub().yields(),
  24. }
  25. this.LatexRunner = SandboxedModule.require(MODULE_PATH, {
  26. requires: {
  27. '@overleaf/settings': this.Settings,
  28. './CommandRunner': this.CommandRunner,
  29. fs: this.fs,
  30. },
  31. })
  32. this.directory = '/local/compile/directory'
  33. this.mainFile = 'main-file.tex'
  34. this.compiler = 'pdflatex'
  35. this.image = 'example.com/image'
  36. this.compileGroup = 'compile-group'
  37. this.callback = sinon.stub()
  38. this.project_id = 'project-id-123'
  39. this.env = { foo: '123' }
  40. this.timeout = 42000
  41. this.flags = []
  42. this.stopOnFirstError = false
  43. this.stats = {}
  44. this.timings = {}
  45. this.call = function (callback) {
  46. this.LatexRunner.runLatex(
  47. this.project_id,
  48. {
  49. directory: this.directory,
  50. mainFile: this.mainFile,
  51. compiler: this.compiler,
  52. timeout: this.timeout,
  53. image: this.image,
  54. environment: this.env,
  55. compileGroup: this.compileGroup,
  56. flags: this.flags,
  57. stopOnFirstError: this.stopOnFirstError,
  58. timings: this.timings,
  59. stats: this.stats,
  60. },
  61. callback
  62. )
  63. }
  64. })
  65. describe('runLatex', function () {
  66. describe('normally', function () {
  67. beforeEach(function (done) {
  68. this.call(done)
  69. })
  70. it('should run the latex command', function () {
  71. this.CommandRunner.run.should.have.been.calledWith(
  72. this.project_id,
  73. [
  74. 'latexmk',
  75. '-cd',
  76. '-jobname=output',
  77. '-auxdir=$COMPILE_DIR',
  78. '-outdir=$COMPILE_DIR',
  79. '-synctex=1',
  80. '-interaction=batchmode',
  81. '-f',
  82. '-pdf',
  83. '$COMPILE_DIR/main-file.tex',
  84. ],
  85. this.directory,
  86. this.image,
  87. this.timeout,
  88. this.env,
  89. this.compileGroup
  90. )
  91. })
  92. it('should record the stdout and stderr', function () {
  93. this.fs.writeFile.should.have.been.calledWith(
  94. this.directory + '/' + 'output.stdout',
  95. 'this is stdout'
  96. )
  97. this.fs.writeFile.should.have.been.calledWith(
  98. this.directory + '/' + 'output.stderr',
  99. 'this is stderr'
  100. )
  101. })
  102. it('should not record cpu metrics', function () {
  103. expect(this.timings['cpu-percent']).to.not.exist
  104. expect(this.timings['cpu-time']).to.not.exist
  105. expect(this.timings['sys-time']).to.not.exist
  106. })
  107. })
  108. describe('with a different compiler', function () {
  109. beforeEach(function (done) {
  110. this.compiler = 'lualatex'
  111. this.call(done)
  112. })
  113. it('should set the appropriate latexmk flag', function () {
  114. this.CommandRunner.run.should.have.been.calledWith(this.project_id, [
  115. 'latexmk',
  116. '-cd',
  117. '-jobname=output',
  118. '-auxdir=$COMPILE_DIR',
  119. '-outdir=$COMPILE_DIR',
  120. '-synctex=1',
  121. '-interaction=batchmode',
  122. '-f',
  123. '-lualatex',
  124. '$COMPILE_DIR/main-file.tex',
  125. ])
  126. })
  127. })
  128. describe('with time -v', function () {
  129. beforeEach(function (done) {
  130. this.commandRunnerOutput.stderr =
  131. '\tCommand being timed: "sh -c timeout 1 yes > /dev/null"\n' +
  132. '\tUser time (seconds): 0.28\n' +
  133. '\tSystem time (seconds): 0.70\n' +
  134. '\tPercent of CPU this job got: 98%\n'
  135. this.call(done)
  136. })
  137. it('should record cpu metrics', function () {
  138. expect(this.timings['cpu-percent']).to.equal(98)
  139. expect(this.timings['cpu-time']).to.equal(0.28)
  140. expect(this.timings['sys-time']).to.equal(0.7)
  141. })
  142. })
  143. describe('with an .Rtex main file', function () {
  144. beforeEach(function (done) {
  145. this.mainFile = 'main-file.Rtex'
  146. this.call(done)
  147. })
  148. it('should run the latex command on the equivalent .tex file', function () {
  149. const command = this.CommandRunner.run.args[0][1]
  150. const mainFile = command.slice(-1)[0]
  151. mainFile.should.equal('$COMPILE_DIR/main-file.tex')
  152. })
  153. })
  154. describe('with a flags option', function () {
  155. beforeEach(function (done) {
  156. this.flags = ['-shell-restricted', '-halt-on-error']
  157. this.call(done)
  158. })
  159. it('should include the flags in the command', function () {
  160. const command = this.CommandRunner.run.args[0][1]
  161. const flags = command.filter(
  162. arg => arg === '-shell-restricted' || arg === '-halt-on-error'
  163. )
  164. flags.length.should.equal(2)
  165. flags[0].should.equal('-shell-restricted')
  166. flags[1].should.equal('-halt-on-error')
  167. })
  168. })
  169. describe('with the stopOnFirstError option', function () {
  170. beforeEach(function (done) {
  171. this.stopOnFirstError = true
  172. this.call(done)
  173. })
  174. it('should set the appropriate flags', function () {
  175. this.CommandRunner.run.should.have.been.calledWith(this.project_id, [
  176. 'latexmk',
  177. '-cd',
  178. '-jobname=output',
  179. '-auxdir=$COMPILE_DIR',
  180. '-outdir=$COMPILE_DIR',
  181. '-synctex=1',
  182. '-interaction=batchmode',
  183. '-halt-on-error',
  184. '-pdf',
  185. '$COMPILE_DIR/main-file.tex',
  186. ])
  187. })
  188. })
  189. })
  190. })