1{
2 lib,
3 stdenv,
4 buildEnv,
5 runCommand,
6 fetchurl,
7 file,
8 texlive,
9 writeShellScript,
10 writeText,
11 texliveInfraOnly,
12 texliveConTeXt,
13 texliveSmall,
14 texliveMedium,
15 texliveFull,
16}:
17
18rec {
19
20 mkTeXTest = lib.makeOverridable (
21 {
22 name,
23 format,
24 text,
25 texLive ? texliveSmall,
26 options ? "-interaction=errorstopmode",
27 preTest ? "",
28 postTest ? "",
29 ...
30 }@attrs:
31 runCommand "texlive-test-tex-${name}"
32 (
33 {
34 nativeBuildInputs = [ texLive ] ++ attrs.nativeBuildInputs or [ ];
35 text = builtins.toFile "${name}.tex" text;
36 }
37 // builtins.removeAttrs attrs [
38 "nativeBuildInputs"
39 "text"
40 "texLive"
41 ]
42 )
43 ''
44 export HOME="$(mktemp -d)"
45 mkdir "$out"
46 cd "$out"
47 cp "$text" "$name.tex"
48 ${preTest}
49 $format $options "$name.tex"
50 ${postTest}
51 ''
52 );
53
54 tlpdbNix =
55 runCommand "texlive-test-tlpdb-nix"
56 {
57 nixpkgsTlpdbNix = ../../tools/typesetting/tex/texlive/tlpdb.nix;
58 tlpdbNix = texlive.tlpdb.nix;
59 }
60 ''
61 mkdir -p "$out"
62 diff -u "''${nixpkgsTlpdbNix}" "''${tlpdbNix}" | tee "$out/tlpdb.nix.patch"
63 '';
64
65 # test two completely different font discovery mechanisms, both of which were once broken:
66 # - lualatex uses its own luaotfload script (#220228)
67 # - xelatex uses fontconfig (#228196)
68 opentype-fonts = lib.recurseIntoAttrs rec {
69 lualatex = mkTeXTest {
70 name = "opentype-fonts-lualatex";
71 format = "lualatex";
72 texLive = texliveSmall.withPackages (ps: [ ps.libertinus-fonts ]);
73 text = ''
74 \documentclass{article}
75 \usepackage{fontspec}
76 \setmainfont{Libertinus Serif}
77 \begin{document}
78 \LaTeX{} is great
79 \end{document}
80 '';
81 };
82 xelatex = lualatex.override {
83 name = "opentype-fonts-xelatex";
84 format = "xelatex";
85 };
86 };
87
88 chktex =
89 runCommand "texlive-test-chktex"
90 {
91 nativeBuildInputs = [
92 (texlive.withPackages (ps: [ ps.chktex ]))
93 ];
94 input = builtins.toFile "chktex-sample.tex" ''
95 \documentclass{article}
96 \begin{document}
97 \LaTeX is great
98 \end{document}
99 '';
100 }
101 ''
102 # chktex is supposed to return 2 when it (successfully) finds warnings, but no errors,
103 # see http://git.savannah.nongnu.org/cgit/chktex.git/commit/?id=ec0fb9b58f02a62ff0bfec98b997208e9d7a5998
104 (set +e; chktex -v -nall -w1 "$input" 2>&1; [ $? = 2 ] || exit 1; set -e) | tee "$out"
105 # also check that the output does indeed contain "One warning printed"
106 grep "One warning printed" "$out"
107 '';
108
109 context = mkTeXTest {
110 name = "texlive-test-context";
111 format = "context";
112 texLive = texliveConTeXt;
113 # check that the PDF has been created: we have hit cases of context
114 # failing with exit status 0 due to a misconfigured texlive
115 postTest = ''
116 if [[ ! -f "$name".pdf ]] ; then
117 echo "ConTeXt test failed: file '$name.pdf' not found"
118 exit 1
119 fi
120 '';
121 text = ''
122 \starttext
123 \startsection[title={ConTeXt test document}]
124 This is an {\em incredibly} simple ConTeXt document.
125 \stopsection
126 \stoptext
127 '';
128 };
129
130 dvipng = lib.recurseIntoAttrs {
131 # https://github.com/NixOS/nixpkgs/issues/75605
132 basic =
133 runCommand "texlive-test-dvipng-basic"
134 {
135 nativeBuildInputs = [
136 file
137 texliveMedium
138 ];
139 input = fetchurl {
140 name = "test_dvipng.tex";
141 url = "http://git.savannah.nongnu.org/cgit/dvipng.git/plain/test_dvipng.tex?id=b872753590a18605260078f56cbd6f28d39dc035";
142 sha256 = "1pjpf1jvwj2pv5crzdgcrzvbmn7kfmgxa39pcvskl4pa0c9hl88n";
143 };
144 }
145 ''
146 cp "$input" ./document.tex
147
148 latex document.tex
149 dvipng -T tight -strict -picky document.dvi
150 for f in document*.png; do
151 file "$f" | tee output
152 grep PNG output
153 done
154
155 mkdir "$out"
156 mv document*.png "$out"/
157 '';
158
159 # test dvipng's limited capability to render postscript specials via GS
160 ghostscript =
161 runCommand "texlive-test-ghostscript"
162 {
163 nativeBuildInputs = [
164 file
165 (texliveSmall.withPackages (ps: [ ps.dvipng ]))
166 ];
167 input = builtins.toFile "postscript-sample.tex" ''
168 \documentclass{minimal}
169 \begin{document}
170 Ni
171 \special{ps:
172 newpath
173 0 0 moveto
174 7 7 rlineto
175 0 7 moveto
176 7 -7 rlineto
177 stroke
178 showpage
179 }
180 \end{document}
181 '';
182 gs_trap = writeShellScript "gs_trap.sh" ''
183 exit 1
184 '';
185 }
186 ''
187 cp "$gs_trap" ./gs
188 export PATH=$PWD:$PATH
189 # check that the trap works
190 gs && exit 1
191
192 cp "$input" ./document.tex
193
194 latex document.tex
195 dvipng -T 1in,1in -strict -picky document.dvi
196 for f in document*.png; do
197 file "$f" | tee output
198 grep PNG output
199 done
200
201 mkdir "$out"
202 mv document*.png "$out"/
203 '';
204 };
205
206 # https://github.com/NixOS/nixpkgs/issues/75070
207 dvisvgm =
208 runCommand "texlive-test-dvisvgm"
209 {
210 nativeBuildInputs = [
211 file
212 texliveMedium
213 ];
214 input = builtins.toFile "dvisvgm-sample.tex" ''
215 \documentclass{article}
216 \begin{document}
217 mwe
218 \end{document}
219 '';
220 }
221 ''
222 cp "$input" ./document.tex
223
224 latex document.tex
225 dvisvgm document.dvi -n -o document_dvi.svg
226 cat document_dvi.svg
227 file document_dvi.svg | grep SVG
228
229 pdflatex document.tex
230 dvisvgm -P document.pdf -n -o document_pdf.svg
231 cat document_pdf.svg
232 file document_pdf.svg | grep SVG
233
234 mkdir "$out"
235 mv document*.svg "$out"/
236 '';
237
238 texdoc =
239 runCommand "texlive-test-texdoc"
240 {
241 nativeBuildInputs = [
242 (texlive.withPackages (ps: [
243 ps.luatex
244 ps.texdoc
245 ps.texdoc.texdoc
246 ]))
247 ];
248 }
249 ''
250 texdoc --version
251
252 texdoc --debug --list texdoc | tee "$out"
253 grep texdoc.pdf "$out"
254 '';
255
256 # check that the default language is US English
257 defaultLanguage = lib.recurseIntoAttrs rec {
258 # language.def
259 etex = mkTeXTest {
260 name = "default-language-etex";
261 format = "etex";
262 text = ''
263 \catcode`\@=11
264 \ifnum\language=\lang@USenglish \message{[tests.texlive] Default language is US English.}
265 \else\errmessage{[tests.texlive] Error: default language is NOT US English.}\fi
266 \ifnum\language=0\message{[tests.texlive] Default language has id 0.}
267 \else\errmessage{[tests.texlive] Error: default language does NOT have id 0.}\fi
268 \bye
269 '';
270 };
271 # language.dat
272 latex = mkTeXTest {
273 name = "default-language-latex";
274 format = "latex";
275 text = ''
276 \makeatletter
277 \ifnum\language=\l@USenglish \GenericWarning{}{[tests.texlive] Default language is US English}
278 \else\GenericError{}{[tests.texlive] Error: default language is NOT US English}{}{}\fi
279 \ifnum\language=0\GenericWarning{}{[tests.texlive] Default language has id 0}
280 \else\GenericError{}{[tests.texlive] Error: default language does NOT have id 0}{}{}\fi
281 \stop
282 '';
283 };
284 # language.dat.lua
285 luatex = etex.override {
286 name = "default-language-luatex";
287 format = "luatex";
288 };
289 };
290
291 # check that all languages are available, including synonyms
292 allLanguages =
293 let
294 hyphenBase = texlive.pkgs.hyphen-base;
295 texLive = texliveFull;
296 in
297 lib.recurseIntoAttrs {
298 # language.def
299 etex = mkTeXTest {
300 name = "all-languages-etex";
301 format = "etex";
302 inherit hyphenBase texLive;
303 text = ''
304 \catcode`\@=11
305 \input kvsetkeys.sty
306 \def\CheckLang#1{
307 \ifcsname lang@#1\endcsname\message{[tests.texlive] Found language #1}
308 \else\errmessage{[tests.texlive] Error: missing language #1}\fi
309 }
310 \comma@parse{@texLanguages@}\CheckLang
311 \bye
312 '';
313 preTest = ''
314 texLanguages="$(sed -n -E 's/^\\addlanguage\s*\{([^}]+)\}.*$/\1/p' < "$hyphenBase"/tex/generic/config/language.def)"
315 texLanguages="''${texLanguages//$'\n'/,}"
316 substituteInPlace "$name.tex" --subst-var texLanguages
317 '';
318 };
319 # language.dat
320 latex = mkTeXTest {
321 name = "all-languages-latex";
322 format = "latex";
323 inherit hyphenBase texLive;
324 text = ''
325 \makeatletter
326 \@for\Lang:=italian,@texLanguages@\do{
327 \ifcsname l@\Lang\endcsname
328 \GenericWarning{}{[tests.texlive] Found language \Lang}
329 \else
330 \GenericError{}{[tests.texlive] Error: missing language \Lang}{}{}
331 \fi
332 }
333 \stop
334 '';
335 preTest = ''
336 texLanguages="$(sed -n -E 's/^([^%= \t]+).*$/\1/p' < "$hyphenBase"/tex/generic/config/language.dat)"
337 texLanguages="''${texLanguages//$'\n'/,}"
338 substituteInPlace "$name.tex" --subst-var texLanguages
339 '';
340 };
341 # language.dat.lua
342 luatex = mkTeXTest {
343 name = "all-languages-luatex";
344 format = "luatex";
345 inherit hyphenBase texLive;
346 text = ''
347 \directlua{
348 require('luatex-hyphen.lua')
349 langs = '@texLanguages@,'
350 texio.write('\string\n')
351 for l in langs:gmatch('([^,]+),') do
352 if luatexhyphen.lookupname(l) \string~= nil then
353 texio.write('[tests.texlive] Found language '..l..'.\string\n')
354 else
355 error('[tests.texlive] Error: missing language '..l..'.', 2)
356 end
357 end
358 }
359 \bye
360 '';
361 preTest = ''
362 texLanguages="$(sed -n -E 's/^.*\[("|'\''')(.*)("|'\''')].*$/\2/p' < "$hyphenBase"/tex/generic/config/language.dat.lua)"
363 texLanguages="''${texLanguages//$'\n'/,}"
364 substituteInPlace "$name.tex" --subst-var texLanguages
365 '';
366 };
367 };
368
369 # test that language files are generated as expected
370 hyphen-base =
371 runCommand "texlive-test-hyphen-base"
372 {
373 hyphenBase = texlive.pkgs.hyphen-base;
374 schemeFull = texliveFull;
375 schemeInfraOnly = texliveInfraOnly;
376 }
377 ''
378 mkdir -p "$out"/{scheme-infraonly,scheme-full}
379
380 # create language files with no hyphenation patterns
381 cat "$hyphenBase"/tex/generic/config/language.us >language.dat
382 cat "$hyphenBase"/tex/generic/config/language.us.def >language.def
383 cat "$hyphenBase"/tex/generic/config/language.us.lua >language.dat.lua
384
385 cat >>language.dat.lua <<EOF
386 }
387 EOF
388
389 cat >>language.def <<EOF
390 %%% No changes may be made beyond this point.
391
392 \uselanguage {USenglish} %%% This MUST be the last line of the file.
393 EOF
394
395 for fname in language.{dat,def,dat.lua} ; do
396 diff --ignore-matching-lines='^\(%\|--\) Generated by ' -u \
397 {"$hyphenBase","$schemeFull"/share/texmf-var}/tex/generic/config/"$fname" \
398 | tee "$out/scheme-full/$fname.patch"
399 diff --ignore-matching-lines='^\(%\|--\) Generated by ' -u \
400 {,"$schemeInfraOnly"/share/texmf-var/tex/generic/config/}"$fname" \
401 | tee "$out/scheme-infraonly/$fname.patch"
402 done
403 '';
404
405 # verify that the restricted mode gets enabled when
406 # needed (detected by checking if it disallows --gscmd)
407 repstopdf =
408 runCommand "texlive-test-repstopdf"
409 {
410 nativeBuildInputs = [ (texlive.withPackages (ps: [ ps.epstopdf ])) ];
411 }
412 ''
413 ! (epstopdf --gscmd echo /dev/null 2>&1 || true) | grep forbidden >/dev/null
414 (repstopdf --gscmd echo /dev/null 2>&1 || true) | grep forbidden >/dev/null
415 mkdir "$out"
416 '';
417
418 # verify that the restricted mode gets enabled when
419 # needed (detected by checking if it disallows --gscmd)
420 rpdfcrop =
421 runCommand "texlive-test-rpdfcrop"
422 {
423 nativeBuildInputs = [ (texlive.withPackages (ps: [ ps.pdfcrop ])) ];
424 }
425 ''
426 ! (pdfcrop --gscmd echo $(command -v pdfcrop) 2>&1 || true) | grep 'restricted mode' >/dev/null
427 (rpdfcrop --gscmd echo $(command -v pdfcrop) 2>&1 || true) | grep 'restricted mode' >/dev/null
428 mkdir "$out"
429 '';
430
431 # check that all binaries run successfully, in the following sense:
432 # (1) run --version, -v, --help, -h successfully; or
433 # (2) run --help, -h, or no argument with error code but show help text; or
434 # (3) run successfully on a test.tex or similar file
435 # we ignore the binaries that cannot be tested as above, and are either
436 # compiled binaries or trivial shell wrappers
437 binaries =
438 let
439 # TODO known broken binaries
440 broken = [
441 # do not know how to test without a valid build.lua
442 "ppmcheckpdf"
443
444 # 'Error initialising QuantumRenderer: no suitable pipeline found'
445 "tlcockpit"
446 ]
447 ++ lib.optional stdenv.hostPlatform.isDarwin "epspdftk"; # wish shebang is a script, not a binary!
448
449 # (1) binaries requiring -v
450 shortVersion = [
451 "devnag"
452 "diadia"
453 "pmxchords"
454 "ptex2pdf"
455 "simpdftex"
456 "ttf2afm"
457 ];
458 # (1) binaries requiring --help or -h
459 help = [
460 "arlatex"
461 "bundledoc"
462 "cachepic"
463 "checklistings"
464 "dvipos"
465 "extractres"
466 "fig4latex"
467 "fragmaster"
468 "kpsewhere"
469 "latex-git-log"
470 "ltxfileinfo"
471 "mendex"
472 "pdflatexpicscale"
473 "perltex"
474 "pn2pdf"
475 "psbook"
476 "psnup"
477 "psresize"
478 "purifyeps"
479 "simpdftex"
480 "tex2xindy"
481 "texluac"
482 "texluajitc"
483 "upmendex"
484 "urlbst"
485 "yplan"
486 ];
487 shortHelp = [
488 "adhocfilelist"
489 "authorindex"
490 "bbl2bib"
491 "bibdoiadd"
492 "bibmradd"
493 "biburl2doi"
494 "bibzbladd"
495 "bookshelf-listallfonts"
496 "bookshelf-mkfontsel"
497 "ctanupload"
498 "disdvi"
499 "dvibook"
500 "dviconcat"
501 "getmapdl"
502 "latex2man"
503 "listings-ext.sh"
504 "pygmentex"
505 ];
506 # (2) binaries that return non-zero exit code even if correctly asked for help
507 ignoreExitCode = [
508 "authorindex"
509 "bookshelf-listallfonts"
510 "bookshelf-mkfontsel"
511 "dvibook"
512 "dviconcat"
513 "dvipos"
514 "extractres"
515 "fig4latex"
516 "fragmaster"
517 "latex2man"
518 "latex-git-log"
519 "listings-ext.sh"
520 "psbook"
521 "psnup"
522 "psresize"
523 "purifyeps"
524 "tex2xindy"
525 "texluac"
526 "texluajitc"
527 ];
528 # (2) binaries that print help on no argument, returning non-zero exit code
529 noArg = [
530 "a2ping"
531 "bg5+latex"
532 "bg5+pdflatex"
533 "bg5latex"
534 "bg5pdflatex"
535 "cef5latex"
536 "cef5pdflatex"
537 "ceflatex"
538 "cefpdflatex"
539 "cefslatex"
540 "cefspdflatex"
541 "chkdvifont"
542 "dvi2fax"
543 "dvired"
544 "dviselect"
545 "dvitodvi"
546 "epsffit"
547 "findhyph"
548 "gbklatex"
549 "gbkpdflatex"
550 "komkindex"
551 "kpsepath"
552 "listbib"
553 "listings-ext"
554 "mag"
555 "mathspic"
556 "mf2pt1"
557 "mk4ht"
558 "mkt1font"
559 "mkgrkindex"
560 "musixflx"
561 "pdf2ps"
562 "pdfclose"
563 "pdftosrc"
564 "pdfxup"
565 "pedigree"
566 "pfb2pfa"
567 "pk2bm"
568 "prepmx"
569 "ps2pk"
570 "psselect"
571 "pstops"
572 "rubibtex"
573 "rubikrotation"
574 "sjislatex"
575 "sjispdflatex"
576 "srcredact"
577 "t4ht"
578 "teckit_compile"
579 "tex4ht"
580 "texdiff"
581 "texdirflatten"
582 "texplate"
583 "tie"
584 "ttf2kotexfont"
585 "ttfdump"
586 "vlna"
587 "vpl2ovp"
588 "vpl2vpl"
589 "yplan"
590 ];
591 # (3) binaries requiring a .tex file
592 contextTest = [ "htcontext" ];
593 latexTest = [
594 "de-macro"
595 "e2pall"
596 "htlatex"
597 "htxelatex"
598 "makeindex"
599 "pslatex"
600 "rumakeindex"
601 "tpic2pdftex"
602 "wordcount"
603 "xhlatex"
604 ];
605 texTest = [
606 "fontinst"
607 "htmex"
608 "httex"
609 "httexi"
610 "htxetex"
611 ];
612 # tricky binaries or scripts that are obviously working but are hard to test
613 # (e.g. because they expect user input no matter the arguments)
614 # (printafm comes from ghostscript, not texlive)
615 ignored = [
616 # compiled binaries
617 "dt2dv"
618 "dv2dt"
619 "dvi2tty"
620 "dvidvi"
621 "dvispc"
622 "otp2ocp"
623 "outocp"
624 "pmxab"
625
626 # GUI scripts that accept no argument or crash without a graphics server; please test manually
627 "epspdftk"
628 "texdoctk"
629 "tlshell"
630 "xasy"
631
632 # requires Cinderella, not open source and not distributed via Nixpkgs
633 "ketcindy"
634 ];
635 # binaries that need a combined scheme and cannot work standalone
636 needScheme = [
637 # pfarrei: require working kpse to find lua module
638 "a5toa4"
639
640 # show-pdf-tags: require working kpse to find lualatex and lua modules
641 "show-pdf-tags"
642
643 # bibexport: requires kpsewhich
644 "bibexport"
645
646 # crossrefware: require bibtexperllibs under TEXMFROOT
647 "bbl2bib"
648 "bibdoiadd"
649 "bibmradd"
650 "biburl2doi"
651 "bibzbladd"
652 "checkcites"
653 "ltx2crossrefxml"
654
655 # epstopdf: requires kpsewhich
656 "epstopdf"
657 "repstopdf"
658
659 # requires kpsewhich
660 "memoize-extract.pl"
661 "memoize-extract.py"
662
663 # require other texlive binaries in PATH
664 "allcm"
665 "allec"
666 "chkweb"
667 "explcheck"
668 "extractbb"
669 "fontinst"
670 "ht*"
671 "installfont-tl"
672 "kanji-config-updmap-sys"
673 "kanji-config-updmap-user"
674 "kpse*"
675 "latexfileversion"
676 "mkocp"
677 "mkofm"
678 "mtxrunjit"
679 "pdftex-quiet"
680 "pslatex"
681 "rumakeindex"
682 "texconfig"
683 "texconfig-sys"
684 "texexec"
685 "texlinks"
686 "texmfstart"
687 "typeoutfileinfo"
688 "wordcount"
689 "xdvi"
690 "xhlatex"
691
692 # misc luatex binaries searching for luatex in PATH
693 "citeproc-lua"
694 "context"
695 "contextjit"
696 "ctanbib"
697 "digestif"
698 "epspdf"
699 "l3build"
700 "luafindfont"
701 "luaotfload-tool"
702 "luatools"
703 "make4ht"
704 "pmxchords"
705 "tex4ebook"
706 "texblend"
707 "texdoc"
708 "texfindpkg"
709 "texlogsieve"
710 "xindex"
711
712 # requires full TEXMFROOT (e.g. for config)
713 "mktexfmt"
714 "mktexmf"
715 "mktexpk"
716 "mktextfm"
717 "psnup"
718 "psresize"
719 "pstops"
720 "tlmgr"
721 "updmap"
722 "webquiz"
723
724 # texlive-scripts: requires texlive.infra's TeXLive::TLUtils under TEXMFROOT
725 "fmtutil"
726 "fmtutil-sys"
727 "fmtutil-user"
728
729 # texlive-scripts: not used in nixpkgs, need updmap in PATH
730 "updmap-sys"
731 "updmap-user"
732 ];
733
734 # simple test files
735 contextTestTex = writeText "context-test.tex" ''
736 \starttext
737 A simple test file.
738 \stoptext
739 '';
740 latexTestTex = writeText "latex-test.tex" ''
741 \documentclass{article}
742 \begin{document}
743 A simple test file.
744 \end{document}
745 '';
746 texTestTex = writeText "tex-test.tex" ''
747 Hello.
748 \bye
749 '';
750
751 # link all binaries in single derivation
752 binPackages = lib.catAttrs "out" (lib.attrValues texlive.pkgs);
753 binaries = buildEnv {
754 name = "texlive-binaries";
755 paths = binPackages;
756 };
757 in
758 runCommand "texlive-test-binaries"
759 {
760 inherit
761 binaries
762 contextTestTex
763 latexTestTex
764 texTestTex
765 ;
766 texliveScheme = texliveFull;
767 }
768 ''
769 loadables="$(command -v bash)"
770 loadables="''${loadables%/bin/bash}/lib/bash"
771 enable -f "$loadables/realpath" realpath
772 mkdir -p "$out"
773 export HOME="$(mktemp -d)"
774 declare -i binCount=0 ignoredCount=0 brokenCount=0 failedCount=0
775 cp "$contextTestTex" context-test.tex
776 cp "$latexTestTex" latex-test.tex
777 cp "$texTestTex" tex-test.tex
778
779 testBin () {
780 path="$(realpath "$bin")"
781 path="''${path##*/}"
782 if [[ -z "$ignoreExitCode" ]] ; then
783 PATH="$path" "$bin" $args >"$out/$base.log" 2>&1
784 ret=$?
785 if [[ $ret == 0 ]] && grep -i 'command not found' "$out/$base.log" >/dev/null ; then
786 echo "command not found when running '$base''${args:+ $args}'"
787 return 1
788 fi
789 return $ret
790 else
791 PATH="$path" "$bin" $args >"$out/$base.log" 2>&1
792 ret=$?
793 if [[ $ret == 0 ]] && grep -i 'command not found' "$out/$base.log" >/dev/null ; then
794 echo "command not found when running '$base''${args:+ $args}'"
795 return 1
796 fi
797 if ! grep -Ei '(Example:|Options:|Syntax:|Usage:|improper command|SYNOPSIS)' "$out/$base.log" >/dev/null ; then
798 echo "did not find usage info when running '$base''${args:+ $args}'"
799 return $ret
800 fi
801 fi
802 }
803
804 for bin in "$binaries"/bin/* ; do
805 base="''${bin##*/}"
806 args=
807 ignoreExitCode=
808 binCount=$((binCount + 1))
809
810 # ignore non-executable files (such as context.lua)
811 if [[ ! -x "$bin" ]] ; then
812 ignoredCount=$((ignoredCount + 1))
813 continue
814 fi
815
816 case "$base" in
817 ${lib.concatStringsSep "|" ignored})
818 ignoredCount=$((ignoredCount + 1))
819 continue ;;
820 ${lib.concatStringsSep "|" broken})
821 brokenCount=$((brokenCount + 1))
822 continue ;;
823 ${lib.concatStringsSep "|" help})
824 args=--help ;;
825 ${lib.concatStringsSep "|" shortHelp})
826 args=-h ;;
827 ${lib.concatStringsSep "|" noArg})
828 ;;
829 ${lib.concatStringsSep "|" contextTest})
830 args=context-test.tex ;;
831 ${lib.concatStringsSep "|" latexTest})
832 args=latex-test.tex ;;
833 ${lib.concatStringsSep "|" texTest})
834 args=tex-test.tex ;;
835 ${lib.concatStringsSep "|" shortVersion})
836 args=-v ;;
837 ebong)
838 touch empty
839 args=empty ;;
840 ht)
841 args='latex latex-test.tex' ;;
842 pdf2dsc)
843 args='--help --help --help' ;;
844 typeoutfileinfo)
845 args=/dev/null ;;
846 *)
847 args=--version ;;
848 esac
849
850 case "$base" in
851 ${lib.concatStringsSep "|" (ignoreExitCode ++ noArg)})
852 ignoreExitCode=1 ;;
853 esac
854
855 case "$base" in
856 ${lib.concatStringsSep "|" needScheme})
857 bin="$texliveScheme/bin/$base"
858 if [[ ! -f "$bin" ]] ; then
859 ignoredCount=$((ignoredCount + 1))
860 continue
861 fi ;;
862 esac
863
864 if testBin ; then : ; else # preserve exit code
865 echo "failed '$base''${args:+ $args}' (exit code: $?)"
866 sed 's/^/ > /' < "$out/$base.log"
867 failedCount=$((failedCount + 1))
868 fi
869 done
870
871 echo "tested $binCount binaries: $ignoredCount ignored, $brokenCount broken, $failedCount failed"
872 [[ $failedCount = 0 ]]
873 '';
874
875 # check that all scripts have a Nix shebang
876 shebangs =
877 let
878 binPackages = lib.catAttrs "out" (lib.attrValues texlive.pkgs);
879 in
880 runCommand "texlive-test-shebangs" { } (
881 ''
882 echo "checking that all texlive scripts shebangs are in '$NIX_STORE'"
883 declare -i scriptCount=0 invalidCount=0
884 ''
885 + (lib.concatMapStrings (pkg: ''
886 for bin in '${pkg.outPath}'/bin/* ; do
887 grep -I -q . "$bin" || continue # ignore binary files
888 [[ -x "$bin" ]] || continue # ignore non-executable files (such as context.lua)
889 scriptCount=$((scriptCount + 1))
890 read -r cmdline < "$bin"
891 read -r interp <<< "$cmdline"
892 if [[ "$interp" != "#!$NIX_STORE"/* && "$interp" != "#! $NIX_STORE"/* ]] ; then
893 echo "error: non-nix shebang '$interp' in script '$bin'"
894 invalidCount=$((invalidCount + 1))
895 fi
896 done
897 '') binPackages)
898 + ''
899 echo "checked $scriptCount scripts, found $invalidCount non-nix shebangs"
900 [[ $invalidCount -gt 0 ]] && exit 1
901 mkdir -p "$out"
902 ''
903 );
904
905 # verify that the precomputed licensing information in default.nix
906 # does indeed match the metadata of the individual packages.
907 #
908 # This is part of the test suite (and not the normal evaluation) to save
909 # time for "normal" evaluations. To be more in line with the other tests, this
910 # also builds a derivation, even though it is essentially an eval-time assertion.
911 licenses =
912 let
913 concatLicenses = builtins.foldl' (acc: el: if builtins.elem el acc then acc else acc ++ [ el ]);
914 # converts a license to its attribute name in lib.licenses
915 licenseToAttrName =
916 license: builtins.head (builtins.attrNames (lib.filterAttrs (n: v: license == v) lib.licenses));
917 lt = (a: b: a < b);
918
919 savedLicenses = scheme: scheme.meta.license;
920 savedLicensesAttrNames = scheme: map licenseToAttrName (savedLicenses scheme);
921
922 correctLicenses =
923 scheme:
924 builtins.foldl' (
925 acc: pkg: concatLicenses acc (lib.toList (pkg.meta.license or [ ]))
926 ) [ ] scheme.passthru.requiredTeXPackages;
927 correctLicensesAttrNames = scheme: lib.sort lt (map licenseToAttrName (correctLicenses scheme));
928
929 hasLicenseMismatch =
930 scheme:
931 (lib.isDerivation scheme) && (savedLicensesAttrNames scheme) != (correctLicensesAttrNames scheme);
932 incorrectSchemes = lib.filterAttrs (n: hasLicenseMismatch) (texlive.combined // texlive.schemes);
933 prettyPrint = name: scheme: ''
934 license info for ${name} is incorrect! Note that order is enforced.
935 saved: [ ${lib.concatStringsSep " " (savedLicensesAttrNames scheme)} ]
936 correct: [ ${lib.concatStringsSep " " (correctLicensesAttrNames scheme)} ]
937 '';
938 errorText = lib.concatStringsSep "\n\n" (lib.mapAttrsToList prettyPrint incorrectSchemes);
939 in
940 runCommand "texlive-test-license"
941 {
942 inherit errorText;
943 }
944 (
945 if (incorrectSchemes == { }) then
946 "echo everything is fine! > $out"
947 else
948 ''
949 echo "$errorText"
950 false
951 ''
952 );
953
954 # verify that all fixed hashes are present
955 # this is effectively an eval-time assertion, converted into a derivation for
956 # ease of testing
957 fixedHashes =
958 let
959 fods = lib.concatMap (
960 p:
961 lib.optional (p ? tex && lib.isDerivation p.tex) p.tex
962 ++ lib.optional (p ? texdoc) p.texdoc
963 ++ lib.optional (p ? texsource) p.texsource
964 ++ lib.optional (p ? tlpkg) p.tlpkg
965 ) (lib.attrValues texlive.pkgs);
966 errorText = lib.concatMapStrings (
967 p:
968 lib.optionalString (
969 !p ? outputHash
970 ) "${p.pname}-${p.tlOutputName} does not have a fixed output hash\n"
971 ) fods;
972 in
973 runCommand "texlive-test-fixed-hashes"
974 {
975 inherit errorText;
976 passAsFile = [ "errorText" ];
977 }
978 ''
979 if [[ -s "$errorTextPath" ]] ; then
980 cat "$errorTextPath"
981 echo Failed: some TeX Live packages do not have fixed output hashes. Please read UPGRADING.md for how to generate a new fixed-hashes.nix.
982 exit 1
983 else
984 touch "$out"
985 fi
986 '';
987}