diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index f43cfd9705..3addf1ddfb 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -439,6 +439,7 @@ block_re = re.compile(r"""^\s*blocktrans(\s+.*context\s+(?:"[^"]*?")|(?:'[^']*?' endblock_re = re.compile(r"""^\s*endblocktrans$""") plural_re = re.compile(r"""^\s*plural$""") constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""") +one_percent_re = re.compile(r"""(?<!%)%(?!%)""") def templatize(src, origin=None): @@ -529,6 +530,7 @@ def templatize(src, origin=None): g = g.strip('"') elif g[0] == "'": g = g.strip("'") + g = one_percent_re.sub('%%', g) if imatch.group(2): # A context is provided context_match = context_re.match(imatch.group(2)) diff --git a/tests/regressiontests/i18n/commands/compilation.py b/tests/regressiontests/i18n/commands/compilation.py index 98b4b33356..1f38910b61 100644 --- a/tests/regressiontests/i18n/commands/compilation.py +++ b/tests/regressiontests/i18n/commands/compilation.py @@ -7,17 +7,15 @@ except ImportError: from django.core.management import CommandError from django.core.management.commands.compilemessages import compile_messages from django.test import TestCase +from django.test.utils import override_settings +from django.utils import translation - -LOCALE='es_AR' +test_dir = os.path.abspath(os.path.dirname(__file__)) class MessageCompilationTests(TestCase): - MO_FILE='locale/%s/LC_MESSAGES/django.mo' % LOCALE - def setUp(self): self._cwd = os.getcwd() - self.test_dir = os.path.abspath(os.path.dirname(__file__)) def tearDown(self): os.chdir(self._cwd) @@ -25,11 +23,60 @@ class MessageCompilationTests(TestCase): class PoFileTests(MessageCompilationTests): + LOCALE='es_AR' + MO_FILE='locale/%s/LC_MESSAGES/django.mo' % LOCALE + def test_bom_rejection(self): - os.chdir(self.test_dir) - # We don't use the django.core.management intrastructure (call_command() + os.chdir(test_dir) + # We don't use the django.core.management infrastructure (call_command() # et al) because CommandError's cause exit(1) there. We test the # underlying compile_messages function instead out = StringIO() - self.assertRaises(CommandError, compile_messages, out, locale=LOCALE) + self.assertRaises(CommandError, compile_messages, out, locale=self.LOCALE) self.assertFalse(os.path.exists(self.MO_FILE)) + + +class PoFileContentsTests(MessageCompilationTests): + # Ticket #11240 + + LOCALE='fr' + MO_FILE='locale/%s/LC_MESSAGES/django.mo' % LOCALE + + def setUp(self): + super(PoFileContentsTests, self).setUp() + self.addCleanup(os.unlink, os.path.join(test_dir, self.MO_FILE)) + + def test_percent_symbol_in_po_file(self): + os.chdir(test_dir) + # We don't use the django.core.management infrastructure (call_command() + # et al) because CommandError's cause exit(1) there. We test the + # underlying compile_messages function instead + out = StringIO() + compile_messages(out, locale=self.LOCALE) + self.assertTrue(os.path.exists(self.MO_FILE)) + + +class PercentRenderingTests(MessageCompilationTests): + # Ticket #11240 -- Testing rendering doesn't belong here but we are trying + # to keep tests for all the stack together + + LOCALE='it' + MO_FILE='locale/%s/LC_MESSAGES/django.mo' % LOCALE + + @override_settings(LOCALE_PATHS=(os.path.join(test_dir, 'locale'),)) + def test_percent_symbol_escaping(self): + from django.template import Template, Context + os.chdir(test_dir) + # We don't use the django.core.management infrastructure (call_command() + # et al) because CommandError's cause exit(1) there. We test the + # underlying compile_messages function instead + out = StringIO() + compile_messages(out, locale=self.LOCALE) + with translation.override(self.LOCALE): + t = Template('{% load i18n %}{% trans "Looks like a str fmt spec %% o but shouldn\'t be interpreted as such" %}') + rendered = t.render(Context({})) + self.assertEqual(rendered, 'IT translation contains %% for the above string') + + t = Template('{% load i18n %}{% trans "Completed 50%% of all the tasks" %}') + rendered = t.render(Context({})) + self.assertEqual(rendered, 'IT translation of Completed 50%% of all the tasks') diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py index fb612a3a42..e6796d47f4 100644 --- a/tests/regressiontests/i18n/commands/extraction.py +++ b/tests/regressiontests/i18n/commands/extraction.py @@ -31,10 +31,13 @@ class ExtractorTests(TestCase): os.chdir(self._cwd) def assertMsgId(self, msgid, s, use_quotes=True): + q = '"' if use_quotes: msgid = '"%s"' % msgid + q = "'" + needle = 'msgid %s' % msgid msgid = re.escape(msgid) - return self.assertTrue(re.search('^msgid %s' % msgid, s, re.MULTILINE)) + return self.assertTrue(re.search('^msgid %s' % msgid, s, re.MULTILINE), 'Could not find %(q)s%(n)s%(q)s in generated PO file' % {'n':needle, 'q':q}) def assertNotMsgId(self, msgid, s, use_quotes=True): if use_quotes: @@ -49,35 +52,57 @@ class BasicExtractorTests(ExtractorTests): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertTrue('#. Translators: This comment should be extracted' in po_contents) - self.assertTrue('This comment should not be extracted' not in po_contents) - # Comments in templates - self.assertTrue('#. Translators: Django template comment for translators' in po_contents) - self.assertTrue("#. Translators: Django comment block for translators\n#. string's meaning unveiled" in po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertTrue('#. Translators: This comment should be extracted' in po_contents) + self.assertTrue('This comment should not be extracted' not in po_contents) + # Comments in templates + self.assertTrue('#. Translators: Django template comment for translators' in po_contents) + self.assertTrue("#. Translators: Django comment block for translators\n#. string's meaning unveiled" in po_contents) - self.assertTrue('#. Translators: One-line translator comment #1' in po_contents) - self.assertTrue('#. Translators: Two-line translator comment #1\n#. continued here.' in po_contents) + self.assertTrue('#. Translators: One-line translator comment #1' in po_contents) + self.assertTrue('#. Translators: Two-line translator comment #1\n#. continued here.' in po_contents) - self.assertTrue('#. Translators: One-line translator comment #2' in po_contents) - self.assertTrue('#. Translators: Two-line translator comment #2\n#. continued here.' in po_contents) + self.assertTrue('#. Translators: One-line translator comment #2' in po_contents) + self.assertTrue('#. Translators: Two-line translator comment #2\n#. continued here.' in po_contents) - self.assertTrue('#. Translators: One-line translator comment #3' in po_contents) - self.assertTrue('#. Translators: Two-line translator comment #3\n#. continued here.' in po_contents) + self.assertTrue('#. Translators: One-line translator comment #3' in po_contents) + self.assertTrue('#. Translators: Two-line translator comment #3\n#. continued here.' in po_contents) - self.assertTrue('#. Translators: One-line translator comment #4' in po_contents) - self.assertTrue('#. Translators: Two-line translator comment #4\n#. continued here.' in po_contents) + self.assertTrue('#. Translators: One-line translator comment #4' in po_contents) + self.assertTrue('#. Translators: Two-line translator comment #4\n#. continued here.' in po_contents) - self.assertTrue('#. Translators: One-line translator comment #5 -- with non ASCII characters: áéíóúö' in po_contents) - self.assertTrue('#. Translators: Two-line translator comment #5 -- with non ASCII characters: áéíóúö\n#. continued here.' in po_contents) + self.assertTrue('#. Translators: One-line translator comment #5 -- with non ASCII characters: áéíóúö' in po_contents) + self.assertTrue('#. Translators: Two-line translator comment #5 -- with non ASCII characters: áéíóúö\n#. continued here.' in po_contents) - def test_templatize(self): + def test_templatize_trans_tag(self): + # ticket #11240 os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents) - self.assertMsgId('I think that 100%% is more that 50%% of %(obj)s.', po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertMsgId('Literal with a percent symbol at the end %%', po_contents) + self.assertMsgId('Literal with a percent %% symbol in the middle', po_contents) + self.assertMsgId('Completed 50%% of all the tasks', po_contents) + self.assertMsgId('Completed 99%% of all the tasks', po_contents) + self.assertMsgId("Shouldn't double escape this sequence: %% (two percent signs)", po_contents) + self.assertMsgId("Shouldn't double escape this sequence %% either", po_contents) + self.assertMsgId("Looks like a str fmt spec %%s but shouldn't be interpreted as such", po_contents) + self.assertMsgId("Looks like a str fmt spec %% o but shouldn't be interpreted as such", po_contents) + + def test_templatize_blocktrans_tag(self): + # ticket #11966 + os.chdir(self.test_dir) + management.call_command('makemessages', locale=LOCALE, verbosity=0) + self.assertTrue(os.path.exists(self.PO_FILE)) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents) + self.assertMsgId('I think that 100%% is more that 50%% of %(obj)s.', po_contents) def test_extraction_error(self): os.chdir(self.test_dir) @@ -103,26 +128,28 @@ class BasicExtractorTests(ExtractorTests): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - # {% trans %} - self.assertTrue('msgctxt "Special trans context #1"' in po_contents) - self.assertTrue("Translatable literal #7a" in po_contents) - self.assertTrue('msgctxt "Special trans context #2"' in po_contents) - self.assertTrue("Translatable literal #7b" in po_contents) - self.assertTrue('msgctxt "Special trans context #3"' in po_contents) - self.assertTrue("Translatable literal #7c" in po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + # {% trans %} + self.assertTrue('msgctxt "Special trans context #1"' in po_contents) + self.assertTrue("Translatable literal #7a" in po_contents) + self.assertTrue('msgctxt "Special trans context #2"' in po_contents) + self.assertTrue("Translatable literal #7b" in po_contents) + self.assertTrue('msgctxt "Special trans context #3"' in po_contents) + self.assertTrue("Translatable literal #7c" in po_contents) - # {% blocktrans %} - self.assertTrue('msgctxt "Special blocktrans context #1"' in po_contents) - self.assertTrue("Translatable literal #8a" in po_contents) - self.assertTrue('msgctxt "Special blocktrans context #2"' in po_contents) - self.assertTrue("Translatable literal #8b-singular" in po_contents) - self.assertTrue("Translatable literal #8b-plural" in po_contents) - self.assertTrue('msgctxt "Special blocktrans context #3"' in po_contents) - self.assertTrue("Translatable literal #8c-singular" in po_contents) - self.assertTrue("Translatable literal #8c-plural" in po_contents) - self.assertTrue('msgctxt "Special blocktrans context #4"' in po_contents) - self.assertTrue("Translatable literal #8d" in po_contents) + # {% blocktrans %} + self.assertTrue('msgctxt "Special blocktrans context #1"' in po_contents) + self.assertTrue("Translatable literal #8a" in po_contents) + self.assertTrue('msgctxt "Special blocktrans context #2"' in po_contents) + self.assertTrue("Translatable literal #8b-singular" in po_contents) + self.assertTrue("Translatable literal #8b-plural" in po_contents) + self.assertTrue('msgctxt "Special blocktrans context #3"' in po_contents) + self.assertTrue("Translatable literal #8c-singular" in po_contents) + self.assertTrue("Translatable literal #8c-plural" in po_contents) + self.assertTrue('msgctxt "Special blocktrans context #4"' in po_contents) + self.assertTrue("Translatable literal #8d" in po_contents) class JavascriptExtractorTests(ExtractorTests): @@ -132,20 +159,22 @@ class JavascriptExtractorTests(ExtractorTests): os.chdir(self.test_dir) management.call_command('makemessages', domain='djangojs', locale=LOCALE, verbosity=0) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertMsgId('This literal should be included.', po_contents) - self.assertMsgId('This one as well.', po_contents) - self.assertMsgId(r'He said, \"hello\".', po_contents) - self.assertMsgId("okkkk", po_contents) - self.assertMsgId("TEXT", po_contents) - self.assertMsgId("It's at http://example.com", po_contents) - self.assertMsgId("String", po_contents) - self.assertMsgId("/* but this one will be too */ 'cause there is no way of telling...", po_contents) - self.assertMsgId("foo", po_contents) - self.assertMsgId("bar", po_contents) - self.assertMsgId("baz", po_contents) - self.assertMsgId("quz", po_contents) - self.assertMsgId("foobar", po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertMsgId('This literal should be included.', po_contents) + self.assertMsgId('This one as well.', po_contents) + self.assertMsgId(r'He said, \"hello\".', po_contents) + self.assertMsgId("okkkk", po_contents) + self.assertMsgId("TEXT", po_contents) + self.assertMsgId("It's at http://example.com", po_contents) + self.assertMsgId("String", po_contents) + self.assertMsgId("/* but this one will be too */ 'cause there is no way of telling...", po_contents) + self.assertMsgId("foo", po_contents) + self.assertMsgId("bar", po_contents) + self.assertMsgId("baz", po_contents) + self.assertMsgId("quz", po_contents) + self.assertMsgId("foobar", po_contents) class IgnoredExtractorTests(ExtractorTests): @@ -154,9 +183,11 @@ class IgnoredExtractorTests(ExtractorTests): pattern1 = os.path.join('ignore_dir', '*') management.call_command('makemessages', locale=LOCALE, verbosity=0, ignore_patterns=[pattern1]) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertMsgId('This literal should be included.', po_contents) - self.assertNotMsgId('This should be ignored.', po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertMsgId('This literal should be included.', po_contents) + self.assertNotMsgId('This should be ignored.', po_contents) class SymlinkExtractorTests(ExtractorTests): @@ -184,9 +215,11 @@ class SymlinkExtractorTests(ExtractorTests): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0, symlinks=True) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertMsgId('This literal should be included.', po_contents) - self.assertTrue('templates_symlinked/test.html' in po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertMsgId('This literal should be included.', po_contents) + self.assertTrue('templates_symlinked/test.html' in po_contents) class CopyPluralFormsExtractorTests(ExtractorTests): @@ -195,8 +228,10 @@ class CopyPluralFormsExtractorTests(ExtractorTests): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertTrue('Plural-Forms: nplurals=2; plural=(n != 1)' in po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertTrue('Plural-Forms: nplurals=2; plural=(n != 1)' in po_contents) class NoWrapExtractorTests(ExtractorTests): @@ -205,15 +240,19 @@ class NoWrapExtractorTests(ExtractorTests): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0, no_wrap=True) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertMsgId('This literal should also be included wrapped or not wrapped depending on the use of the --no-wrap option.', po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertMsgId('This literal should also be included wrapped or not wrapped depending on the use of the --no-wrap option.', po_contents) def test_no_wrap_disabled(self): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0, no_wrap=False) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertMsgId('""\n"This literal should also be included wrapped or not wrapped depending on the "\n"use of the --no-wrap option."', po_contents, use_quotes=False) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertMsgId('""\n"This literal should also be included wrapped or not wrapped depending on the "\n"use of the --no-wrap option."', po_contents, use_quotes=False) class NoLocationExtractorTests(ExtractorTests): @@ -222,12 +261,16 @@ class NoLocationExtractorTests(ExtractorTests): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0, no_location=True) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertFalse('#: templates/test.html:55' in po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertFalse('#: templates/test.html:55' in po_contents) def test_no_location_disabled(self): os.chdir(self.test_dir) management.call_command('makemessages', locale=LOCALE, verbosity=0, no_location=False) self.assertTrue(os.path.exists(self.PO_FILE)) - po_contents = open(self.PO_FILE, 'r').read() - self.assertTrue('#: templates/test.html:55' in po_contents) + #po_contents = open(self.PO_FILE, 'r').read() + with open(self.PO_FILE, 'r') as fp: + po_contents = fp.read() + self.assertTrue('#: templates/test.html:55' in po_contents) diff --git a/tests/regressiontests/i18n/commands/locale/fr/LC_MESSAGES/django.po b/tests/regressiontests/i18n/commands/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000000..26bf6f133c --- /dev/null +++ b/tests/regressiontests/i18n/commands/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-04 04:59-0600\n" +"PO-Revision-Date: 2011-12-10 19:12-0300\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#. Translators: Django template comment for translators +#: templates/test.html:9 +#, python-format +msgid "I think that 100%% is more that 50%% of anything." +msgstr "" + +#: templates/test.html:10 +#, python-format +msgid "I think that 100%% is more that 50%% of %(obj)s." +msgstr "" + +#: templates/test.html:70 +#, python-format +msgid "Literal with a percent symbol at the end %%" +msgstr "" + +#: templates/test.html:71 +#, python-format +msgid "Literal with a percent %% symbol in the middle" +msgstr "" + +#: templates/test.html:72 +#, python-format +msgid "Completed 50%% of all the tasks" +msgstr "" + +#: templates/test.html:73 +#, python-format +msgctxt "ctx0" +msgid "Completed 99%% of all the tasks" +msgstr "" + +#: templates/test.html:74 +#, python-format +msgid "Shouldn't double escape this sequence: %% (two percent signs)" +msgstr "" + +#: templates/test.html:75 +#, python-format +msgctxt "ctx1" +msgid "Shouldn't double escape this sequence %% either" +msgstr "" + +#: templates/test.html:76 +#, python-format +msgid "Looks like a str fmt spec %%s but shouldn't be interpreted as such" +msgstr "Translation of the above string" + +#: templates/test.html:77 +#, python-format +msgid "Looks like a str fmt spec %% o but shouldn't be interpreted as such" +msgstr "Translation contains %% for the above string" diff --git a/tests/regressiontests/i18n/commands/templates/test.html b/tests/regressiontests/i18n/commands/templates/test.html index 24fc708621..6f794af789 100644 --- a/tests/regressiontests/i18n/commands/templates/test.html +++ b/tests/regressiontests/i18n/commands/templates/test.html @@ -65,4 +65,13 @@ continued here.{% endcomment %} {% blocktrans context "Special blocktrans context #1" %}Translatable literal #8a{% endblocktrans %} {% blocktrans count 2 context "Special blocktrans context #2" %}Translatable literal #8b-singular{% plural %}Translatable literal #8b-plural{% endblocktrans %} {% blocktrans context "Special blocktrans context #3" count 2 %}Translatable literal #8c-singular{% plural %}Translatable literal #8c-plural{% endblocktrans %} -{% blocktrans with a=1 context "Special blocktrans context #4" %}Translatable literal #8d {{ a }}{% endblocktrans %} \ No newline at end of file +{% blocktrans with a=1 context "Special blocktrans context #4" %}Translatable literal #8d {{ a }}{% endblocktrans %} + +{% trans "Literal with a percent symbol at the end %" %} +{% trans "Literal with a percent % symbol in the middle" %} +{% trans "Completed 50% of all the tasks" %} +{% trans "Completed 99% of all the tasks" context "ctx0" %} +{% trans "Shouldn't double escape this sequence: %% (two percent signs)" %} +{% trans "Shouldn't double escape this sequence %% either" context "ctx1" %} +{% trans "Looks like a str fmt spec %s but shouldn't be interpreted as such" %} +{% trans "Looks like a str fmt spec % o but shouldn't be interpreted as such" %} diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 33b16886cb..cc17d74315 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -31,7 +31,8 @@ if can_run_extraction_tests: CopyPluralFormsExtractorTests, NoWrapExtractorTests, NoLocationExtractorTests) if can_run_compilation_tests: - from .commands.compilation import MessageCompilationTests, PoFileTests + from .commands.compilation import (PoFileTests, PoFileContentsTests, + PercentRenderingTests) from .contenttypes.tests import ContentTypeTests from .forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm from .models import Company, TestModel