Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions odoo/addons/base/tests/config/cli
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,7 @@
--db_app_name=myapp{pid}

--load-language fr_FR
--language fr_FR
--i18n-export /tmp/translate_out.csv
--i18n-import /tmp/translate_in.csv
--i18n-overwrite
--modules stock,hr,mail

--no-database-list

--dev xml,reload
Expand Down
6 changes: 1 addition & 5 deletions odoo/addons/base/tests/config/non_default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@ db_replica_port = 2038

# i18n
load_language = fr_FR
language = fr_FR
translate_out = /tmp/translate_out.csv
translate_in = /tmp/translate_in.csv
overwrite_existing_translations = True
translate_modules = stock,hr,mail
overwrite_existing_translations = False

# security
list_db = False
Expand Down
62 changes: 58 additions & 4 deletions odoo/addons/base/tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import io
import os
import re
import sys
import subprocess as sp
import sys
import textwrap
import time
import unittest
from pathlib import Path

import odoo.addons
from odoo.cli.command import commands, load_addons_commands, load_internal_commands
from odoo.tests import BaseCase, Like, TransactionCase
from odoo.tools import config, file_path
from odoo.tests import BaseCase, Like


class TestCommand(BaseCase):
Expand Down Expand Up @@ -52,7 +54,7 @@ def test_unknown_command(self):
with self.subTest(name):
command_output = self.run_command(name, check=False).stderr.strip()
self.assertEqual(
command_output,
command_output,
f"Unknown command '{name}'.\n"
"Use 'odoo-bin --help' to see the list of available commands."
)
Expand Down Expand Up @@ -134,3 +136,55 @@ def test_shell(self):
">>> Hello from Python!",
'>>> '
])


class TestCommandUsingDb(TestCommand, TransactionCase):

@unittest.skipIf(
os.name != 'posix' and sys.version_info < (3, 12),
"os.set_blocking on files only available in windows starting 3.12",
)
def test_i18n_export(self):
# i18n export is a process that takes a long time to run, we are
# not interrested in running it in full, we are only interrested
# in making sure it starts correctly.
#
# This test only asserts the first few lines and then SIGTERM
# the process. We took the challenge to write a cross-platform
# test, the lack of a select-like API for Windows makes the code
# a bit complicated. Sorry :/

expected_text = textwrap.dedent("""\
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# \t* base
""").encode()

proc = self.popen_command(
'i18n', 'export', '-d', self.env.cr.dbname, '-o', '-', 'base',
# ensure we get a io.FileIO and not a buffered or text shit
text=False, bufsize=0,
)

# Feed the buffer for maximum 5 seconds.
buffer = io.BytesIO()
timeout = time.monotonic() + 5
os.set_blocking(proc.stdout.fileno(), False)
while buffer.tell() < len(expected_text) and time.monotonic() < timeout:
if chunk := proc.stdout.read(len(expected_text) - buffer.tell()):
buffer.write(chunk)
else:
# would had loved to use select() for its timeout, but
# select doesn't work on files on windows, use a flat
# sleep instead: not great, not terrible.
time.sleep(.1)

self.assertEqual(buffer.getvalue(), expected_text,
"The subprocess did not write the prelude in under 5 seconds.")

proc.terminate()
try:
proc.wait(timeout=5)
except sp.TimeoutExpired:
proc.kill()
raise
89 changes: 28 additions & 61 deletions odoo/addons/base/tests/test_configmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

import odoo
from odoo.tests import TransactionCase
from odoo.tools import file_path, file_open, file_open_temporary_directory
from odoo.tools import file_open, file_open_temporary_directory, file_path
from odoo.tools.config import configmanager


EMPTY_CONFIG_PATH = file_path('base/tests/config/empty.conf')
PROJECT_PATH = odoo.tools.config.root_path.removesuffix('/odoo')
DEFAULT_DATADIR = odoo.tools.config._default_options['data_dir']
Expand Down Expand Up @@ -139,12 +138,7 @@ def test_01_default_config(self):

# i18n
'load_language': None,
'language': None,
'translate_out': '',
'translate_in': '',
'overwrite_existing_translations': False,
'translate_modules': ['all'],

# security
'list_db': True,

Expand Down Expand Up @@ -256,11 +250,7 @@ def test_02_config_file(self):

# i18n
'load_language': 'fr_FR', # blacklist for save, read from the config file
'language': 'fr_FR', # blacklist for save, read from the config file
'translate_out': '/tmp/translate_out.csv', # blacklist for save, read from the config file
'translate_in': '/tmp/translate_in.csv', # blacklist for save, read from the config file
'overwrite_existing_translations': False,
'translate_modules': ['all'], # ignored from the config file
'overwrite_existing_translations': False, # blacklist for save, read from the config file

# security
'list_db': False,
Expand Down Expand Up @@ -366,7 +356,7 @@ def test_04_odoo16_config_file(self):
'test_file': '',
'test_tags': None,
'transient_age_limit': 1.0,
'translate_modules': ['all'],
'translate_modules': "['all']",
'unaccent': False,
'update': {},
'upgrade_path': [],
Expand All @@ -379,12 +369,9 @@ def test_04_odoo16_config_file(self):
'dev_mode': [],
'geoip_database': '/usr/share/GeoIP/GeoLite2-City.mmdb',
'init': {},
'language': None,
'publisher_warranty_url': 'http://services.odoo.com/publisher-warranty/',
'save': False,
'stop_after_init': False,
'translate_in': '',
'translate_out': '',

