Browse Source

[test] Add compilation tests

pull/120/head
Louis 9 years ago
parent
commit
e60485405c
  1. 5
      patacrep/songbook/__main__.py
  2. 2
      setup.py
  3. 7
      songbook
  4. 25
      test/dynamic.py
  5. 39
      test/test_chordpro/test_parser.py
  6. 0
      test/test_compilation/__init__.py
  7. 180
      test/test_compilation/subdir.tex.control
  8. 89
      test/test_compilation/test_compilation.py

5
patacrep/songbook.py → patacrep/songbook/__main__.py

@ -39,7 +39,10 @@ class VerboseAction(argparse.Action):
def argument_parser(args): def argument_parser(args):
"""Parse arguments""" """Parse arguments"""
parser = argparse.ArgumentParser(description="A song book compiler") parser = argparse.ArgumentParser(
prog="songbook",
description="A song book compiler",
)
parser.add_argument( parser.add_argument(
'--version', help='Show version', action='version', '--version', help='Show version', action='version',

2
setup.py

@ -24,7 +24,7 @@ setup(
include_package_data=True, include_package_data=True,
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
"songbook = patacrep.songbook:main", "songbook = patacrep.songbook.__main__:main",
], ],
}, },
classifiers=[ classifiers=[

7
songbook

@ -1,9 +1,6 @@
#! /usr/bin/env python3 #! /bin/sh
# Do not edit this file. This file is just a helper file for development test. # Do not edit this file. This file is just a helper file for development test.
# It is not part of the distributed software. # It is not part of the distributed software.
"""Command line tool to compile songbooks using the songbook library.""" python -m patacrep.songbook $*
from patacrep.songbook import main
main()

25
test/dynamic.py

@ -0,0 +1,25 @@
"""Dynamic creation of test methods."""
class DynamicTest(type):
"""Metaclass that creates on-the-fly test methods.
Each class implementing this metaclass must define two class methods:
- `_iter_testmethods`, which iterate over `(methodname, args)`, where
`methodname` is the name of a test method to create, and `args` is a list
of arguments to pass to `_create_test`;
- `_create_test`, a method returning a test method.
"""
def __init__(cls, name, bases, nmspc):
super().__init__(name, bases, nmspc)
for methodname, args in cls._iter_testmethods():
setattr(cls, methodname, cls._create_test(*args))
def _iter_testmethods(cls):
"""Iterate over test methods."""
raise NotImplementedError()
def _create_test(cls, *args, **kwargs):
"""Create and return a test method."""
raise NotImplementedError()

39
test/test_chordpro/test_parser.py

@ -10,6 +10,7 @@ from patacrep.build import DEFAULT_CONFIG
from patacrep.songs.chordpro import ChordproSong from patacrep.songs.chordpro import ChordproSong
from .. import disable_logging from .. import disable_logging
from .. import dynamic # pylint: disable=unused-import
LANGUAGES = { LANGUAGES = {
'tex': 'latex', 'tex': 'latex',
@ -17,15 +18,22 @@ LANGUAGES = {
'html': 'html', 'html': 'html',
} }
class FileTestMeta(type): class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest):
"""Metaclass that creates on-the-fly test function according to files. """Test of chorpro parser, and several renderers.
For any given `foo.source`, it is parsed as a chordpro file, and should be
rendered as `foo.sgc` with the chordpro renderer, and `foo.tex` with the
latex renderer.
See the :class:`FileTest` documentation for more information. This class does nothing by itself, but its metaclass populates it with test
methods testing parser and renderers.
""" """
def __init__(cls, name, bases, nmspc): maxDiff = None
super().__init__(name, bases, nmspc)
@classmethod
def _iter_testmethods(cls):
"""Iterate over song files to test."""
for source in sorted(glob.glob(os.path.join( for source in sorted(glob.glob(os.path.join(
os.path.dirname(__file__), os.path.dirname(__file__),
'*.source', '*.source',
@ -35,14 +43,13 @@ class FileTestMeta(type):
destname = "{}.{}".format(base, dest) destname = "{}.{}".format(base, dest)
if not os.path.exists(destname): if not os.path.exists(destname):
continue continue
setattr( yield (
cls,
"test_{}_{}".format(os.path.basename(base), dest), "test_{}_{}".format(os.path.basename(base), dest),
cls._create_test(base, dest), [base, dest],
) )
@staticmethod @classmethod
def _create_test(base, dest): def _create_test(cls, base, dest):
"""Return a function testing that `base` compilation in `dest` format. """Return a function testing that `base` compilation in `dest` format.
""" """
@ -67,15 +74,3 @@ class FileTestMeta(type):
).format(base=os.path.basename(base), format=dest) ).format(base=os.path.basename(base), format=dest)
return test_parse_render return test_parse_render
class FileTest(unittest.TestCase, metaclass=FileTestMeta):
"""Test of chorpro parser, and several renderers.
For any given `foo.source`, it is parsed as a chordpro file, and should be
rendered as `foo.sgc` with the chordpro renderer, and `foo.tex` with the
latex renderer.
This class does nothing by itself, but its metaclass populates it with test
methods testing parser and renderers.
"""
maxDiff = None

0
test/test_compilation/__init__.py

180
test/test_compilation/subdir.tex.control

@ -0,0 +1,180 @@
%% Automatically generated document.
%% You may edit this file but all changes will be overwritten.
%% If you want to change this document, have a look at
%% the templating system.
%%
%% Generated using Songbook <http://www.patacrep.com>
\makeatletter
\def\input@path{ %
{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir/latex/} %
{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir2/latex/} %
{/home/louis/projets/patacrep/patacrep/test/test_compilation/latex/} %
{/home/louis/projets/patacrep/patacrep/patacrep/data/latex/} %
}
\makeatother
\documentclass[
]{article}
\usepackage[
chorded,
lilypond,
pictures,
guitar,
]{patacrep}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\PassOptionsToPackage{french}{babel}
\usepackage[english]{babel}
\lang{english}
\usepackage{graphicx}
\graphicspath{ %
{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir/img/} %
{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir2/img/} %
{/home/louis/projets/patacrep/patacrep/test/test_compilation/img/} %
{/home/louis/projets/patacrep/patacrep/patacrep/data/img/} %
}
\makeatletter
\@ifpackageloaded{hyperref}{}{
\usepackage{url}
\newcommand{\phantomsection}{}
\newcommand{\hyperlink}[2]{#2}
\newcommand{\href}[2]{\expandafter\url\expandafter{#1}}
}
\makeatother
\usepackage{chords}
\title{Guitar songbook}
\author{The Patacrep Team}
\newindex{titleidx}{subdir_title}
\newauthorindex{authidx}{subdir_auth}
\notenamesout{A}{B}{C}{D}{E}{F}{G}
\begin{document}
\maketitle
\showindex{\songindexname}{titleidx}
\showindex{\authorindexname}{authidx}
% list of chords
\ifchorded
\ifdiagram
\phantomsection
\addcontentsline{toc}{section}{\chordlistname}
\chords
\fi
\fi
\phantomsection
\addcontentsline{toc}{section}{\songlistname}
\begin{songs}{titleidx,authidx}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% songs/./datadir2.sgc
\selectlanguage{french}
\beginsong{Image included from a different datadir\\
Chordpro}[
by={
},
cov={/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir2/img/datadir2.png},
]
\cover
\lilypond{datadir2.ly}
\image{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir2/img/datadir2.png}
\endsong
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% songs/./datadir2.sg
\import{subdir_datadir/songs/}{datadir2.sg}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% songs/./relative.sgc
\selectlanguage{french}
\beginsong{Image included from song directory\\
Chordpro}[
by={
},
cov={/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir/songs/relative.png},
]
\cover
\lilypond{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir/songs/relative.ly}
\image{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir/songs/relative.png}
\endsong
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% songs/./datadir.sgc
\selectlanguage{french}
\beginsong{Image included from datadir\\
Chordpro}[
by={
},
cov={/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir/img/datadir.png},
]
\cover
\lilypond{datadir.ly}
\image{/home/louis/projets/patacrep/patacrep/test/test_compilation/subdir_datadir/img/datadir.png}
\endsong
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% songs/./relative.sg
\import{subdir_datadir/songs/}{relative.sg}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% songs/./datadir.sg
\import{subdir_datadir/songs/}{datadir.sg}
\end{songs}
\end{document}

89
test/test_compilation/test_compilation.py

@ -0,0 +1,89 @@
"""Tests for the chordpro parser."""
# pylint: disable=too-few-public-methods
import glob
import os
import subprocess
import unittest
from .. import dynamic # pylint: disable=unused-import
class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest):
"""Test of songbook compilation.
For any given `foo.sb`, it performs several checks:
- the corresponding tex file is generated;
- the generated tex file matches the `foo.tex.control` control file;
- the compilation (tex, pdf, indexes) works without errors.
This class does nothing by itself, but its metaclass populates it with test
methods testing parser and renderers.
"""
maxDiff = None
@classmethod
def _iter_testmethods(cls):
"""Iterate over songbook files to test."""
for songbook in sorted(glob.glob(os.path.join(
os.path.dirname(__file__),
'*.sb',
))):
base = songbook[:-len(".sb")]
control = "{}.tex.control".format(base)
if not os.path.exists(control):
continue
yield (
"test_{}".format(os.path.basename(base)),
[base],
)
@classmethod
def _create_test(cls, base):
"""Return a function testing that `base` compiles."""
def test_compile(self):
"""Test that `base` is correctly compiled."""
if base is None:
return
songbook = "{}.sb".format(base)
# Check tex generation
self.assertEqual(0, self.compile_songbook(songbook, "tex"))
# Check generated tex
control = "{}.tex.control".format(base)
tex = "{}.tex".format(base)
with open(control, 'r', encoding='utf8') as expectfile:
with open(tex, 'r', encoding='utf8') as latexfile:
self.assertMultiLineEqual(
latexfile.read(),
expectfile.read(),
)
# Check compilation
self.assertEqual(0, self.compile_songbook(songbook))
test_compile.__doc__ = (
"Test that '{base}' is correctly compiled."
).format(base=os.path.basename(base))
return test_compile
@staticmethod
def compile_songbook(songbook, steps=None):
"""Compile songbook, and return the command return code."""
command = ['python', '-m', 'patacrep.songbook', songbook]
if steps:
command.extend(['--steps', steps])
return subprocess.check_call(
command,
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
cwd=os.path.dirname(songbook),
)
Loading…
Cancel
Save