sale_stock/tests/test_create_perf.py

194 lines
7.0 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from decorator import decorator
import logging
import random
import time
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
from odoo.fields import Command
from odoo.tests import tagged
from odoo.tests.common import users, warmup
_logger = logging.getLogger(__name__)
@decorator
def prepare(func, self):
"""Prepare data to remove common querries from the count.
Must be run after `warmup` because of the invalidations"""
# prefetch the data linked to the company and its country code to avoid changing
# the query count during l10n tests
self.env.company.country_id.code
func(self)
@tagged('so_batch_perf')
class TestPERF(TransactionCaseWithUserDemo):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.ENTITIES = 50
cls.products = cls.env['product.product'].create([{
'name': 'Product %s' % i,
'list_price': 1 + 10 * i,
'type': 'service',
} for i in range(10)])
cls.partners = cls.env['res.partner'].create([{
'name': 'Partner %s' % i,
} for i in range(cls.ENTITIES)])
cls.salesmans = cls.env.ref('base.user_admin') | cls.user_demo
cls.env.flush_all()
@users('admin')
@warmup
@prepare
def test_empty_sale_order_creation_perf(self):
with self.assertQueryCount(admin=33):
self.env['sale.order'].create({
'partner_id': self.partners[0].id,
'user_id': self.salesmans[0].id,
})
@users('admin')
@warmup
@prepare
def test_empty_sales_orders_batch_creation_perf(self):
# + 1 SO insert
# + 1 SO sequence fetch
# + 1 warehouse fetch
# + 1 query to get analytic default account
# + 1 followers queries ?
with self.assertQueryCount(admin=37):
self.env['sale.order'].create([{
'partner_id': self.partners[0].id,
'user_id': self.salesmans[0].id,
} for i in range(2)])
@users('admin')
@warmup
@prepare
def test_dummy_sales_orders_batch_creation_perf(self):
""" Dummy SOlines (notes/sections) should not add any custom queries other than their insert"""
# + 2 SOL (batched) insert
with self.assertQueryCount(admin=40):
self.env['sale.order'].create([{
'partner_id': self.partners[0].id,
'user_id': self.salesmans[0].id,
"order_line": [
(0, 0, {"display_type": "line_note", "name": "NOTE"}),
(0, 0, {"display_type": "line_section", "name": "SECTION"})
]
} for i in range(2)])
@users('admin')
@warmup
@prepare
def test_light_sales_orders_batch_creation_perf_without_taxes(self):
self.env['res.country'].search([]).mapped('code')
self.products[0].taxes_id = [Command.set([])]
# + 2 SQL insert
# + 2 queries to get analytic default tags
# + 9 follower queries ?
with self.assertQueryCount(admin=49): # com 46
self.env['sale.order'].create([{
'partner_id': self.partners[0].id,
'user_id': self.salesmans[0].id,
"order_line": [
(0, 0, {"display_type": "line_note", "name": "NOTE"}),
(0, 0, {"display_type": "line_section", "name": "SECTION"}),
(0, 0, {'product_id': self.products[0].id})
]
} for i in range(2)])
# Following tests are not deterministic
# And are privatized on purpose
# until the problem is found
@users('admin')
@warmup
def __test_light_sales_orders_batch_creation_perf(self):
with self.assertQueryCount(admin=70): # 69 locally, 70 in nightly runbot
self.env['sale.order'].create([{
'partner_id': self.partners[0].id,
'user_id': self.salesmans[0].id,
"order_line": [
(0, 0, {"display_type": "line_note", "name": "NOTE"}),
(0, 0, {"display_type": "line_section", "name": "SECTION"}),
(0, 0, {'product_id': self.products[0].id})
]
} for i in range(2)])
@users('admin')
@warmup
def __test_complex_sales_orders_batch_creation_perf(self):
# NOTE: sometimes more queries on runbot,
# do not change without verifying in multi-builds
# (Seems to be a time-based problem, everytime happening around 10PM)
self._test_complex_sales_orders_batch_creation_perf(1504)
@users('admin')
@warmup
def ___test_complex_sales_orders_batch_creation_perf_with_discount_computation(self):
"""Cover the "complex" logic triggered inside the `_compute_discount`"""
self.env['product.pricelist'].search([]).discount_policy = 'without_discount'
self.env.user.groups_id += self.env.ref('product.group_discount_per_so_line')
# Verify any modification to this count on nightly runbot builds
self._test_complex_sales_orders_batch_creation_perf(1546)
def _test_complex_sales_orders_batch_creation_perf(self, query_count):
MSG = "Model %s, %i records, %s, time %.2f"
vals_list = [{
"partner_id": self.partners[i].id,
"user_id": self.salesmans[i % 2].id,
"order_line": [
(0, 0, {"display_type": "line_note", "name": "NOTE"})
] + [
(0, 0, {'product_id': product.id}) for product in self.products
],
} for i in range(self.ENTITIES)]
with self.assertQueryCount(admin=query_count):
t0 = time.time()
self.env["sale.order"].create(vals_list)
t1 = time.time()
_logger.info(MSG, 'sale.order', self.ENTITIES, "BATCH", t1 - t0)
self.env.cr.flush()
_logger.info(MSG, 'sale.order', self.ENTITIES, "FLUSH", time.time() - t1)
@users('admin')
@warmup
def __test_randomized_solines_qties(self):
"""Make sure the price and discounts computation are complexified
and do not gain from any prefetch/batch gains during the price computation
"""
# Enable discounts
self.env['product.pricelist'].search([]).discount_policy = 'without_discount'
self.env.user.groups_id += self.env.ref('product.group_discount_per_so_line')
vals_list = [{
"partner_id": self.partners[i].id,
"user_id": self.salesmans[i % 2].id,
"order_line": [
(0, 0, {"display_type": "line_note", "name": "NOTE"})
] + [
(0, 0, {
'product_id': product.id,
'product_uom_qty': random.random()
}) for product in self.products
],
} for i in range(self.ENTITIES)]
# 1592 locally, 1593 in nightly runbot, 1954 sometimes
with self.assertQueryCount(admin=1593):
self.env["sale.order"].create(vals_list)