# undocummented options
'bin_path': '',
Expand Down Expand Up @@ -419,23 +406,32 @@ def test_04_odoo16_config_file(self):
'x_sendfile': False,
})

output = [
(f"WARNING:odoo.tools.config:unknown option '{option}' in "
f"the config file at {config_path}, option stored as-is, "
"without parsing")
for option in ('demo', 'geoip_database', 'osv_memory_age_limit')
] + [
(f"WARNING:odoo.tools.config:option {option} reads 'False' "
f"in the config file at {config_path} but isn't a boolean "
"option, skip")
for option in (
'db_host', 'db_name', 'db_password', 'db_port', 'db_user',
'email_from', 'from_filter', 'log_db', 'smtp_password',
'smtp_ssl_certificate_filename',
'smtp_ssl_private_key_filename', 'smtp_user'
def missing(*options):
return [
f"WARNING:odoo.tools.config:unknown option '{option}' in "
f"the config file at {config_path}, option stored as-is, "
"without parsing"
for option in options
]

def falsy(*options):
return [
f"WARNING:odoo.tools.config:option {option} reads 'False' "
f"in the config file at {config_path} but isn't a boolean "
"option, skip"
for option in options
]

self.assertEqual(capture.output,
missing('demo', 'geoip_database', 'osv_memory_age_limit')
+ falsy(
'db_host', 'db_name', 'db_password', 'db_port',
'db_user', 'email_from', 'from_filter', 'log_db',
'smtp_password', 'smtp_ssl_certificate_filename',
'smtp_ssl_private_key_filename', 'smtp_user',
)
]
self.assertEqual(capture.output, output)
+ missing('translate_modules'),
)

def test_05_repeat_parse_config(self):
"""Emulate multiple calls to parse_config()"""
Expand Down Expand Up @@ -539,12 +535,7 @@ def test_06_cli(self):

# i18n
'load_language': 'fr_FR',
'language': 'fr_FR',
'translate_out': '/tmp/translate_out.csv',
'translate_in': '/tmp/translate_in.csv',
'overwrite_existing_translations': True,
'translate_modules': ['hr', 'mail', 'stock'],

# security
'list_db': False,

Expand Down Expand Up @@ -664,11 +655,7 @@ def test_07_environ(self):

# i18n (not loaded)
'load_language': None,
'language': None,
'translate_out': '',
'translate_in': '',
'overwrite_existing_translations': False,
'translate_modules': ['all'],

# security
'list_db': False,
Expand Down Expand Up @@ -701,26 +688,6 @@ def test_06_syslog_logfile_exclusive_cli(self, error):
self.parse_reset(['-c', file_path('base/tests/config/sysloglogfile.conf')])
error.assert_has_calls(2 * [call("the syslog and logfile options are exclusive")])

@patch('optparse.OptionParser.error')
def test_07_translate_in_requires_language_and_db_name(self, error):
self.parse_reset(['--i18n-import', '/path/to/file.csv'])
self.parse_reset(['--i18n-import', '/path/to/file.csv', '-d', 'dbname'])
self.parse_reset(['--i18n-import', '/path/to/file.csv', '-l', 'fr_FR'])
error.assert_has_calls(3 * [call("the i18n-import option cannot be used without the language (-l) and the database (-d) options")])

@patch('optparse.OptionParser.error')
def test_08_overwrite_existing_translations_requires_translate_in_or_update(self, error):
self.parse_reset(['--i18n-overwrite'])
error.assert_has_calls(1 * [call("the i18n-overwrite option cannot be used without the i18n-import option or without the update option")])
error.reset_mock()
self.parse_reset(['--i18n-overwrite', '-u', 'base'])
error.assert_not_called()

@patch('optparse.OptionParser.error')
def test_09_translate_out_requires_db_name(self, error):
self.parse_reset(['--i18n-export', '/path/to/file.csv'])
error.assert_has_calls(1 * [call("the i18n-export option cannot be used without the database (-d) option")])

@patch('optparse.OptionParser.error')
def test_10_init_update_incompatible_with_multidb(self, error):
self.parse_reset(['-d', 'db1,db2', '-i', 'base'])
Expand Down
4 changes: 2 additions & 2 deletions odoo/addons/base/wizard/base_export_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def act_getfile(self):
with io.BytesIO() as buf:
if this.export_type == 'model':
ids = self.env[this.model_name].search(ast.literal_eval(this.domain)).ids
trans_export_records(lang, this.model_name, ids, buf, this.format, self._cr)
trans_export_records(lang, this.model_name, ids, buf, this.format, self.env)
else:
mods = sorted(this.mapped('modules.name')) or ['all']
trans_export(lang, mods, buf, this.format, self._cr)
trans_export(lang, mods, buf, this.format, self.env)
out = base64.encodebytes(buf.getvalue())

filename = 'new'
Expand Down
Loading