You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
544 lines
16 KiB
544 lines
16 KiB
# vim:fileencoding=utf-8
|
|
|
|
# Copyright (c) 2006-2019 Andrey Golovigin
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
# a copy of this software and associated documentation files (the
|
|
# "Software"), to deal in the Software without restriction, including
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
# the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
"""
|
|
Test routines for the BibTeX .bib parser.
|
|
|
|
Some tests were adopted from Babybib - another BibTeX parser by Matthew Brett.
|
|
https://github.com/matthew-brett/babybib
|
|
|
|
"""
|
|
|
|
from __future__ import absolute_import, unicode_literals
|
|
|
|
from unittest import TestCase
|
|
|
|
import six
|
|
from six.moves import zip_longest
|
|
|
|
from pybtex.database import BibliographyData, Entry, Person
|
|
from pybtex.database.input.bibtex import Parser
|
|
|
|
|
|
class _TestParser(Parser):
|
|
def __init__(self, *args, **kwargs):
|
|
super(_TestParser, self).__init__(*args, **kwargs)
|
|
self.errors = []
|
|
|
|
def handle_error(self, error):
|
|
self.errors.append(error)
|
|
|
|
|
|
class ParserTest(object):
|
|
input_string = None
|
|
input_strings = []
|
|
correct_result = None
|
|
parser_options = {}
|
|
errors = []
|
|
|
|
def setUp(self):
|
|
if not self.input_strings:
|
|
self.input_strings = [self.input_string]
|
|
|
|
def test_parser(self):
|
|
parser = _TestParser(encoding='UTF-8', **self.parser_options)
|
|
for input_string in self.input_strings:
|
|
parser.parse_string(input_string)
|
|
result = parser.data
|
|
correct_result = self.correct_result
|
|
assert result == correct_result
|
|
for error, correct_error in zip_longest(parser.errors, self.errors):
|
|
actual_error = six.text_type(error)
|
|
assert actual_error == correct_error
|
|
|
|
|
|
class EmptyDataTest(ParserTest, TestCase):
|
|
input_string = u''
|
|
correct_result = BibliographyData()
|
|
|
|
|
|
class BracesTest(ParserTest, TestCase):
|
|
input_string = u"""@ARTICLE{
|
|
test,
|
|
title={Polluted
|
|
with {DDT}.
|
|
},
|
|
}"""
|
|
correct_result = BibliographyData([(u'test', Entry('article', [(u'title', 'Polluted with {DDT}.')]))])
|
|
|
|
|
|
class BracesAndQuotesTest(ParserTest, TestCase):
|
|
input_string = u'''@ARTICLE{
|
|
test,
|
|
title="Nested braces and {"quotes"}",
|
|
}'''
|
|
correct_result = BibliographyData([(u'test', Entry('article', [(u'title', 'Nested braces and {"quotes"}')]))])
|
|
|
|
|
|
class EntryInStringTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
@article{Me2010, author="Brett, Matthew", title="An article
|
|
@article{something, author={Name, Another}, title={not really an article}}
|
|
"}
|
|
@article{Me2009,author={Nom de Plume, My}, title="A short story"}
|
|
"""
|
|
correct_result = BibliographyData(
|
|
entries=[
|
|
(u'Me2010', Entry(u'article',
|
|
fields=[
|
|
(u'title', u'An article @article{something, author={Name, Another}, title={not really an article}}'),
|
|
],
|
|
persons=[(u'author', [Person(u'Brett, Matthew')])]
|
|
)),
|
|
(u'Me2009', Entry(u'article',
|
|
fields=[(u'title', u'A short story')],
|
|
persons={u'author': [Person(u'Nom de Plume, My')]}
|
|
)),
|
|
]
|
|
)
|
|
|
|
|
|
class EntryInCommentTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
Both the articles register despite the comment block
|
|
@Comment{
|
|
@article{Me2010, title="An article"}
|
|
@article{Me2009, title="A short story"}
|
|
}
|
|
These all work OK without errors
|
|
@Comment{and more stuff}
|
|
|
|
Last article to show we can get here
|
|
@article{Me2011, }
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('Me2010', Entry('article', fields=[('title', 'An article')])),
|
|
('Me2009', Entry('article', fields=[('title', 'A short story')])),
|
|
('Me2011', Entry('article')),
|
|
])
|
|
|
|
|
|
class AtTest(ParserTest, TestCase):
|
|
# FIXME: check warnings
|
|
input_string = u"""
|
|
The @ here parses fine in both cases
|
|
@article{Me2010,
|
|
title={An @tey article}}
|
|
@article{Me2009, title="A @tey short story"}
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('Me2010', Entry('article', [('title', 'An @tey article')])),
|
|
('Me2009', Entry('article', [('title', 'A @tey short story')])),
|
|
])
|
|
errors = [
|
|
"syntax error in line 2: '(' or '{' expected",
|
|
]
|
|
|
|
class EntryTypesTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
Testing what are allowed for entry types
|
|
|
|
These are OK
|
|
@somename{an_id,}
|
|
@t2{another_id,}
|
|
@t@{again_id,}
|
|
@t+{aa1_id,}
|
|
@_t{aa2_id,}
|
|
|
|
These ones not
|
|
@2thou{further_id,}
|
|
@some name{id3,}
|
|
@some#{id4,}
|
|
@some%{id4,}
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('an_id', Entry('somename')),
|
|
('another_id', Entry('t2')),
|
|
('again_id', Entry('t@')),
|
|
('aa1_id', Entry('t+')),
|
|
('aa2_id', Entry('_t')),
|
|
])
|
|
errors = [
|
|
"syntax error in line 12: a valid name expected",
|
|
"syntax error in line 13: '(' or '{' expected",
|
|
"syntax error in line 14: '(' or '{' expected",
|
|
"syntax error in line 15: '(' or '{' expected",
|
|
]
|
|
|
|
|
|
class FieldNamesTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
Check for characters allowed in field names
|
|
Here the cite key is fine, but the field name is not allowed:
|
|
``You are missing a field name``
|
|
@article{2010, 0author="Me"}
|
|
|
|
Underscores allowed (no error)
|
|
@article{2011, _author="Me"}
|
|
|
|
Not so for spaces obviously (``expecting an '='``)
|
|
@article{2012, author name = "Myself"}
|
|
|
|
Or hashes (``missing a field name``)
|
|
@article{2013, #name = "Myself"}
|
|
|
|
But field names can start with +-.
|
|
@article{2014, .name = "Myself"}
|
|
@article{2015, +name = "Myself"}
|
|
@article{2016, -name = "Myself"}
|
|
@article{2017, @name = "Myself"}
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('2010', Entry('article')),
|
|
('2011', Entry('article', [('_author', 'Me')])),
|
|
('2012', Entry('article')),
|
|
('2013', Entry('article')),
|
|
('2014', Entry('article', [('.name', 'Myself')])),
|
|
('2015', Entry('article', [('+name', 'Myself')])),
|
|
('2016', Entry('article', [('-name', 'Myself')])),
|
|
('2017', Entry('article', [('@name', 'Myself')])),
|
|
])
|
|
errors = [
|
|
"syntax error in line 5: '}' expected",
|
|
"syntax error in line 11: '=' expected",
|
|
'syntax error in line 14: \'}\' expected',
|
|
]
|
|
|
|
|
|
class InlineCommentTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
"some text" causes an error like this
|
|
``You're missing a field name---line 6 of file bibs/inline_comment.bib``
|
|
for all 3 of the % some text occurences below; in each case the parser keeps
|
|
what it has up till that point and skips, so that it correctly gets the last
|
|
entry.
|
|
@article{Me2010,}
|
|
@article{Me2011,
|
|
author="Brett-like, Matthew",
|
|
% some text
|
|
title="Another article"}
|
|
@article{Me2012, % some text
|
|
author="Real Brett"}
|
|
This one correctly read
|
|
@article{Me2013,}
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('Me2010', Entry('article')),
|
|
('Me2011', Entry('article', persons={'author': [
|
|
Person(first='Matthew', last='Brett-like'),
|
|
]})),
|
|
('Me2012', Entry('article')),
|
|
('Me2013', Entry('article')),
|
|
])
|
|
errors = [
|
|
"syntax error in line 10: '}' expected",
|
|
"syntax error in line 12: '}' expected",
|
|
]
|
|
|
|
|
|
class SimpleEntryTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
% maybe the simplest possible
|
|
% just a comment and one reference
|
|
|
|
@ARTICLE{Brett2002marsbar,
|
|
author = {Matthew Brett and Jean-Luc Anton and Romain Valabregue and Jean-Baptise
|
|
Poline},
|
|
title = {{Region of interest analysis using an SPM toolbox}},
|
|
journal = {Neuroimage},
|
|
institution = {},
|
|
year = {2002},
|
|
volume = {16},
|
|
pages = {1140--1141},
|
|
number = {2}
|
|
}
|
|
"""
|
|
correct_result = BibliographyData({
|
|
'Brett2002marsbar': Entry('article',
|
|
fields=[
|
|
('title', '{Region of interest analysis using an SPM toolbox}'),
|
|
('journal', 'Neuroimage'),
|
|
('institution', ''),
|
|
('year', '2002'),
|
|
('volume', '16'),
|
|
('pages', '1140--1141'),
|
|
('number', '2'),
|
|
],
|
|
persons={
|
|
'author': [
|
|
Person(first='Matthew', last='Brett'),
|
|
Person(first='Jean-Luc', last='Anton'),
|
|
Person(first='Romain', last='Valabregue'),
|
|
Person(first='Jean-Baptise', last='Poline'),
|
|
],
|
|
},
|
|
)
|
|
})
|
|
|
|
|
|
class KeyParsingTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
# will not work as expected
|
|
@article(test(parens1))
|
|
|
|
# works fine
|
|
@article(test(parens2),)
|
|
|
|
# works fine
|
|
@article{test(braces1)}
|
|
|
|
# also works
|
|
@article{test(braces2),}
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('test(parens1))', Entry('article')),
|
|
('test(parens2)', Entry('article')),
|
|
('test(braces1)', Entry('article')),
|
|
('test(braces2)', Entry('article')),
|
|
])
|
|
errors = [
|
|
"syntax error in line 5: ')' expected",
|
|
]
|
|
|
|
|
|
class KeylessEntriesTest(ParserTest, TestCase):
|
|
parser_options = {'keyless_entries': True}
|
|
input_string = u"""
|
|
@BOOK(
|
|
title="I Am Jackie Chan: My Life in Action",
|
|
year=1999
|
|
)
|
|
@BOOK()
|
|
@BOOK{}
|
|
|
|
@BOOK{
|
|
title = "Der deutsche Jackie Chan Filmführer",
|
|
}
|
|
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('unnamed-1', Entry('book', [('title', 'I Am Jackie Chan: My Life in Action'), ('year', '1999')])),
|
|
('unnamed-2', Entry('book')),
|
|
('unnamed-3', Entry('book')),
|
|
('unnamed-4', Entry('book', [('title', u'Der deutsche Jackie Chan Filmführer')])),
|
|
])
|
|
|
|
|
|
class MacrosTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
@String{and = { and }}
|
|
@String{etal = and # { {et al.}}}
|
|
@Article(
|
|
unknown,
|
|
author = nobody,
|
|
)
|
|
@Article(
|
|
gsl,
|
|
author = "Gough, Brian"#etal,
|
|
)
|
|
"""
|
|
correct_result = BibliographyData([
|
|
('unknown', Entry('article')),
|
|
('gsl', Entry('article', persons={u'author': [Person(u'Gough, Brian'), Person(u'{et al.}')]})),
|
|
])
|
|
errors = [
|
|
'undefined string in line 6: nobody',
|
|
]
|
|
|
|
|
|
class WantedEntriesTest(ParserTest, TestCase):
|
|
parser_options = {'wanted_entries': ['GSL']}
|
|
input_string = u"""
|
|
@Article(
|
|
gsl,
|
|
)
|
|
"""
|
|
correct_result = BibliographyData(entries={
|
|
'GSL': Entry('article'),
|
|
})
|
|
|
|
|
|
class CrossrefTest(ParserTest, TestCase):
|
|
parser_options = {'wanted_entries': ['GSL', 'GSL2']}
|
|
input_string = u"""
|
|
@Article(gsl, crossref="the_journal")
|
|
@Article(gsl2, crossref="The_Journal")
|
|
@Journal{the_journal,}
|
|
"""
|
|
correct_result = BibliographyData(entries=[
|
|
('GSL', Entry('article', [('crossref', 'the_journal')])),
|
|
('GSL2', Entry('article', [('crossref', 'The_Journal')])),
|
|
('the_journal', Entry('journal')),
|
|
])
|
|
|
|
|
|
class CrossrefWantedTest(ParserTest, TestCase):
|
|
"""When cross-referencing an explicitly cited, the key from .aux file should be used."""
|
|
|
|
parser_options = {'wanted_entries': ['GSL', 'GSL2', 'The_Journal']}
|
|
input_string = u"""
|
|
@Article(gsl, crossref="the_journal")
|
|
@Article(gsl2, crossref="The_Journal")
|
|
@Journal{the_journal,}
|
|
"""
|
|
correct_result = BibliographyData(entries=[
|
|
('GSL', Entry('article', [('crossref', 'the_journal')])),
|
|
('GSL2', Entry('article', [('crossref', 'The_Journal')])),
|
|
('The_Journal', Entry('journal')),
|
|
])
|
|
|
|
|
|
class UnusedEntryTest(ParserTest, TestCase):
|
|
parser_options = {'wanted_entries': []}
|
|
input_string = u"""
|
|
@Article(
|
|
gsl,
|
|
author = nobody,
|
|
)
|
|
"""
|
|
correct_result = BibliographyData()
|
|
|
|
|
|
class CrossFileMacrosTest(ParserTest, TestCase):
|
|
input_strings = [
|
|
u'@string{jackie = "Jackie Chan"}',
|
|
u""",
|
|
@Book{
|
|
i_am_jackie,
|
|
author = jackie,
|
|
title = "I Am " # jackie # ": My Life in Action",
|
|
}
|
|
""",
|
|
]
|
|
correct_result = BibliographyData({
|
|
'i_am_jackie': Entry('book',
|
|
fields=[('title', 'I Am Jackie Chan: My Life in Action')],
|
|
persons={'author': [Person(u'Chan, Jackie')]}
|
|
),
|
|
})
|
|
|
|
|
|
class AtCharacterTest(ParserTest, TestCase):
|
|
input_strings = [
|
|
r""",
|
|
@proceedings{acc,
|
|
title = {Proc.\@ of the American Control Conference},
|
|
notes = "acc@example.org"
|
|
}
|
|
""",
|
|
]
|
|
correct_result = BibliographyData({
|
|
'acc': Entry('proceedings',
|
|
fields=[
|
|
('title', r'Proc.\@ of the American Control Conference'),
|
|
('notes', 'acc@example.org'),
|
|
],
|
|
),
|
|
})
|
|
|
|
|
|
class AtCharacterInUnwantedEntryTest(ParserTest, TestCase):
|
|
parser_options = {'wanted_entries': []}
|
|
input_strings = [
|
|
r""",
|
|
@proceedings{acc,
|
|
title = {Proc.\@ of the American Control Conference},
|
|
notes = "acc@example.org"
|
|
}
|
|
""",
|
|
]
|
|
correct_result = BibliographyData()
|
|
|
|
|
|
class CaseSensitivityTest(ParserTest, TestCase):
|
|
input_strings = [
|
|
r""",
|
|
@Article{CamelCase,
|
|
Title = {To CamelCase or Under score},
|
|
year = 2009,
|
|
NOTES = "none"
|
|
}
|
|
""",
|
|
]
|
|
correct_result = BibliographyData({
|
|
'CamelCase': Entry('article',
|
|
fields=[
|
|
('Title', 'To CamelCase or Under score'),
|
|
('year', '2009'),
|
|
('NOTES', 'none'),
|
|
],
|
|
),
|
|
})
|
|
|
|
|
|
class WindowsNewlineTest(ParserTest, TestCase):
|
|
input_strings = [
|
|
u"""'@Article\r\n\r\n\r\n}\r\n'""",
|
|
]
|
|
correct_result = BibliographyData()
|
|
errors = ["syntax error in line 4: '(' or '{' expected"]
|
|
|
|
|
|
class DuplicateFieldTest(ParserTest, TestCase):
|
|
input_strings = [
|
|
r"""
|
|
@MASTERSTHESIS{
|
|
Mastering,
|
|
year = 1364,
|
|
title = "Mastering Thesis Writing",
|
|
school = "Charles University in Prague",
|
|
TITLE = "No One Reads Master's Theses Anyway LOL",
|
|
TiTlE = "Well seriously, lol.",
|
|
}
|
|
"""
|
|
]
|
|
correct_result = BibliographyData({
|
|
'Mastering': Entry('mastersthesis',
|
|
fields=[
|
|
('year', '1364'),
|
|
('title', 'Mastering Thesis Writing'),
|
|
('school', 'Charles University in Prague'),
|
|
],
|
|
),
|
|
})
|
|
errors = [
|
|
'entry with key Mastering has a duplicate TITLE field',
|
|
'entry with key Mastering has a duplicate TiTlE field',
|
|
]
|
|
|
|
|
|
class DuplicatePersonFieldTest(ParserTest, TestCase):
|
|
input_string = u"""
|
|
@article{Me2009,author={Nom de Plume, My}, title="A short story", AUTHoR = {Foo}}
|
|
"""
|
|
correct_result = BibliographyData(
|
|
entries=[
|
|
(u'Me2009', Entry(u'article',
|
|
fields=[(u'title', u'A short story')],
|
|
persons={u'author': [Person(u'Nom de Plume, My')]}
|
|
)),
|
|
]
|
|
)
|
|
errors = [
|
|
'entry with key Me2009 has a duplicate AUTHoR field',
|
|
] |