logParserTests.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. import { expect } from 'chai'
  2. import LatexLogParser from '../../../../frontend/js/ide/log-parser/latex-log-parser'
  3. import BibLogParser from '../../../../frontend/js/ide/log-parser/bib-log-parser'
  4. const fixturePath = '../../helpers/fixtures/logs/'
  5. const fs = require('fs')
  6. const path = require('path')
  7. describe('logParser', function (done) {
  8. before(function () {
  9. this.errorLog = fs.readFileSync(
  10. path.resolve(__dirname, fixturePath + 'errors.log'),
  11. {
  12. encoding: 'utf8',
  13. flag: 'r',
  14. }
  15. )
  16. this.warningLog = fs.readFileSync(
  17. path.resolve(__dirname, fixturePath + 'warnings.log'),
  18. {
  19. encoding: 'utf8',
  20. flag: 'r',
  21. }
  22. )
  23. this.badBoxesLog = fs.readFileSync(
  24. path.resolve(__dirname, fixturePath + 'bad-boxes.log'),
  25. {
  26. encoding: 'utf8',
  27. flag: 'r',
  28. }
  29. )
  30. this.biberWarningsLog = fs.readFileSync(
  31. path.resolve(__dirname, fixturePath + 'biber-warnings.log'),
  32. { encoding: 'utf8', flag: 'r' }
  33. )
  34. this.natbibWarningsLog = fs.readFileSync(
  35. path.resolve(__dirname, fixturePath + 'natbib-warnings.log'),
  36. { encoding: 'utf8', flag: 'r' }
  37. )
  38. this.geometryWarningsLog = fs.readFileSync(
  39. path.resolve(__dirname, fixturePath + 'geometry-warnings.log'),
  40. { encoding: 'utf8', flag: 'r' }
  41. )
  42. this.captionWarningsLog = fs.readFileSync(
  43. path.resolve(__dirname, fixturePath + 'caption-warnings.log'),
  44. { encoding: 'utf8', flag: 'r' }
  45. )
  46. this.runawayArgumentsLog = fs.readFileSync(
  47. path.resolve(__dirname, fixturePath + 'runaway-arguments.log'),
  48. { encoding: 'utf8', flag: 'r' }
  49. )
  50. this.biberBlg = fs.readFileSync(
  51. path.resolve(__dirname, fixturePath + 'biber.blg'),
  52. {
  53. encoding: 'utf8',
  54. flag: 'r',
  55. }
  56. )
  57. this.bibtexBlg = fs.readFileSync(
  58. path.resolve(__dirname, fixturePath + 'bibtex.blg'),
  59. {
  60. encoding: 'utf8',
  61. flag: 'r',
  62. }
  63. )
  64. this.fileLineErrorLog = fs.readFileSync(
  65. path.resolve(__dirname, fixturePath + 'file-line-error.log'),
  66. {
  67. encoding: 'utf8',
  68. flag: 'r',
  69. }
  70. )
  71. this.filenamesLog = fs.readFileSync(
  72. path.resolve(__dirname, fixturePath + 'filenames.log'),
  73. {
  74. encoding: 'utf8',
  75. flag: 'r',
  76. }
  77. )
  78. this.secondaryFileLineErrorLog = fs.readFileSync(
  79. path.resolve(__dirname, fixturePath + 'file-line-error-2.log'),
  80. {
  81. encoding: 'utf8',
  82. flag: 'r',
  83. }
  84. )
  85. })
  86. it('should parse errors', function () {
  87. const latexParser = new LatexLogParser(this.errorLog, {
  88. ignoreDuplicates: true,
  89. })
  90. const errors = latexParser.parse().errors
  91. const expectedErrors = [
  92. [29, 'Undefined control sequence.'] + '',
  93. [
  94. 30,
  95. 'LaTeX Error: \\begin{equation} on input line 28 ended by \\end{equaion}.',
  96. ] + '',
  97. [30, 'Missing $ inserted.'] + '',
  98. [30, 'Display math should end with $$.'] + '',
  99. [46, 'Extra }, or forgotten \\right.'] + '',
  100. [46, 'Missing \\right. inserted.'] + '',
  101. [46, 'Missing } inserted.'] + '',
  102. ]
  103. expect(errors.length).to.equal(expectedErrors.length)
  104. for (let i = 0; i < errors.length; i++) {
  105. expect(
  106. expectedErrors.indexOf([errors[i].line, errors[i].message] + '')
  107. ).to.equal(i)
  108. }
  109. })
  110. it('should parse Badbox errors', function () {
  111. const latexParser = new LatexLogParser(this.badBoxesLog)
  112. const errors = latexParser.parse().typesetting
  113. const expectedErrors = [
  114. [9, 'Overfull \\hbox (29.11179pt too wide) in paragraph at lines 9--10'] +
  115. '',
  116. [11, 'Underfull \\hbox (badness 10000) in paragraph at lines 11--13'] +
  117. '',
  118. [27, 'Overfull \\vbox (12.00034pt too high) detected at line 27'] + '',
  119. [46, 'Underfull \\vbox (badness 10000) detected at line 46'] + '',
  120. [54, 'Underfull \\hbox (badness 10000) in paragraph at lines 54--55'] +
  121. '',
  122. [58, 'Underfull \\hbox (badness 10000) in paragraph at lines 58--60'] +
  123. '',
  124. ]
  125. expect(errors.length).to.equal(expectedErrors.length)
  126. for (let i = 0; i < errors.length; i++) {
  127. expect(
  128. expectedErrors.indexOf([errors[i].line, errors[i].message] + '')
  129. ).to.equal(i)
  130. }
  131. })
  132. it('should parse Warnings', function () {
  133. const latexParser = new LatexLogParser(this.warningLog)
  134. const errors = latexParser.parse().warnings
  135. const expectedErrors = [
  136. [
  137. 7,
  138. "Citation `Lambert:2010iw' on page 1 undefined on input line 7.",
  139. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/introduction.tex',
  140. ] + '',
  141. [
  142. 7,
  143. "Citation `Lambert:2010iw' on page 1 undefined on input line 7.",
  144. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/introduction.tex',
  145. ] + '',
  146. [
  147. 72,
  148. "Citation `Manton:2004tk' on page 3 undefined on input line 72.",
  149. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/instantons.tex',
  150. ] + '',
  151. [
  152. 108,
  153. "Citation `Atiyah1978' on page 4 undefined on input line 108.",
  154. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/instantons.tex',
  155. ] + '',
  156. [
  157. 176,
  158. "Citation `Dorey:1996hu' on page 5 undefined on input line 176.",
  159. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/instantons.tex',
  160. ] + '',
  161. [
  162. 3,
  163. "Citation `Manton1982' on page 8 undefined on input line 3.",
  164. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/moduli_space_approximation.tex',
  165. ] + '',
  166. [
  167. 21,
  168. "Citation `Weinberg:2006rq' on page 9 undefined on input line 21.",
  169. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/moduli_space_approximation.tex',
  170. ] + '',
  171. [
  172. 192,
  173. "Citation `Bak:1999sv' on page 12 undefined on input line 192.",
  174. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/moduli_space_approximation.tex',
  175. ] + '',
  176. [
  177. 9,
  178. "Citation `Peeters:2001np' on page 13 undefined on input line 9.",
  179. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/dynamics_of_single_instanton.tex',
  180. ] + '',
  181. [
  182. 27,
  183. "Citation `Osborn:1981yf' on page 15 undefined on input line 27.",
  184. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/dynamics_of_two_instantons.tex',
  185. ] + '',
  186. [
  187. 27,
  188. "Citation `Peeters:2001np' on page 15 undefined on input line 27.",
  189. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/dynamics_of_two_instantons.tex',
  190. ] + '',
  191. [
  192. 20,
  193. "Citation `Osborn:1981yf' on page 22 undefined on input line 20.",
  194. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/appendices.tex',
  195. ] + '',
  196. [
  197. 103,
  198. "Citation `Osborn:1981yf' on page 23 undefined on input line 103.",
  199. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/appendices.tex',
  200. ] + '',
  201. [
  202. 103,
  203. "Citation `Peeters:2001np' on page 23 undefined on input line 103.",
  204. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/appendices.tex',
  205. ] + '',
  206. [
  207. 352,
  208. "Citation `Peeters:2001np' on page 27 undefined on input line 352.",
  209. 'compiles/d1585ce575dea4cab55f784a22a88652/sections/appendices.tex',
  210. ] + '',
  211. ]
  212. // there logs display an additional summary error for undefined references
  213. const offsetErrorLen = errors.length - 1
  214. expect(offsetErrorLen).to.equal(expectedErrors.length)
  215. for (let i = 0; i < offsetErrorLen; i++) {
  216. expect(expectedErrors[i]).to.equal(
  217. [errors[i].line, errors[i].message, errors[i].file] + ''
  218. )
  219. }
  220. })
  221. it('should parse Biber warnings', function () {
  222. const latexParser = new LatexLogParser(this.biberWarningsLog)
  223. const errors = latexParser.parse().warnings
  224. const expectedErrors = [
  225. [
  226. null,
  227. 'Package biblatex Warning: No "backend" specified, using Biber backend. To use BibTeX, load biblatex with the "backend=bibtex" option.',
  228. '/usr/local/texlive/2013/texmf-dist/tex/latex/biblatex/biblatex.sty',
  229. ] + '',
  230. [
  231. null,
  232. 'Package biblatex Warning: The following entry could not be found in the database: Missing3 Please verify the spelling and rerun LaTeX afterwards.',
  233. '/compile/output.bbl',
  234. ] + '',
  235. [
  236. null,
  237. 'Package biblatex Warning: The following entry could not be found in the database: Missing2 Please verify the spelling and rerun LaTeX afterwards.',
  238. '/compile/output.bbl',
  239. ] + '',
  240. [
  241. null,
  242. 'Package biblatex Warning: The following entry could not be found in the database: Missing1 Please verify the spelling and rerun LaTeX afterwards.',
  243. '/compile/output.bbl',
  244. ] + '',
  245. ]
  246. expect(errors.length).to.equal(expectedErrors.length)
  247. for (let i = 0; i < errors.length; i++) {
  248. expect(expectedErrors[i]).to.equal(
  249. [errors[i].line, errors[i].message, errors[i].file] + ''
  250. )
  251. }
  252. })
  253. it('should parse Natbib warnings', function () {
  254. const latexParser = new LatexLogParser(this.natbibWarningsLog)
  255. const errors = latexParser.parse().warnings
  256. const expectedErrors = [
  257. [
  258. 6,
  259. "Package natbib Warning: Citation `blah' on page 1 undefined on input line 6.",
  260. '/compile/main.tex',
  261. ] + '',
  262. [
  263. null,
  264. 'Package natbib Warning: There were undefined citations.',
  265. '/compile/main.tex',
  266. ] + '',
  267. ]
  268. expect(errors.length).to.equal(expectedErrors.length)
  269. for (let i = 0; i < errors.length; i++) {
  270. expect(expectedErrors[i]).to.equal(
  271. [errors[i].line, errors[i].message, errors[i].file] + ''
  272. )
  273. }
  274. })
  275. it('should parse Geometry warnings', function () {
  276. const latexParser = new LatexLogParser(this.geometryWarningsLog)
  277. const errors = latexParser.parse().warnings
  278. const expectedErrors = [
  279. [
  280. null,
  281. "Package geometry Warning: Over-specification in `h'-direction. `width' (597.50787pt) is ignored.",
  282. '/compile/main.tex',
  283. ] + '',
  284. [
  285. null,
  286. "Package geometry Warning: Over-specification in `v'-direction. `height' (845.04684pt) is ignored.",
  287. '/compile/main.tex',
  288. ] + '',
  289. ]
  290. expect(errors.length).to.equal(expectedErrors.length)
  291. for (let i = 0; i < errors.length; i++) {
  292. expect(expectedErrors[i]).to.equal(
  293. [errors[i].line, errors[i].message, errors[i].file] + ''
  294. )
  295. }
  296. })
  297. it('should parse Caption warnings', function () {
  298. const latexParser = new LatexLogParser(this.captionWarningsLog)
  299. const errors = latexParser.parse().warnings
  300. const expectedErrors = [
  301. [
  302. null,
  303. 'Package caption Warning: Unsupported document class (or package) detected, usage of the caption package is not recommended. See the caption package documentation for explanation.',
  304. '/usr/local/texlive/2014/texmf-dist/tex/latex/caption/caption.sty',
  305. ] + '',
  306. [
  307. 46,
  308. "Package caption Warning: The option `hypcap=true' will be ignored for this particular \\caption on input line 46. See the caption package documentation for explanation.",
  309. '/compile/main.tex',
  310. ] + '',
  311. ]
  312. expect(errors.length).to.equal(expectedErrors.length)
  313. for (let i = 0; i < errors.length; i++) {
  314. expect(expectedErrors[i]).to.equal(
  315. [errors[i].line, errors[i].message, errors[i].file] + ''
  316. )
  317. }
  318. })
  319. it('should parse Runaway Arguments', function () {
  320. const latexParser = new LatexLogParser(this.runawayArgumentsLog)
  321. const errors = latexParser.parse().errors
  322. const expectedErrors = [
  323. [null, 'Runaway argument?', '/compile/runaway_argument.tex'] + '',
  324. [null, 'Emergency stop.', '/compile/runaway_argument.tex'] + '',
  325. ]
  326. expect(errors.length).to.equal(expectedErrors.length)
  327. for (let i = 0; i < errors.length; i++) {
  328. expect(expectedErrors[i]).to.equal(
  329. [errors[i].line, errors[i].message, errors[i].file] + ''
  330. )
  331. }
  332. })
  333. it('should parse filenames', function () {
  334. const latexParser = new LatexLogParser(this.filenamesLog)
  335. const { errors, warnings, typesetting } = latexParser.parse()
  336. const expectedErrors = [
  337. [
  338. 1,
  339. 'Undefined control sequence.',
  340. '/compile/a folder with spaces/a subfolder with spaces/a subsubfolder with spaces/another file with spaces.tex',
  341. ] + '',
  342. ]
  343. const expectedWarnings = [
  344. [
  345. 9,
  346. "Citation `Peeters:2001np' on page 13 undefined on input line 9.",
  347. '/compile/main',
  348. ] + '',
  349. ]
  350. const expectedTypesetting = [
  351. [
  352. 123,
  353. 'Overfull \\hbox (4.56pt too wide) in paragraph at lines 123--456',
  354. '/compile/otherfile',
  355. ] + '',
  356. ]
  357. expect(expectedErrors.length).to.equal(errors.length)
  358. expect(warnings.length).to.equal(warnings.length)
  359. expect(typesetting.length).to.equal(typesetting.length)
  360. for (let i = 0; i < errors.length; i++) {
  361. expect(expectedErrors[i]).to.equal(
  362. [errors[i].line, errors[i].message, errors[i].file] + ''
  363. )
  364. }
  365. for (let j = 0; j < warnings.length; j++) {
  366. expect(expectedWarnings[j]).to.equal(
  367. [warnings[j].line, warnings[j].message, warnings[j].file] + ''
  368. )
  369. }
  370. for (let k = 0; k < typesetting.length; k++) {
  371. expect(expectedTypesetting[k]).to.equal(
  372. [typesetting[k].line, typesetting[k].message, typesetting[k].file] + ''
  373. )
  374. }
  375. })
  376. it('should perform file line error parsing', function () {
  377. let latexParser = new LatexLogParser(this.fileLineErrorLog)
  378. let errors = latexParser.parse().errors
  379. let expectedErrors = [
  380. [
  381. 1,
  382. 'Undefined control sequence.',
  383. '/compile/a folder with spaces/a subfolder with spaces/a subsubfolder with spaces/another file with spaces.tex',
  384. ] + '',
  385. ]
  386. expect(errors.length).to.equal(expectedErrors.length)
  387. for (let i = 0; i < errors.length; i++) {
  388. expect(expectedErrors[i]).to.equal(
  389. [errors[i].line, errors[i].message, errors[i].file] + ''
  390. )
  391. }
  392. // again with a more complex example
  393. latexParser = new LatexLogParser(this.secondaryFileLineErrorLog)
  394. errors = latexParser.parse().errors
  395. expectedErrors = [
  396. [1, 'Misplaced alignment tab character &.', './acks/name.tex'],
  397. [14, 'Misplaced alignment tab character &.', './main.tex'],
  398. ]
  399. expect(errors.length).to.equal(expectedErrors.length)
  400. for (let i = 0; i < errors.length; i++) {
  401. expect(expectedErrors[i]).to.deep.equal([
  402. errors[i].line,
  403. errors[i].message,
  404. errors[i].file,
  405. ])
  406. }
  407. })
  408. it('should ignore duplicates', function () {
  409. let latexParser = new LatexLogParser(this.errorLog)
  410. let errors = latexParser.parse().errors
  411. // duplicates included
  412. expect(errors.length).to.equal(10)
  413. latexParser = new LatexLogParser(this.errorLog, { ignoreDuplicates: true })
  414. errors = latexParser.parse().errors
  415. // duplicates excluded
  416. expect(errors.length).to.equal(7)
  417. })
  418. it('should get file paths', function () {
  419. let latexParser = new LatexLogParser(this.errorLog)
  420. let errors = latexParser.parse().errors
  421. for (let i = 0; i < errors.length; i++) {
  422. expect(errors[i].file).to.equal(
  423. 'compiles/dff0c37d892f346e58fc14975a16bf69/sections/appendices.tex'
  424. )
  425. }
  426. latexParser = new LatexLogParser(this.badBoxesLog)
  427. errors = latexParser.parse().all
  428. for (let j = 0; j < errors.length; j++) {
  429. expect(errors[j].file).to.equal(
  430. 'compiles/b6cf470376785e64ad84c57e3296c912/logs/bad-boxes.tex'
  431. )
  432. }
  433. })
  434. it('should parse a typical biber .blg file', function () {
  435. const bibParser = new BibLogParser(this.biberBlg, {})
  436. const result = bibParser.parse()
  437. expect(typeof result).to.equal('object')
  438. expect(result.all.length).to.equal(14)
  439. expect(result.errors.length).to.equal(1)
  440. expect(result.warnings.length).to.equal(2)
  441. const error = result.errors[0]
  442. expect(error.level).to.equal('error')
  443. expect(error.line).to.equal('8')
  444. expect(error.file).to.equal('bibliography.bib')
  445. expect(error.message).to.equal(
  446. 'syntax error: at end of input, expected end of entry ("}" or ")") (skipping to next "@")'
  447. )
  448. })
  449. it('should throw an error when non-biblog passed to BibLogParser', function (done) {
  450. const bibParser = new BibLogParser(this.captionWarningsLog, {})
  451. try {
  452. bibParser.parse()
  453. } catch (e) {
  454. expect(e).to.exist
  455. done()
  456. }
  457. })
  458. it('should throw an error when empty string passed to BibLogParser', function (done) {
  459. const bibParser = new BibLogParser('', {})
  460. try {
  461. bibParser.parse()
  462. } catch (e) {
  463. expect(e).to.exist
  464. done()
  465. }
  466. })
  467. it('should throw an error when non-string passed to BibLogParser', function (done) {
  468. try {
  469. const bibParser = new BibLogParser({ a: 1 }, {})
  470. bibParser.parse()
  471. } catch (e) {
  472. expect(e).to.exist
  473. done()
  474. }
  475. })
  476. it('should parse a typical bibtex .blg file', function () {
  477. const bibParser = new BibLogParser(this.bibtexBlg, {})
  478. const result = bibParser.parse()
  479. expect(typeof result).to.equal('object')
  480. expect(result.all.length).to.equal(13)
  481. expect(result.warnings.length).to.equal(6)
  482. const firstWarning = result.warnings[0]
  483. expect(firstWarning.file).to.equal('references.bib')
  484. expect(firstWarning.line).to.equal('152')
  485. expect(firstWarning.level).to.equal('warning')
  486. expect(firstWarning.message).to.equal(
  487. 'string name "something" is undefined'
  488. )
  489. const thirdWarning = result.warnings[2]
  490. expect(thirdWarning.message).to.equal(
  491. "can't use both author and editor fields in Binney87"
  492. )
  493. expect(result.errors.length).to.equal(7)
  494. const firstError = result.errors[0]
  495. expect(firstError.file).to.equal('references.bib')
  496. expect(firstError.line).to.equal('196')
  497. expect(firstError.level).to.equal('error')
  498. expect(
  499. firstError.message.indexOf("I was expecting a `,' or a `}'")
  500. ).to.equal(0)
  501. expect(
  502. firstError.message.indexOf('(Error may have been on previous line)') > 0
  503. ).to.equal(true)
  504. const crossReferenceError = result.errors[5]
  505. expect(crossReferenceError.level).to.equal('error')
  506. expect(
  507. crossReferenceError.message.indexOf('A bad cross reference')
  508. ).to.equal(0)
  509. const styleError = result.errors[6]
  510. expect(styleError.level).to.equal('error')
  511. expect(
  512. styleError.message.indexOf("I couldn't open style file aa.bst")
  513. ).to.equal(0)
  514. })
  515. })