Index: tools/telemetry/third_party/coverage/tests/test_templite.py |
diff --git a/tools/telemetry/third_party/coverage/tests/test_templite.py b/tools/telemetry/third_party/coverage/tests/test_templite.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2f9b2dbde4bad22132d6d91bd38064e5c1549f2a |
--- /dev/null |
+++ b/tools/telemetry/third_party/coverage/tests/test_templite.py |
@@ -0,0 +1,294 @@ |
+# -*- coding: utf8 -*- |
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt |
+ |
+"""Tests for coverage.templite.""" |
+ |
+import re |
+ |
+from coverage.templite import Templite, TempliteSyntaxError, TempliteValueError |
+ |
+from tests.coveragetest import CoverageTest |
+ |
+# pylint: disable=unused-variable |
+ |
+class AnyOldObject(object): |
+ """Simple testing object. |
+ |
+ Use keyword arguments in the constructor to set attributes on the object. |
+ |
+ """ |
+ def __init__(self, **attrs): |
+ for n, v in attrs.items(): |
+ setattr(self, n, v) |
+ |
+ |
+class TempliteTest(CoverageTest): |
+ """Tests for Templite.""" |
+ |
+ run_in_temp_dir = False |
+ |
+ def try_render(self, text, ctx=None, result=None): |
+ """Render `text` through `ctx`, and it had better be `result`. |
+ |
+ Result defaults to None so we can shorten the calls where we expect |
+ an exception and never get to the result comparison. |
+ """ |
+ actual = Templite(text).render(ctx or {}) |
+ # If result is None, then an exception should have prevented us getting |
+ # to here. |
+ assert result is not None |
+ self.assertEqual(actual, result) |
+ |
+ def assertSynErr(self, msg): |
+ """Assert that a `TempliteSyntaxError` will happen. |
+ |
+ A context manager, and the message should be `msg`. |
+ """ |
+ pat = "^" + re.escape(msg) + "$" |
+ return self.assertRaisesRegex(TempliteSyntaxError, pat) |
+ |
+ def test_passthrough(self): |
+ # Strings without variables are passed through unchanged. |
+ self.assertEqual(Templite("Hello").render(), "Hello") |
+ self.assertEqual( |
+ Templite("Hello, 20% fun time!").render(), |
+ "Hello, 20% fun time!" |
+ ) |
+ |
+ def test_variables(self): |
+ # Variables use {{var}} syntax. |
+ self.try_render("Hello, {{name}}!", {'name':'Ned'}, "Hello, Ned!") |
+ |
+ def test_undefined_variables(self): |
+ # Using undefined names is an error. |
+ with self.assertRaises(Exception): |
+ self.try_render("Hi, {{name}}!") |
+ |
+ def test_pipes(self): |
+ # Variables can be filtered with pipes. |
+ data = { |
+ 'name': 'Ned', |
+ 'upper': lambda x: x.upper(), |
+ 'second': lambda x: x[1], |
+ } |
+ self.try_render("Hello, {{name|upper}}!", data, "Hello, NED!") |
+ |
+ # Pipes can be concatenated. |
+ self.try_render("Hello, {{name|upper|second}}!", data, "Hello, E!") |
+ |
+ def test_reusability(self): |
+ # A single Templite can be used more than once with different data. |
+ globs = { |
+ 'upper': lambda x: x.upper(), |
+ 'punct': '!', |
+ } |
+ |
+ template = Templite("This is {{name|upper}}{{punct}}", globs) |
+ self.assertEqual(template.render({'name':'Ned'}), "This is NED!") |
+ self.assertEqual(template.render({'name':'Ben'}), "This is BEN!") |
+ |
+ def test_attribute(self): |
+ # Variables' attributes can be accessed with dots. |
+ obj = AnyOldObject(a="Ay") |
+ self.try_render("{{obj.a}}", locals(), "Ay") |
+ |
+ obj2 = AnyOldObject(obj=obj, b="Bee") |
+ self.try_render("{{obj2.obj.a}} {{obj2.b}}", locals(), "Ay Bee") |
+ |
+ def test_member_function(self): |
+ # Variables' member functions can be used, as long as they are nullary. |
+ class WithMemberFns(AnyOldObject): |
+ """A class to try out member function access.""" |
+ def ditto(self): |
+ """Return twice the .txt attribute.""" |
+ return self.txt + self.txt |
+ obj = WithMemberFns(txt="Once") |
+ self.try_render("{{obj.ditto}}", locals(), "OnceOnce") |
+ |
+ def test_item_access(self): |
+ # Variables' items can be used. |
+ d = {'a':17, 'b':23} |
+ self.try_render("{{d.a}} < {{d.b}}", locals(), "17 < 23") |
+ |
+ def test_loops(self): |
+ # Loops work like in Django. |
+ nums = [1,2,3,4] |
+ self.try_render( |
+ "Look: {% for n in nums %}{{n}}, {% endfor %}done.", |
+ locals(), |
+ "Look: 1, 2, 3, 4, done." |
+ ) |
+ # Loop iterables can be filtered. |
+ def rev(l): |
+ """Return the reverse of `l`.""" |
+ l = l[:] |
+ l.reverse() |
+ return l |
+ |
+ self.try_render( |
+ "Look: {% for n in nums|rev %}{{n}}, {% endfor %}done.", |
+ locals(), |
+ "Look: 4, 3, 2, 1, done." |
+ ) |
+ |
+ def test_empty_loops(self): |
+ self.try_render( |
+ "Empty: {% for n in nums %}{{n}}, {% endfor %}done.", |
+ {'nums':[]}, |
+ "Empty: done." |
+ ) |
+ |
+ def test_multiline_loops(self): |
+ self.try_render( |
+ "Look: \n{% for n in nums %}\n{{n}}, \n{% endfor %}done.", |
+ {'nums':[1,2,3]}, |
+ "Look: \n\n1, \n\n2, \n\n3, \ndone." |
+ ) |
+ |
+ def test_multiple_loops(self): |
+ self.try_render( |
+ "{% for n in nums %}{{n}}{% endfor %} and " |
+ "{% for n in nums %}{{n}}{% endfor %}", |
+ {'nums': [1,2,3]}, |
+ "123 and 123" |
+ ) |
+ |
+ def test_comments(self): |
+ # Single-line comments work: |
+ self.try_render( |
+ "Hello, {# Name goes here: #}{{name}}!", |
+ {'name':'Ned'}, "Hello, Ned!" |
+ ) |
+ # and so do multi-line comments: |
+ self.try_render( |
+ "Hello, {# Name\ngoes\nhere: #}{{name}}!", |
+ {'name':'Ned'}, "Hello, Ned!" |
+ ) |
+ |
+ def test_if(self): |
+ self.try_render( |
+ "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!", |
+ {'ned': 1, 'ben': 0}, |
+ "Hi, NED!" |
+ ) |
+ self.try_render( |
+ "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!", |
+ {'ned': 0, 'ben': 1}, |
+ "Hi, BEN!" |
+ ) |
+ self.try_render( |
+ "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", |
+ {'ned': 0, 'ben': 0}, |
+ "Hi, !" |
+ ) |
+ self.try_render( |
+ "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", |
+ {'ned': 1, 'ben': 0}, |
+ "Hi, NED!" |
+ ) |
+ self.try_render( |
+ "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", |
+ {'ned': 1, 'ben': 1}, |
+ "Hi, NEDBEN!" |
+ ) |
+ |
+ def test_complex_if(self): |
+ class Complex(AnyOldObject): |
+ """A class to try out complex data access.""" |
+ def getit(self): |
+ """Return it.""" |
+ return self.it |
+ obj = Complex(it={'x':"Hello", 'y': 0}) |
+ self.try_render( |
+ "@" |
+ "{% if obj.getit.x %}X{% endif %}" |
+ "{% if obj.getit.y %}Y{% endif %}" |
+ "{% if obj.getit.y|str %}S{% endif %}" |
+ "!", |
+ { 'obj': obj, 'str': str }, |
+ "@XS!" |
+ ) |
+ |
+ def test_loop_if(self): |
+ self.try_render( |
+ "@{% for n in nums %}{% if n %}Z{% endif %}{{n}}{% endfor %}!", |
+ {'nums': [0,1,2]}, |
+ "@0Z1Z2!" |
+ ) |
+ self.try_render( |
+ "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!", |
+ {'nums': [0,1,2]}, |
+ "X@012!" |
+ ) |
+ self.try_render( |
+ "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!", |
+ {'nums': []}, |
+ "X!" |
+ ) |
+ |
+ def test_nested_loops(self): |
+ self.try_render( |
+ "@" |
+ "{% for n in nums %}" |
+ "{% for a in abc %}{{a}}{{n}}{% endfor %}" |
+ "{% endfor %}" |
+ "!", |
+ {'nums': [0,1,2], 'abc': ['a', 'b', 'c']}, |
+ "@a0b0c0a1b1c1a2b2c2!" |
+ ) |
+ |
+ def test_non_ascii(self): |
+ self.try_render( |
+ u"{{where}} ollǝɥ", |
+ { 'where': u'ǝɹǝɥʇ' }, |
+ u"ǝɹǝɥʇ ollǝɥ" |
+ ) |
+ |
+ def test_exception_during_evaluation(self): |
+ # TypeError: Couldn't evaluate {{ foo.bar.baz }}: |
+ msg = "Couldn't evaluate None.bar" |
+ with self.assertRaisesRegex(TempliteValueError, msg): |
+ self.try_render( |
+ "Hey {{foo.bar.baz}} there", {'foo': None}, "Hey ??? there" |
+ ) |
+ |
+ def test_bad_names(self): |
+ with self.assertSynErr("Not a valid name: 'var%&!@'"): |
+ self.try_render("Wat: {{ var%&!@ }}") |
+ with self.assertSynErr("Not a valid name: 'filter%&!@'"): |
+ self.try_render("Wat: {{ foo|filter%&!@ }}") |
+ with self.assertSynErr("Not a valid name: '@'"): |
+ self.try_render("Wat: {% for @ in x %}{% endfor %}") |
+ |
+ def test_bogus_tag_syntax(self): |
+ with self.assertSynErr("Don't understand tag: 'bogus'"): |
+ self.try_render("Huh: {% bogus %}!!{% endbogus %}??") |
+ |
+ def test_malformed_if(self): |
+ with self.assertSynErr("Don't understand if: '{% if %}'"): |
+ self.try_render("Buh? {% if %}hi!{% endif %}") |
+ with self.assertSynErr("Don't understand if: '{% if this or that %}'"): |
+ self.try_render("Buh? {% if this or that %}hi!{% endif %}") |
+ |
+ def test_malformed_for(self): |
+ with self.assertSynErr("Don't understand for: '{% for %}'"): |
+ self.try_render("Weird: {% for %}loop{% endfor %}") |
+ with self.assertSynErr("Don't understand for: '{% for x from y %}'"): |
+ self.try_render("Weird: {% for x from y %}loop{% endfor %}") |
+ with self.assertSynErr("Don't understand for: '{% for x, y in z %}'"): |
+ self.try_render("Weird: {% for x, y in z %}loop{% endfor %}") |
+ |
+ def test_bad_nesting(self): |
+ with self.assertSynErr("Unmatched action tag: 'if'"): |
+ self.try_render("{% if x %}X") |
+ with self.assertSynErr("Mismatched end tag: 'for'"): |
+ self.try_render("{% if x %}X{% endfor %}") |
+ with self.assertSynErr("Too many ends: '{% endif %}'"): |
+ self.try_render("{% if x %}{% endif %}{% endif %}") |
+ |
+ def test_malformed_end(self): |
+ with self.assertSynErr("Don't understand end: '{% end if %}'"): |
+ self.try_render("{% if x %}X{% end if %}") |
+ with self.assertSynErr("Don't understand end: '{% endif now %}'"): |
+ self.try_render("{% if x %}X{% endif now %}") |