[UPD] run_parallel_test: support all test in asyncio

- Support running test in sandbox to fix conflict test
- Support search_class_module for all template
- Add option to run without parallel
- Separate mariadb test to increase speed
- Separate test in multiple test
- Add pool thread, can set max thread
- Keep cache, async copytree, add ignore pattern instead rm
- Support all os command with asyncio
This commit is contained in:
Mathieu Benoit 2022-02-03 22:44:08 -05:00
parent 5065dc2f2a
commit aeaa6933a8
2 changed files with 686 additions and 79 deletions

View file

@ -357,6 +357,8 @@ test_full:
test_full_fast: test_full_fast:
./script/make.sh clean ./script/make.sh clean
./script/test/run_parallel_test.py ./script/test/run_parallel_test.py
# TODO This test is broken in parallel
./script/make.sh test_code_generator_hello_world
.PHONY: test_base .PHONY: test_base
test_base: test_base:

View file

@ -1,14 +1,18 @@
#!./.venv/bin/python #!./.venv/bin/python
import argparse import argparse
import asyncio import asyncio
import configparser
import datetime import datetime
import logging import logging
import os import os
import sys import sys
import tempfile
import time import time
import uuid import uuid
from collections import deque
from typing import Tuple from typing import Tuple
import aioshutil
from colorama import Fore from colorama import Fore
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
@ -21,7 +25,7 @@ def get_config():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\ description="""\
Run code generator test in parallel. Run code generator test in parallel (asyncio).
""", """,
epilog="""\ epilog="""\
""", """,
@ -31,6 +35,30 @@ def get_config():
action="store_true", action="store_true",
help="Will not stop or init check if contain git change.", help="Will not stop or init check if contain git change.",
) )
parser.add_argument(
"--no_parallel",
action="store_true",
help="Will run in serial.",
)
parser.add_argument(
"--keep_cache",
action="store_true",
help=(
"Will not delete the temporary directory, check in print and log."
),
)
parser.add_argument(
"-p",
"--max_process",
type=int,
default=0,
help="Max processor to use. If 0, use max.",
)
parser.add_argument(
"--debug",
action="store_true",
help="Enable asyncio debugging",
)
args = parser.parse_args() args = parser.parse_args()
return args return args
@ -134,6 +162,27 @@ def print_log(lst_task, tpl_result):
print(f"Log file {LOG_FILE}") print(f"Log file {LOG_FILE}")
async def run_command_get_output(*args, cwd=None):
if cwd is not None:
process = await asyncio.create_subprocess_exec(
*args,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=cwd,
)
else:
process = await asyncio.create_subprocess_exec(
*args,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
# Wait for the subprocess to finish
stdout, stderr = await process.communicate()
return stdout.decode()
async def run_command(*args, test_name=None): async def run_command(*args, test_name=None):
# Create subprocess # Create subprocess
start_time = time.time() start_time = time.time()
@ -176,21 +225,306 @@ async def run_command(*args, test_name=None):
return str_output_init + all_output, process.returncode return str_output_init + all_output, process.returncode
def update_config(
origin_config,
new_path_config,
lst_path_to_add_config=None,
lst_path_to_remove_config=None,
module_name=None,
):
config_parser = configparser.ConfigParser()
config_parser.read(origin_config)
str_path = config_parser["options"]["addons_path"].strip(",")
lst_path = str_path.split(",")
# Clean path from test
if lst_path_to_remove_config:
for remove_key in lst_path_to_remove_config:
if remove_key.startswith("./"):
remove_key = remove_key[2:]
if module_name and remove_key.endswith(module_name):
remove_key = remove_key[: -(len(module_name) + 1)]
for s_path in lst_path:
if s_path.endswith(remove_key):
lst_path.remove(s_path)
break
if lst_path_to_add_config:
for add_path in lst_path_to_add_config:
if module_name and add_path.endswith(module_name):
add_path = add_path[: -(len(module_name) + 1)]
lst_path.insert(0, add_path)
s_new_path = ",".join(lst_path)
config_parser["options"]["addons_path"] = s_new_path
with open(new_path_config, "w") as configfile:
config_parser.write(configfile)
async def test_exec( async def test_exec(
config,
path_module_check: str, path_module_check: str,
generated_module=None, generated_module=None,
generate_path=None,
tested_module=None, tested_module=None,
search_class_module=None, search_class_module=None,
script_after_init_check=None, script_after_init_check=None,
lst_init_module_name=None, lst_init_module_name=None,
test_name=None, test_name=None,
install_path=None, install_path=None,
run_in_sandbox=False,
) -> Tuple[str, int]: ) -> Tuple[str, int]:
test_result = "" test_result = ""
test_status = 0 test_status = 0
new_destination_path = None
if search_class_module:
if install_path is not None:
path_template_to_generate = os.path.join(
install_path, tested_module
)
elif generate_path:
path_template_to_generate = os.path.join(
generate_path, tested_module
)
else:
path_template_to_generate = os.path.join(
path_module_check, tested_module
)
if generate_path is not None:
path_module_to_generate = os.path.join(
generate_path, search_class_module
)
else:
path_module_to_generate = os.path.join(
path_module_check, search_class_module
)
else:
path_template_to_generate = None
path_module_to_generate = None
use_test_path_generic = False
destination_path = None
temp_dir_name = None
if run_in_sandbox:
if config.keep_cache:
temp_dir = tempfile.mkdtemp()
temp_dir_name = temp_dir
else:
temp_dir = tempfile.TemporaryDirectory()
temp_dir_name = temp_dir.name
print(temp_dir_name)
temp_dir_name = os.path.join(temp_dir_name, "workspace")
os.mkdir(temp_dir_name)
lst_path_to_add_config = []
lst_path_to_remove_config = []
if not os.path.exists(path_module_check):
return (
f"Error var path_module_check '{path_module_check}' not"
" exist.",
-1,
)
copy_path_module_check = path_module_check
path_module_check = os.path.normpath(
os.path.join(temp_dir_name, path_module_check)
)
if generated_module and path_module_check.endswith(
"/" + generated_module
):
use_test_path_generic = True
destination_path = path_module_check[
: -(len(generated_module) + 1)
]
copy_path = copy_path_module_check[: -(len(generated_module) + 1)]
elif search_class_module and path_module_check.endswith(
"/" + search_class_module
):
destination_path = path_module_check[
: -(len(search_class_module) + 1)
]
copy_path = copy_path_module_check[
: -(len(search_class_module) + 1)
]
else:
destination_path = path_module_check
copy_path = copy_path_module_check
lst_path_to_add_config.append(destination_path)
lst_path_to_remove_config.append(copy_path)
ignore_tree = await aioshutil.ignore_patterns(".git", "setup")
await aioshutil.copytree(
copy_path, destination_path, ignore=ignore_tree
)
# destination_path_with_git = os.path.join(destination_path, ".git")
# if os.path.exists(destination_path_with_git):
# await aioshutil.rmtree(destination_path_with_git)
# destination_path_with_setup = os.path.join(destination_path, "setup")
# if os.path.exists(destination_path_with_setup):
# await aioshutil.rmtree(destination_path_with_setup)
if tested_module:
lst_module_to_test = tested_module.split(",")
for module_name in lst_module_to_test:
# Update path to change new emplacement
s_lst_path_tested_module = await run_command_get_output(
"find", ".", "-name", module_name
)
if not s_lst_path_tested_module:
return (
f"Error cannot find module '{path_module_check}' not"
" exist.",
-1,
)
else:
lst_path_tested_module = (
s_lst_path_tested_module.strip().split("\n")
)
s_first_path = lst_path_tested_module[0]
parent_dir = os.path.dirname(s_first_path)
# Copy it
if copy_path != parent_dir:
os.path.basename(parent_dir)
new_destination_path = os.path.join(
os.path.dirname(destination_path),
os.path.basename(parent_dir),
)
ignore_tree = await aioshutil.ignore_patterns(
".git", "setup"
)
await aioshutil.copytree(
parent_dir,
new_destination_path,
ignore=ignore_tree,
)
# destination_path_with_git = os.path.join(
# new_destination_path, ".git"
# )
# if os.path.exists(destination_path_with_git):
# await aioshutil.rmtree(destination_path_with_git)
lst_path_to_add_config.append(new_destination_path)
lst_path_to_remove_config.append(parent_dir)
new_s_first_path = os.path.normpath(
os.path.join(temp_dir_name, s_first_path)
)
s_lst_path_generated_module = await run_command_get_output(
"find",
".",
"-name",
generated_module,
cwd=temp_dir_name,
)
if s_lst_path_generated_module:
lst_path_generated_module = (
s_lst_path_generated_module.strip().split("\n")
)
s_first_path = os.path.normpath(
os.path.join(
temp_dir_name,
os.path.dirname(lst_path_generated_module[0]),
)
)
else:
# TODO This is wrong... bug in template if reach this case
s_first_path = destination_path
hook_file = os.path.join(new_s_first_path, "hooks.py")
with open(hook_file) as hook:
hook_line = hook.read()
has_template = (
"template_dir = os.path.normpath" in hook_line
)
# Goal, update path_module_generate, maybe commented
# Goal, update template_dir, maybe not exist
# TODO need to refactor this and use AST and not string research
# try find nb space indentation
lst_f_key = [
"# path_module_generate = ",
"#path_module_generate = ",
"path_module_generate = ",
]
nb_space_indentation = None
first_index = None
for f_key in lst_f_key:
if f_key in hook_line:
first_index = hook_line.find(f_key)
f_begin_index = (
hook_line.rfind("\n", 0, first_index) + 1
)
nb_space_indentation = (
first_index - f_begin_index
)
break
if nb_space_indentation is None:
return (
f"Cannot find keys '{lst_f_key}' in"
f" {hook_file}",
-1,
)
end_string = "\n\n"
index_end_string = hook_line.find(
end_string, first_index
)
if has_template:
new_hook_line = (
hook_line[: first_index + len(f_key)]
+ f'"{s_first_path}"\n'
+ f'{nb_space_indentation * " "}template_dir ='
f' "{s_first_path}/" + MODULE_NAME\n\n'
+ hook_line[index_end_string:]
)
else:
new_hook_line = (
hook_line[: first_index + len(f_key)]
+ f'"{s_first_path}"\n'
+ hook_line[index_end_string:]
)
new_hook_line = new_hook_line.replace(
"# path_module_generate = ",
"path_module_generate = ",
)
new_hook_line = new_hook_line.replace(
'# "path_sync_code": path_module_generate,',
'"path_sync_code": path_module_generate,',
)
with open(hook_file, "w") as hook:
hook.write(new_hook_line)
# Format editing code before commit
await run_command_get_output(
"./script/maintenance/black.sh", temp_dir_name
)
# init repo with git
for dir_to_git in lst_path_to_add_config:
await run_command_get_output("git", "init", ".", cwd=dir_to_git)
await run_command_get_output("git", "add", ".", cwd=dir_to_git)
await run_command_get_output(
"git", "commit", "-am", "'first commit'", cwd=dir_to_git
)
new_config_path = os.path.join(temp_dir_name, "config.conf")
update_config(
"./config.conf",
new_config_path,
lst_path_to_add_config=lst_path_to_add_config,
lst_path_to_remove_config=lst_path_to_remove_config,
module_name=generated_module,
)
else:
new_config_path = None
if install_path is None: if install_path is None:
install_path = path_module_check install_path = path_module_check
elif run_in_sandbox:
install_path = os.path.normpath(
os.path.join(temp_dir_name, install_path)
)
# Check code, init module to install # Check code, init module to install
if lst_init_module_name: if lst_init_module_name:
@ -228,6 +562,7 @@ async def test_exec(
is_db_create = False is_db_create = False
unique_database_name = f"test_demo_{uuid.uuid4()}"[:63] unique_database_name = f"test_demo_{uuid.uuid4()}"[:63]
if not test_status: if not test_status:
# Create database
res, status = await run_command( res, status = await run_command(
"./script/db_restore.py", "./script/db_restore.py",
"--database", "--database",
@ -239,15 +574,22 @@ async def test_exec(
is_db_create = not status is_db_create = not status
if not test_status and lst_init_module_name: if not test_status and lst_init_module_name:
# Parallel execution here # Install required module
# No parallel execution here
str_test = ",".join(lst_init_module_name) str_test = ",".join(lst_init_module_name)
script_name = ( script_name = (
"./script/addons/install_addons_dev.sh" "./script/addons/install_addons_dev.sh"
if tested_module if tested_module
else "./script/addons/install_addons.sh" else "./script/addons/install_addons.sh"
) )
if new_config_path:
res, status = await run_command(
script_name,
unique_database_name,
str_test,
new_config_path,
test_name=test_name,
)
else:
res, status = await run_command( res, status = await run_command(
script_name, script_name,
unique_database_name, unique_database_name,
@ -258,15 +600,7 @@ async def test_exec(
test_status += status test_status += status
if not test_status and search_class_module and generated_module: if not test_status and search_class_module and generated_module:
path_template_to_generate = os.path.join( # Update template with class/model/inherit
path_module_check, tested_module
)
path_module_to_generate = os.path.join(
path_module_check, search_class_module
)
# Parallel execution here
# No parallel execution here
res, status = await run_command( res, status = await run_command(
"./script/code_generator/search_class_model.py", "./script/code_generator/search_class_model.py",
"--quiet", "--quiet",
@ -279,10 +613,28 @@ async def test_exec(
test_result += res test_result += res
test_status += status test_status += status
if not test_status and tested_module and generated_module: test_generated_path = (
# Parallel execution here install_path if destination_path is None else new_destination_path
)
test_generated_path = (
destination_path if use_test_path_generic else install_path
)
# if destination_path is None:
# destination_path = install_path
# No parallel execution here if not test_status and tested_module and generated_module:
# Finally, the test
if new_config_path:
res, status = await run_command(
"./script/code_generator/install_and_test_code_generator.sh",
unique_database_name,
tested_module,
test_generated_path,
generated_module,
new_config_path,
test_name=test_name,
)
else:
res, status = await run_command( res, status = await run_command(
"./script/code_generator/install_and_test_code_generator.sh", "./script/code_generator/install_and_test_code_generator.sh",
unique_database_name, unique_database_name,
@ -337,7 +689,7 @@ def print_summary_task(task_list):
# START TEST # START TEST
async def run_demo_test() -> Tuple[str, int]: async def run_demo_test(config) -> Tuple[str, int]:
lst_test_name = [ lst_test_name = [
"demo_helpdesk_data", "demo_helpdesk_data",
"demo_internal", "demo_internal",
@ -349,6 +701,7 @@ async def run_demo_test() -> Tuple[str, int]:
"demo_website_snippet", "demo_website_snippet",
] ]
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
lst_init_module_name=lst_test_name, lst_init_module_name=lst_test_name,
test_name="demo_test", test_name="demo_test",
@ -357,11 +710,12 @@ async def run_demo_test() -> Tuple[str, int]:
return res, status return res, status
async def run_mariadb_test() -> Tuple[str, int]: async def run_code_generator_migrator_demo_mariadb_sql_example_1_test(
test_result = "" config,
test_status = 0 ) -> Tuple[str, int]:
# Migrator # Migrator
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
generated_module="demo_mariadb_sql_example_1", generated_module="demo_mariadb_sql_example_1",
tested_module="code_generator_migrator_demo_mariadb_sql_example_1", tested_module="code_generator_migrator_demo_mariadb_sql_example_1",
@ -372,12 +726,18 @@ async def run_mariadb_test() -> Tuple[str, int]:
"code_generator_portal", "code_generator_portal",
], ],
test_name="mariadb_test-migrator", test_name="mariadb_test-migrator",
run_in_sandbox=True,
) )
test_result += res
test_status += status
return res, status
async def run_code_generator_template_demo_mariadb_sql_example_1_test(
config,
) -> Tuple[str, int]:
# Template # Template
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_mariadb_sql_example_1", generated_module="code_generator_demo_mariadb_sql_example_1",
tested_module="code_generator_template_demo_mariadb_sql_example_1", tested_module="code_generator_template_demo_mariadb_sql_example_1",
@ -387,12 +747,18 @@ async def run_mariadb_test() -> Tuple[str, int]:
"demo_mariadb_sql_example_1", "demo_mariadb_sql_example_1",
], ],
test_name="mariadb_test-template", test_name="mariadb_test-template",
run_in_sandbox=True,
) )
test_result += res
test_status += status
return res, status
async def run_code_generator_demo_mariadb_sql_example_1_test(
config,
) -> Tuple[str, int]:
# Code generator # Code generator
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
generated_module="demo_mariadb_sql_example_1", generated_module="demo_mariadb_sql_example_1",
lst_init_module_name=[ lst_init_module_name=[
@ -400,6 +766,31 @@ async def run_mariadb_test() -> Tuple[str, int]:
], ],
tested_module="code_generator_demo_mariadb_sql_example_1", tested_module="code_generator_demo_mariadb_sql_example_1",
test_name="mariadb_test-code-generator", test_name="mariadb_test-code-generator",
run_in_sandbox=True,
)
return res, status
async def run_code_generator_data_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_helpdesk_data",
"demo_website_data",
]
lst_tested_module = [
"code_generator_demo_export_helpdesk",
"code_generator_demo_export_website",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_data_test",
run_in_sandbox=True,
) )
test_result += res test_result += res
test_status += status test_status += status
@ -407,35 +798,85 @@ async def run_mariadb_test() -> Tuple[str, int]:
return test_result, test_status return test_result, test_status
async def run_code_generator_multiple_test() -> Tuple[str, int]: async def run_code_generator_theme_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"theme_website_demo_code_generator",
]
lst_tested_module = [
"code_generator_demo_theme_website",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_theme_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_generic_all_test(config) -> Tuple[str, int]:
test_result = "" test_result = ""
test_status = 0 test_status = 0
lst_generated_module = [ lst_generated_module = [
"code_generator_demo",
"demo_helpdesk_data",
"demo_website_data",
"demo_internal", "demo_internal",
"demo_portal", "demo_portal",
"demo_helpdesk_data",
"demo_website_data",
"demo_website_leaflet",
"demo_website_snippet",
"theme_website_demo_code_generator", "theme_website_demo_code_generator",
]
lst_tested_module = [
"code_generator_demo_internal",
"code_generator_demo_portal",
"code_generator_demo_export_helpdesk",
"code_generator_demo_export_website",
"code_generator_demo_website_leaflet",
"code_generator_demo_website_snippet",
"code_generator_demo_theme_website",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_generic_all_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_website_snippet_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_website_leaflet", "demo_website_leaflet",
"demo_website_snippet", "demo_website_snippet",
] ]
lst_tested_module = [ lst_tested_module = [
"code_generator_demo",
"code_generator_demo_export_helpdesk",
"code_generator_demo_export_website",
"code_generator_demo_internal",
"code_generator_demo_portal",
"code_generator_demo_theme_website",
"code_generator_demo_website_leaflet", "code_generator_demo_website_leaflet",
"code_generator_demo_website_snippet", "code_generator_demo_website_snippet",
] ]
# Multiple # Multiple
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module), generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module), tested_module=",".join(lst_tested_module),
test_name="code_generator_multiple_test", test_name="code_generator_website_snippet_test",
run_in_sandbox=True,
) )
test_result += res test_result += res
test_status += status test_status += status
@ -443,7 +884,56 @@ async def run_code_generator_multiple_test() -> Tuple[str, int]:
return test_result, test_status return test_result, test_status
async def run_code_generator_inherit_test() -> Tuple[str, int]: async def run_code_generator_demo_generic_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_internal",
"demo_portal",
]
lst_tested_module = [
"code_generator_demo_internal",
"code_generator_demo_portal",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_demo_generic_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_demo_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"code_generator_demo",
]
lst_tested_module = [
"code_generator_demo",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_demo_test",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_inherit_test(config) -> Tuple[str, int]:
# TODO can be merge into code_generator_multiple # TODO can be merge into code_generator_multiple
test_result = "" test_result = ""
test_status = 0 test_status = 0
@ -455,10 +945,12 @@ async def run_code_generator_inherit_test() -> Tuple[str, int]:
] ]
# Inherit # Inherit
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module), generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module), tested_module=",".join(lst_tested_module),
test_name="code_generator_inherit_test", test_name="code_generator_inherit_test",
run_in_sandbox=True,
) )
test_result += res test_result += res
test_status += status test_status += status
@ -466,7 +958,7 @@ async def run_code_generator_inherit_test() -> Tuple[str, int]:
return test_result, test_status return test_result, test_status
async def run_code_generator_auto_backup_test() -> Tuple[str, int]: async def run_code_generator_auto_backup_test(config) -> Tuple[str, int]:
test_result = "" test_result = ""
test_status = 0 test_status = 0
lst_generated_module = [ lst_generated_module = [
@ -477,10 +969,12 @@ async def run_code_generator_auto_backup_test() -> Tuple[str, int]:
] ]
# Auto-backup # Auto-backup
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/OCA_server-tools/auto_backup", "./addons/OCA_server-tools/auto_backup",
generated_module=",".join(lst_generated_module), generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module), tested_module=",".join(lst_tested_module),
test_name="code_generator_auto_backup_test", test_name="code_generator_auto_backup_test",
run_in_sandbox=True,
) )
test_result += res test_result += res
test_status += status test_status += status
@ -488,19 +982,23 @@ async def run_code_generator_auto_backup_test() -> Tuple[str, int]:
return test_result, test_status return test_result, test_status
async def run_code_generator_template_demo_portal_test() -> Tuple[str, int]: async def run_code_generator_template_demo_portal_test(
config,
) -> Tuple[str, int]:
test_result = "" test_result = ""
test_status = 0 test_status = 0
# Template # Template
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_portal", generated_module="code_generator_demo_portal",
tested_module="code_generator_template_demo_portal", tested_module="code_generator_template_demo_portal",
# search_class_module="demo_mariadb_sql_example_1", search_class_module="demo_portal",
lst_init_module_name=[ lst_init_module_name=[
"demo_portal", "demo_portal",
], ],
test_name="code_generator_template_demo_portal", test_name="code_generator_template_demo_portal",
run_in_sandbox=True,
) )
test_result += res test_result += res
test_status += status test_status += status
@ -508,21 +1006,23 @@ async def run_code_generator_template_demo_portal_test() -> Tuple[str, int]:
return test_result, test_status return test_result, test_status
async def run_code_generator_template_demo_internal_inherit_test() -> Tuple[ async def run_code_generator_template_demo_internal_inherit_test(
str, int config,
]: ) -> Tuple[str, int]:
test_result = "" test_result = ""
test_status = 0 test_status = 0
# Template # Template
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template", "./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_internal_inherit", generated_module="code_generator_demo_internal_inherit",
tested_module="code_generator_template_demo_internal_inherit", tested_module="code_generator_template_demo_internal_inherit",
# search_class_module="code_generator_demo_internal_inherit", search_class_module="demo_internal_inherit",
lst_init_module_name=[ lst_init_module_name=[
"demo_internal_inherit", "demo_internal_inherit",
], ],
test_name="code_generator_template_demo_internal_inherit", test_name="code_generator_template_demo_internal_inherit",
run_in_sandbox=True,
) )
test_result += res test_result += res
test_status += status test_status += status
@ -530,22 +1030,25 @@ async def run_code_generator_template_demo_internal_inherit_test() -> Tuple[
return test_result, test_status return test_result, test_status
async def run_code_generator_template_demo_sysadmin_cron_test() -> Tuple[ async def run_code_generator_template_demo_sysadmin_cron_test(
str, int config,
]: ) -> Tuple[str, int]:
test_result = "" test_result = ""
test_status = 0 test_status = 0
# Template # Template
res, status = await test_exec( res, status = await test_exec(
config,
"./addons/OCA_server-tools/auto_backup", "./addons/OCA_server-tools/auto_backup",
generated_module="code_generator_auto_backup", generated_module="code_generator_auto_backup",
generate_path="./addons/OCA_server-tools/",
tested_module="code_generator_template_demo_sysadmin_cron", tested_module="code_generator_template_demo_sysadmin_cron",
# search_class_module="code_generator_demo_internal_inherit", search_class_module="auto_backup",
lst_init_module_name=[ lst_init_module_name=[
"auto_backup", "auto_backup",
], ],
test_name="code_generator_template_demo_sysadmin_cron", test_name="code_generator_template_demo_sysadmin_cron",
install_path="./addons/TechnoLibre_odoo-code-generator-template", install_path="./addons/TechnoLibre_odoo-code-generator-template",
run_in_sandbox=True,
) )
test_result += res test_result += res
test_status += status test_status += status
@ -553,7 +1056,7 @@ async def run_code_generator_template_demo_sysadmin_cron_test() -> Tuple[
return test_result, test_status return test_result, test_status
async def run_helloworld_test() -> Tuple[str, int]: async def run_helloworld_test(config) -> Tuple[str, int]:
res, status = await run_command( res, status = await run_command(
"./test/code_generator/hello_world.sh", test_name="helloworld_test" "./test/code_generator/hello_world.sh", test_name="helloworld_test"
) )
@ -561,32 +1064,134 @@ async def run_helloworld_test() -> Tuple[str, int]:
return res, status return res, status
def run_all_test() -> None: async def run_in_serial(task_list):
task_list = [] q = asyncio.Queue()
for task in task_list:
await q.put(task)
lst_result = []
for i in range(len(task_list)):
co = await q.get()
result = await co
lst_result.append(result)
return lst_result
task_list.append(run_demo_test())
task_list.append(run_helloworld_test()) class AsyncioPool:
task_list.append(run_mariadb_test()) # TODO check to replace this pool by https://github.com/dano/aioprocessing
task_list.append(run_code_generator_multiple_test()) def __init__(self, concurrency, loop=None):
task_list.append(run_code_generator_inherit_test()) """
task_list.append(run_code_generator_auto_backup_test()) @param loop: asyncio loop
task_list.append(run_code_generator_template_demo_portal_test()) @param concurrency: Maximum number of concurrently running tasks
task_list.append(run_code_generator_template_demo_internal_inherit_test()) """
task_list.append(run_code_generator_template_demo_sysadmin_cron_test()) self._loop = loop or asyncio.get_event_loop()
self._concurrency = concurrency
self._coros = deque([]) # All coroutines queued for execution
self._futures = [] # All currently running coroutines
self._lst_result = []
def close(self):
self._loop.close()
def add_coro(self, coro):
"""
@param coro: coroutine to add
"""
self._coros.append(coro)
self.print_status()
def run_until_complete(self):
self._loop.run_until_complete(self._wait_for_futures())
return self._lst_result
def print_status(self):
print(
" Status: coros:%s - futures:%s"
% (len(self._coros), len(self._futures))
)
def _start_futures(self):
num_to_start = self._concurrency - len(self._futures)
num_to_start = min(num_to_start, len(self._coros))
for _ in range(num_to_start):
coro = self._coros.popleft()
future = asyncio.ensure_future(coro, loop=self._loop)
self._futures.append(future)
self.print_status()
async def _wait_for_futures(self):
while len(self._coros) > 0 or len(self._futures) > 0:
self._start_futures()
futures_completed, futures_pending = await asyncio.wait(
self._futures,
loop=self._loop,
return_when=asyncio.FIRST_COMPLETED,
)
for future in futures_completed:
self._lst_result.append(future.result())
self._futures.remove(future)
self._start_futures()
def run_all_test(config) -> None:
# low in time, at the end for more speed
task_list = [
run_code_generator_migrator_demo_mariadb_sql_example_1_test(config),
run_code_generator_template_demo_mariadb_sql_example_1_test(config),
run_code_generator_demo_mariadb_sql_example_1_test(config),
run_code_generator_auto_backup_test(config),
run_code_generator_template_demo_portal_test(config),
run_code_generator_template_demo_internal_inherit_test(config),
run_code_generator_template_demo_sysadmin_cron_test(config),
run_code_generator_demo_test(config),
run_code_generator_generic_all_test(config),
# save 30 seconds with generic_all and next commented
# run_code_generator_data_test(config),
# run_code_generator_theme_test(config),
# run_code_generator_website_snippet_test(config),
# run_code_generator_demo_generic_test(config),
# TODO Will cause conflict with the other because write in code_generator_demo/hooks.py
# run_helloworld_test(config),
run_code_generator_inherit_test(config),
run_demo_test(config),
]
print_summary_task(task_list) print_summary_task(task_list)
if asyncio.get_event_loop().is_closed(): if asyncio.get_event_loop().is_closed():
asyncio.set_event_loop(asyncio.new_event_loop()) asyncio.set_event_loop(asyncio.new_event_loop())
if config.no_parallel:
tpl_result = asyncio.run(run_in_serial(task_list))
elif config.max_process:
pool = AsyncioPool(config.max_process)
for task in task_list:
pool.add_coro(task)
try:
tpl_result = pool.run_until_complete()
finally:
pool.close()
else:
# Use maximal resource
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
if config.debug:
loop.set_debug(True)
try:
commands = asyncio.gather(*task_list) commands = asyncio.gather(*task_list)
tpl_result = loop.run_until_complete(commands) tpl_result = loop.run_until_complete(commands)
finally:
loop.close() loop.close()
print_log(task_list, tpl_result) print_log(task_list, tpl_result)
check_result(task_list, tpl_result) check_result(task_list, tpl_result)
def main(): def main():
# TODO configure logger with thread
# logging.basicConfig(
# level=logging.INFO,
# format='%(threadName)10s %(name)18s: %(message)s',
# stream=sys.stderr,
# )
config = get_config() config = get_config()
start_time = time.time() start_time = time.time()
if not config.ignore_init_check_git: if not config.ignore_init_check_git:
@ -594,7 +1199,7 @@ def main():
else: else:
success = True success = True
if success: if success:
run_all_test() run_all_test(config)
end_time = time.time() end_time = time.time()
diff_sec = end_time - start_time diff_sec = end_time - start_time
# print(f"Time execution {diff_sec:.3f}s") # print(f"Time execution {diff_sec:.3f}s")