diff --git a/patacrep/songs/chordpro/lexer.py b/patacrep/songs/chordpro/lexer.py
index a0b57826..50048b4c 100644
--- a/patacrep/songs/chordpro/lexer.py
+++ b/patacrep/songs/chordpro/lexer.py
@@ -80,8 +80,8 @@ class ChordProLexer:
r'[ \t]+'
return token
- t_tablature_TEXT = r'[^\n]+'
- t_tablature_ENDOFLINE = r'\n'
+ t_tablature_TEXT = r'[^\n\r]+'
+ t_tablature_ENDOFLINE = r'\r?\n'
def __init__(self, *, filename=None):
self.__class__.lexer = lex.lex(module=self)
@@ -90,7 +90,7 @@ class ChordProLexer:
# Define a rule so we can track line numbers
@staticmethod
def t_ENDOFLINE(token):
- r'[\n\r]'
+ r'\r?\n'
token.lexer.lineno += 1
return token
@@ -101,7 +101,7 @@ class ChordProLexer:
@staticmethod
def t_WORD(token):
- r'[^{}\n\][\t ]+'
+ r'[^{}\r\n\][\t ]+'
return token
def t_LBRACKET(self, __token):
diff --git a/test/test_chordpro/metadata.tex b/test/test_chordpro/metadata.tex
index 0be273f1..52582de4 100644
--- a/test/test_chordpro/metadata.tex
+++ b/test/test_chordpro/metadata.tex
@@ -11,7 +11,7 @@ Subtitle5}[
Texte de Jean Richepin, chanté par Georges Brassens },
album={Album},
copyright={Copyright},
- cover={img/test/test_chordpro/metadata_cover},
+ cover={img/metadata_cover},
foo={Foo},
]
@@ -19,8 +19,8 @@ Subtitle5}[
\textnote{Comment}
\musicnote{GuitarComment}
-\lilypond{scores/test/test_chordpro/metadata_lilypond}
-\image{img/test/test_chordpro/metadata_image}
+\lilypond{scores/metadata_lilypond}
+\image{img/metadata_image}
diff --git a/test/test_chordpro/newline.crlf.html b/test/test_chordpro/newline.crlf.html
new file mode 100644
index 00000000..5540af64
--- /dev/null
+++ b/test/test_chordpro/newline.crlf.html
@@ -0,0 +1,43 @@
+
+Lang: en
+
+
+
+
+
This is a verse
+With a new line
+
+The second part of the verse
+Is this line
+
+
+
Here is a new line at the end
+
+
+
+
Foo bar
+
+
+
+And a new line
+At the beginning
+
+
+
New lines can also
+
+Be in chorus
+
+
+
New lines can also
+
+Be in bridges
+
+
+
New lines can also
+
+Be surrounded by spaces
+
+
+
New lines cannot
+
+
\ No newline at end of file
diff --git a/test/test_chordpro/newline.crlf.sgc b/test/test_chordpro/newline.crlf.sgc
new file mode 100644
index 00000000..e9bf0c62
--- /dev/null
+++ b/test/test_chordpro/newline.crlf.sgc
@@ -0,0 +1,41 @@
+{lang: en}
+
+This is a verse
+With a new line
+{newline}
+The second part of the verse
+Is this line
+
+
+Here is a new line at the end
+{newline}
+
+
+Foo bar
+
+
+{newline}
+And a new line
+At the beginning
+
+
+{start_of_chorus}
+ New lines can also
+ {newline}
+ Be in chorus
+{end_of_chorus}
+
+
+{start_of_bridge}
+ New lines can also
+ {newline}
+ Be in bridges
+{end_of_bridge}
+
+
+New lines can also
+{newline}
+Be surrounded by spaces
+
+
+New lines cannot
diff --git a/test/test_chordpro/newline.crlf.source b/test/test_chordpro/newline.crlf.source
new file mode 100644
index 00000000..bd45d5ce
--- /dev/null
+++ b/test/test_chordpro/newline.crlf.source
@@ -0,0 +1,2 @@
+# This content will be overwritten with `newline.source` content
+# with windows line endings (CRLF) - for testing purposes
diff --git a/test/test_chordpro/newline.crlf.tex b/test/test_chordpro/newline.crlf.tex
new file mode 100644
index 00000000..d557de5f
--- /dev/null
+++ b/test/test_chordpro/newline.crlf.tex
@@ -0,0 +1,61 @@
+\selectlanguage{english}
+
+\beginsong{}[
+ by={
+ },
+]
+
+
+\begin{verse}
+ This is a verse
+ With a new line
+ ~\\
+ The second part of the verse
+ Is this line
+\end{verse}
+
+
+\begin{verse}
+ Here is a new line at the end
+ ~\\
+\end{verse}
+
+
+\begin{verse}
+ Foo bar
+\end{verse}
+
+
+\begin{verse}
+ ~\\
+ And a new line
+ At the beginning
+\end{verse}
+
+
+\begin{chorus}
+ New lines can also
+ ~\\
+ Be in chorus
+\end{chorus}
+
+
+\begin{bridge}
+ New lines can also
+ ~\\
+ Be in bridges
+\end{bridge}
+
+
+\begin{verse}
+ New lines can also
+ ~\\
+ Be surrounded by spaces
+\end{verse}
+
+
+\begin{verse}
+ New lines cannot
+\end{verse}
+
+\endsong
diff --git a/test/test_chordpro/test_parser.py b/test/test_chordpro/test_parser.py
index f542a203..39c37052 100644
--- a/test/test_chordpro/test_parser.py
+++ b/test/test_chordpro/test_parser.py
@@ -2,6 +2,7 @@
# pylint: disable=too-few-public-methods
+import contextlib
import glob
import os
import unittest
@@ -33,6 +34,38 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest):
maxDiff = None
+ @classmethod
+ def setUpClass(cls):
+ cls._overwrite_clrf()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls._reset_clrf()
+
+ @staticmethod
+ @contextlib.contextmanager
+ def chdir():
+ """Context to temporarry change current directory to this file directory
+ """
+ olddir = os.getcwd()
+ os.chdir(resource_filename(__name__, ""))
+ yield
+ os.chdir(olddir)
+
+ def assertRender(self, base, destformat): # pylint: disable=invalid-name
+ """Assert that `{base}.source` is correctly rendered in the `destformat`.
+ """
+ sourcename = "{}.source".format(base)
+ destname = "{}.{}".format(base, destformat)
+ with self.chdir():
+ with open_read(destname) as expectfile:
+ with disable_logging():
+ song = self.song_plugins[LANGUAGES[destformat]]['sgc'](sourcename, self.config)
+ self.assertMultiLineEqual(
+ song.render(output=sourcename).strip(),
+ expectfile.read().strip(),
+ )
+
@classmethod
def _iter_testmethods(cls):
"""Iterate over song files to test."""
@@ -40,7 +73,7 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest):
cls.config = DEFAULT_CONFIG
if 'datadir' not in cls.config:
cls.config['datadir'] = []
- cls.config['datadir'].append(resource_filename(__name__, 'datadir'))
+ cls.config['datadir'].append('datadir')
cls.config['datadir'].append(__DATADIR__)
cls.song_plugins = files.load_plugins(
@@ -48,19 +81,17 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest):
root_modules=['songs'],
keyword='SONG_RENDERERS',
)
- for source in sorted(glob.glob(os.path.join(
- os.path.dirname(__file__),
- '*.source',
- ))):
- base = os.path.relpath(source, os.getcwd())[:-len(".source")]
- for dest in LANGUAGES:
- destname = "{}.{}".format(base, dest)
- if not os.path.exists(destname):
- continue
- yield (
- "test_{}_{}".format(os.path.basename(base), dest),
- cls._create_test(base, dest),
- )
+ with cls.chdir():
+ for source in sorted(glob.glob('*.source')):
+ base = source[:-len(".source")]
+ for dest in LANGUAGES:
+ destname = "{}.{}".format(base, dest)
+ if not os.path.exists(destname):
+ continue
+ yield (
+ "test_{}_{}".format(base, dest),
+ cls._create_test(base, dest),
+ )
@classmethod
def _create_test(cls, base, dest):
@@ -71,19 +102,35 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest):
"""Test that `base` is correctly parsed and rendered."""
if base is None or dest is None:
return
- destname = "{}.{}".format(base, dest)
- with open_read(destname) as expectfile:
- chordproname = "{}.source".format(base)
- with disable_logging():
- self.assertMultiLineEqual(
- self.song_plugins[LANGUAGES[dest]]['sgc'](chordproname, self.config).render(
- output=chordproname,
- ).strip(),
- expectfile.read().strip(),
- )
+ self.assertRender(base, dest)
test_parse_render.__doc__ = (
"Test that '{base}' is correctly parsed and rendererd into '{format}' format."
).format(base=os.path.basename(base), format=dest)
return test_parse_render
+ @classmethod
+ def _overwrite_clrf(cls):
+ """Overwrite `*.crlf.source` files to force the CRLF line endings.
+ """
+ with cls.chdir():
+ for crlfname in sorted(glob.glob('*.crlf.source')):
+ base = crlfname[:-len(".crlf.source")]
+ sourcename = base + ".source"
+ with open_read(sourcename) as sourcefile:
+ with open(crlfname, 'w') as crlffile:
+ for line in sourcefile:
+ crlffile.write(line.replace('\n', '\r\n'))
+
+ @classmethod
+ def _reset_clrf(cls):
+ """Reset `*.crlf.source` files.
+ """
+ crlf_msg = """# This content will be overwritten with `{}.source` content
+# with windows line endings (CRLF) - for testing purposes
+"""
+ with cls.chdir():
+ for crlfname in sorted(glob.glob('*.crlf.source')):
+ base = crlfname[:-len(".crlf.source")]
+ with open(crlfname, 'w') as crlffile:
+ crlffile.write(crlf_msg.format(base))