Files
odoo_source/addons/test_mail/tests/test_performance.py
T
Raphael ColletandVictor Feyens 840609975a [IMP] core: better way to set/update magic fields
The goal of this change is to simplify the code managing `create_date`
and `write_date` in methods `create()` and `write()`, and also to remove
weird behaviors caused by the way those fields were updated.

Assume we update a simple field on a record.  This adds pending updates
for the field and `write_date`.  However, the value of `write_date` is
not known yet: it will be updated as `NOW() AT TIME ZONE 'UTC'` in SQL.
So `write_date` is actually given a dummy value in pending updates, and
it is invalidated from cache, until its value is flushed to the database
and fetched again.

Now assume we access another field on the record, and that field is not
in cache.  The prefetching mechanism will read all column fields,
including `write_date`, and flush them first.

    # this adds pending updates foo: 42, write_uid: 1, write_date: False
    record.foo = 42

    # assume 'bar' is not in cache; this prefetches all column fields,
    # which flushes the pending updates above before reading them back
    result = record.bar

We can avoid flushing pending updates if the values read from database
do not overwrite existing values in cache.  If you assume that the value
of a pending update is in cache (in the example, `foo: 42`), you don't
need to flush the corresponding field.  Indeed, the value of `foo` will
remain 42 in cache, whatever its value in the database.  This assumption
(pending updates are in cache) is true for all fields *except* for
`write_date`: it is invalidated from cache, and given a dummy value in
pending updates.  This branch actually makes this assumption true for
all fields.  The avoidance of flushing pending updates will be done in
another commit.

In order to directly assign `write_date` its value, we use a cache for
the value `NOW() AT TIME ZONE 'UTC'` from the database.  This costs at
most one query per transaction, and potentially saves a few queries.

Co-authored-by: Victor Feyens <vfe@odoo.com>
2021-02-22 16:20:55 +00:00

975 lines
42 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
from odoo.tests.common import users, warmup
from odoo.tests import tagged
from odoo.tools import mute_logger, formataddr
@tagged('mail_performance')
class BaseMailPerformance(TransactionCaseWithUserDemo):
def setUp(self):
super(BaseMailPerformance, self).setUp()
self._quick_create_ctx = {
'no_reset_password': True,
'mail_create_nolog': True,
'mail_create_nosubscribe': True,
'mail_notrack': True,
}
self.user_employee = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'Ernest Employee',
'login': 'emp',
'email': 'e.e@example.com',
'signature': '--\nErnest',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id, self.env.ref('base.group_partner_manager').id])],
})
# patch registry to simulate a ready environment
self.patch(self.env.registry, 'ready', True)
@tagged('mail_performance')
class TestMailPerformance(BaseMailPerformance):
def setUp(self):
super(TestMailPerformance, self).setUp()
self.res_partner_3 = self.env['res.partner'].create({
'name': 'Gemini Furniture',
'email': 'gemini.furniture39@example.com',
})
self.res_partner_4 = self.env['res.partner'].create({
'name': 'Ready Mat',
'email': 'ready.mat28@example.com',
})
self.res_partner_10 = self.env['res.partner'].create({
'name': 'The Jackson Group',
'email': 'jackson.group82@example.com',
})
self.res_partner_12 = self.env['res.partner'].create({
'name': 'Azure Interior',
'email': 'azure.Interior24@example.com',
})
self.env['mail.performance.thread'].create([
{
'name': 'Object 0',
'value': 0,
'partner_id': self.res_partner_3.id,
}, {
'name': 'Object 1',
'value': 10,
'partner_id': self.res_partner_3.id,
}, {
'name': 'Object 2',
'value': 20,
'partner_id': self.res_partner_4.id,
}, {
'name': 'Object 3',
'value': 30,
'partner_id': self.res_partner_10.id,
}, {
'name': 'Object 4',
'value': 40,
'partner_id': self.res_partner_12.id,
}
])
@users('__system__', 'demo')
@warmup
def test_read_mail(self):
""" Read records inheriting from 'mail.thread'. """
records = self.env['mail.performance.thread'].search([])
self.assertEqual(len(records), 5)
with self.assertQueryCount(__system__=2, demo=2):
# without cache
for record in records:
record.partner_id.country_id.name
with self.assertQueryCount(0):
# with cache
for record in records:
record.partner_id.country_id.name
with self.assertQueryCount(0):
# value_pc must have been prefetched, too
for record in records:
record.value_pc
@users('__system__', 'demo')
@warmup
def test_write_mail(self):
""" Write records inheriting from 'mail.thread' (no recomputation). """
records = self.env['mail.performance.thread'].search([])
self.assertEqual(len(records), 5)
with self.assertQueryCount(__system__=2, demo=2):
records.write({'name': 'X'})
@users('__system__', 'demo')
@warmup
def test_write_mail_with_recomputation(self):
""" Write records inheriting from 'mail.thread' (with recomputation). """
records = self.env['mail.performance.thread'].search([])
self.assertEqual(len(records), 5)
with self.assertQueryCount(__system__=2, demo=2):
records.write({'value': 42})
@users('__system__', 'demo')
@warmup
def test_write_mail_with_tracking(self):
""" Write records inheriting from 'mail.thread' (with field tracking). """
record = self.env['mail.performance.thread'].create({
'name': 'Test',
'track': 'Y',
'value': 40,
'partner_id': self.res_partner_12.id,
})
with self.assertQueryCount(__system__=3, demo=3):
record.track = 'X'
@users('__system__', 'demo')
@warmup
def test_create_mail(self):
""" Create records inheriting from 'mail.thread' (without field tracking). """
model = self.env['mail.performance.thread']
with self.assertQueryCount(__system__=2, demo=2):
model.with_context(tracking_disable=True).create({'name': 'X'})
@users('__system__', 'demo')
@warmup
def test_create_mail_with_tracking(self):
""" Create records inheriting from 'mail.thread' (with field tracking). """
with self.assertQueryCount(__system__=7, demo=7):
self.env['mail.performance.thread'].create({'name': 'X'})
@users('__system__', 'emp')
@warmup
def test_create_mail_simple(self):
with self.assertQueryCount(__system__=6, emp=6):
self.env['mail.test.simple'].create({'name': 'Test'})
@users('__system__', 'emp')
@warmup
def test_write_mail_simple(self):
rec = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=1, emp=1):
rec.write({
'name': 'Test2',
'email_from': 'test@test.com',
})
@tagged('mail_performance')
class TestMailAPIPerformance(BaseMailPerformance):
def setUp(self):
super(TestMailAPIPerformance, self).setUp()
self.customer = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'Test Customer',
'email': 'customer.test@example.com',
})
self.user_test = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'Paulette Testouille',
'login': 'paul',
'email': 'user.test.paulette@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
# automatically follow activities, for backward compatibility concerning query count
self.env.ref('mail.mt_activities').write({'default': True})
def _create_test_records(self):
self.test_record_full = self.env['mail.test.ticket'].with_context(self._quick_create_ctx).create({
'name': 'TestRecord',
'customer_id': self.customer.id,
'user_id': self.user_test.id,
'email_from': 'nopartner.test@example.com',
})
self.test_template_full = self.env['mail.template'].create({
'name': 'TestTemplate',
'model_id': self.env['ir.model']._get('mail.test.ticket').id,
'subject': 'About ${object.name}',
'body_html': '<p>Hello ${object.name}</p>',
'email_from': '${object.user_id.email_formatted | safe}',
'partner_to': '${object.customer_id.id}',
'email_to': '${("%s Customer <%s>" % (object.name, object.email_from)) | safe}',
})
@users('__system__', 'emp')
@warmup
def test_adv_activity(self):
model = self.env['mail.test.activity']
with self.assertQueryCount(__system__=6, emp=6):
model.create({'name': 'Test'})
@users('__system__', 'emp')
@warmup
@mute_logger('odoo.models.unlink')
def test_adv_activity_full(self):
record = self.env['mail.test.activity'].create({'name': 'Test'})
MailActivity = self.env['mail.activity'].with_context({
'default_res_model': 'mail.test.activity',
})
with self.assertQueryCount(__system__=6, emp=6):
activity = MailActivity.create({
'summary': 'Test Activity',
'res_id': record.id,
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
})
#read activity_type to normalize cache between enterprise and community
#voip module read activity_type during create leading to one less query in enterprise on action_feedback
category = activity.activity_type_id.category
with self.assertQueryCount(__system__=16, emp=18):
activity.action_feedback(feedback='Zizisse Done !')
@users('__system__', 'emp')
@warmup
@mute_logger('odoo.models.unlink')
def test_adv_activity_mixin(self):
record = self.env['mail.test.activity'].create({'name': 'Test'})
with self.assertQueryCount(__system__=8, emp=8):
activity = record.action_start('Test Start')
#read activity_type to normalize cache between enterprise and community
#voip module read activity_type during create leading to one less query in enterprise on action_close
category = activity.activity_type_id.category
record.write({'name': 'Dupe write'})
with self.assertQueryCount(__system__=17, emp=19):
record.action_close('Dupe feedback')
self.assertEqual(record.activity_ids, self.env['mail.activity'])
@users('__system__', 'emp')
@warmup
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
def test_mail_composer(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
customer_id = self.customer.id
with self.assertQueryCount(__system__=2, emp=2):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
'default_res_id': test_record.id,
}).create({
'body': '<p>Test Body</p>',
'partner_ids': [(4, customer_id)],
})
with self.assertQueryCount(__system__=24, emp=31):
composer.send_mail()
@users('__system__', 'emp')
@warmup
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_w_template(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
test_template = self.env['mail.template'].browse(self.test_template_full.id)
# TODO FIXME non deterministic, last check 25 Mar 2020. Runbot at most +7 compared to local.
with self.assertQueryCount(__system__=11, emp=12):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
'default_res_id': test_record.id,
'default_template_id': test_template.id,
}).create({})
composer.onchange_template_id_wrapper()
with self.assertQueryCount(__system__=32, emp=39):
composer.send_mail()
# remove created partner to ensure tests are the same each run
self.env['res.partner'].sudo().search([('email', '=', 'nopartner.test@example.com')]).unlink()
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_message_assignation_email(self):
self.user_test.write({'notification_type': 'email'})
record = self.env['mail.test.track'].create({'name': 'Test'})
with self.assertQueryCount(__system__=36, emp=37):
record.write({
'user_id': self.user_test.id,
})
@users('__system__', 'emp')
@warmup
def test_message_assignation_inbox(self):
record = self.env['mail.test.track'].create({'name': 'Test'})
with self.assertQueryCount(__system__=18, emp=21):
record.write({
'user_id': self.user_test.id,
})
@users('__system__', 'emp')
@warmup
def test_message_log(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=1, emp=1):
record._message_log(
body='<p>Test _message_log</p>',
message_type='comment')
@users('__system__', 'emp')
@warmup
def test_message_log_with_post(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=3, emp=6):
record.message_post(
body='<p>Test message_post as log</p>',
subtype_xmlid='mail.mt_note',
message_type='comment')
@users('__system__', 'emp')
@warmup
def test_message_post_no_notification(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=3, emp=6):
record.message_post(
body='<p>Test Post Performances basic</p>',
partner_ids=[],
message_type='comment',
subtype_xmlid='mail.mt_comment')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_message_post_one_email_notification(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=27, emp=30):
record.message_post(
body='<p>Test Post Performances with an email ping</p>',
partner_ids=self.customer.ids,
message_type='comment',
subtype_xmlid='mail.mt_comment')
@users('__system__', 'emp')
@warmup
def test_message_post_one_inbox_notification(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=12, emp=17):
record.message_post(
body='<p>Test Post Performances with an inbox ping</p>',
partner_ids=self.user_test.partner_id.ids,
message_type='comment',
subtype_xmlid='mail.mt_comment')
@mute_logger('odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_message_subscribe_default(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=6, emp=6):
record.message_subscribe(partner_ids=self.user_test.partner_id.ids)
with self.assertQueryCount(__system__=3, emp=3):
record.message_subscribe(partner_ids=self.user_test.partner_id.ids)
@mute_logger('odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_message_subscribe_subtypes(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
subtype_ids = (self.env.ref('test_mail.st_mail_test_simple_external') | self.env.ref('mail.mt_comment')).ids
with self.assertQueryCount(__system__=5, emp=5):
record.message_subscribe(partner_ids=self.user_test.partner_id.ids, subtype_ids=subtype_ids)
with self.assertQueryCount(__system__=2, emp=2):
record.message_subscribe(partner_ids=self.user_test.partner_id.ids, subtype_ids=subtype_ids)
@mute_logger('odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_message_track(self):
record = self.env['mail.performance.tracking'].create({'name': 'Zizizatestname'})
with self.assertQueryCount(__system__=3, emp=3):
record.write({'name': 'Zizizanewtestname'})
record.flush()
with self.assertQueryCount(__system__=5, emp=5):
record.write({'field_%s' % (i): 'Tracked Char Fields %s' % (i) for i in range(3)})
record.flush()
with self.assertQueryCount(__system__=6, emp=6):
record.write({'field_%s' % (i): 'Field Without Cache %s' % (i) for i in range(3)})
record.flush()
record.write({'field_%s' % (i): 'Field With Cache %s' % (i) for i in range(3)})
record.flush()
@tagged('mail_performance')
class TestMailComplexPerformance(BaseMailPerformance):
def setUp(self):
super(TestMailComplexPerformance, self).setUp()
self.user_portal = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'Olivia Portal',
'login': 'port',
'email': 'p.p@example.com',
'signature': '--\nOlivia',
'notification_type': 'email',
'groups_id': [(6, 0, [self.env.ref('base.group_portal').id])],
})
# setup mail gateway
self.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', 'example.com')
self.env['ir.config_parameter'].sudo().set_param('mail.catchall.alias', 'test-catchall')
self.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', 'test-bounce')
self.channel = self.env['mail.channel'].with_context(self._quick_create_ctx).create({
'name': 'Listener',
})
# prepare recipients to test for more realistic workload
self.customer = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'Test Customer',
'email': 'test@example.com'
})
self.container = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({
'name': 'Test Container',
'customer_id': self.customer.id,
'alias_name': 'test-alias',
})
Partners = self.env['res.partner'].with_context(self._quick_create_ctx)
self.partners = self.env['res.partner']
for x in range(0, 10):
self.partners |= Partners.create({'name': 'Test %s' % x, 'email': 'test%s@example.com' % x})
self.container.message_subscribe(self.partners.ids, subtype_ids=[
self.env.ref('mail.mt_comment').id,
self.env.ref('test_mail.st_mail_test_container_child_full').id]
)
# `test_complex_mail_mail_send`
self.container.flush()
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_mail_mail_send(self):
message = self.env['mail.message'].sudo().create({
'subject': 'Test',
'body': '<p>Test</p>',
'author_id': self.env.user.partner_id.id,
'email_from': self.env.user.partner_id.email,
'model': 'mail.test.container',
'res_id': self.container.id,
})
mail = self.env['mail.mail'].sudo().create({
'body_html': '<p>Test</p>',
'mail_message_id': message.id,
'recipient_ids': [(4, pid) for pid in self.partners.ids],
})
mail_ids = mail.ids
with self.assertQueryCount(__system__=6, emp=6):
self.env['mail.mail'].sudo().browse(mail_ids).send()
self.assertEqual(mail.body_html, '<p>Test</p>')
self.assertEqual(mail.reply_to, formataddr(('%s %s' % (self.env.company.name, self.container.name), 'test-alias@example.com')))
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_message_post(self):
self.container.message_subscribe(self.user_portal.partner_id.ids)
record = self.container.with_user(self.env.user)
with self.assertQueryCount(__system__=61, emp=62):
record.message_post(
body='<p>Test Post Performances</p>',
message_type='comment',
subtype_xmlid='mail.mt_comment')
self.assertEqual(record.message_ids[0].body, '<p>Test Post Performances</p>')
self.assertEqual(record.message_ids[0].notified_partner_ids, self.partners | self.user_portal.partner_id)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_message_post_template(self):
self.container.message_subscribe(self.user_portal.partner_id.ids)
record = self.container.with_user(self.env.user)
template_id = self.env.ref('test_mail.mail_test_container_tpl').id
with self.assertQueryCount(__system__=70, emp=71):
record.message_post_with_template(template_id, message_type='comment', composition_mode='comment')
self.assertEqual(record.message_ids[0].body, '<p>Adding stuff on %s</p>' % record.name)
self.assertEqual(record.message_ids[0].notified_partner_ids, self.partners | self.user_portal.partner_id | self.customer)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_message_subscribe(self):
pids = self.partners.ids
cids = self.channel.ids
subtypes = self.env.ref('mail.mt_comment') | self.env.ref('test_mail.st_mail_test_ticket_container_upd')
subtype_ids = subtypes.ids
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': False,
'customer_id': False,
'user_id': self.user_portal.id,
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.env.user.partner_id | self.user_portal.partner_id)
self.assertEqual(rec1.message_channel_ids, self.env['mail.channel'])
# subscribe new followers with forced given subtypes
with self.assertQueryCount(__system__=8, emp=8):
rec.message_subscribe(
partner_ids=pids[:4],
channel_ids=cids,
subtype_ids=subtype_ids
)
self.assertEqual(rec1.message_partner_ids, self.env.user.partner_id | self.user_portal.partner_id | self.partners[:4])
self.assertEqual(rec1.message_channel_ids, self.channel)
# subscribe existing and new followers with force=False, meaning only some new followers will be added
with self.assertQueryCount(__system__=6, emp=6):
rec.message_subscribe(
partner_ids=pids[:6],
channel_ids=cids,
subtype_ids=None
)
self.assertEqual(rec1.message_partner_ids, self.env.user.partner_id | self.user_portal.partner_id | self.partners[:6])
self.assertEqual(rec1.message_channel_ids, self.channel)
# subscribe existing and new followers with force=True, meaning all will have the same subtypes
with self.assertQueryCount(__system__=7, emp=7):
rec.message_subscribe(
partner_ids=pids,
channel_ids=cids,
subtype_ids=subtype_ids
)
self.assertEqual(rec1.message_partner_ids, self.env.user.partner_id | self.user_portal.partner_id | self.partners)
self.assertEqual(rec1.message_channel_ids, self.channel)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_tracking_assignation(self):
""" Assignation performance test on already-created record """
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': self.container.id,
'customer_id': self.customer.id,
'user_id': self.env.uid,
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id)
with self.assertQueryCount(__system__=36, emp=37):
rec.write({'user_id': self.user_portal.id})
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
# write tracking message
self.assertEqual(rec1.message_ids[0].subtype_id, self.env.ref('mail.mt_note'))
self.assertEqual(rec1.message_ids[0].notified_partner_ids, self.env['res.partner'])
# creation message
self.assertEqual(rec1.message_ids[1].subtype_id, self.env.ref('test_mail.st_mail_test_ticket_container_upd'))
self.assertEqual(rec1.message_ids[1].notified_partner_ids, self.partners)
self.assertEqual(len(rec1.message_ids), 2)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_tracking_subscription_create(self):
""" Creation performance test involving auto subscription, assignation, tracking with subtype and template send. """
container_id = self.container.id
customer_id = self.customer.id
user_id = self.user_portal.id
with self.assertQueryCount(__system__=105, emp=106):
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': container_id,
'customer_id': customer_id,
'user_id': user_id,
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
# creation message
self.assertEqual(rec1.message_ids[0].subtype_id, self.env.ref('test_mail.st_mail_test_ticket_container_upd'))
self.assertEqual(rec1.message_ids[0].notified_partner_ids, self.partners | self.user_portal.partner_id)
self.assertEqual(len(rec1.message_ids), 1)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_tracking_subscription_subtype(self):
""" Write performance test involving auto subscription, tracking with subtype """
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': False,
'customer_id': False,
'user_id': self.user_portal.id,
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
self.assertEqual(len(rec1.message_ids), 1)
with self.assertQueryCount(__system__=75, emp=75):
rec.write({
'name': 'Test2',
'container_id': self.container.id,
})
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
# write tracking message
self.assertEqual(rec1.message_ids[0].subtype_id, self.env.ref('test_mail.st_mail_test_ticket_container_upd'))
self.assertEqual(rec1.message_ids[0].notified_partner_ids, self.partners | self.user_portal.partner_id)
# creation message
self.assertEqual(rec1.message_ids[1].subtype_id, self.env.ref('mail.mt_note'))
self.assertEqual(rec1.message_ids[1].notified_partner_ids, self.env['res.partner'])
self.assertEqual(len(rec1.message_ids), 2)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_tracking_subscription_write(self):
""" Write performance test involving auto subscription, tracking with subtype and template send """
container_id = self.container.id
customer_id = self.customer.id
container2 = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({
'name': 'Test Container 2',
'customer_id': False,
'alias_name': False,
})
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': container2.id,
'customer_id': False,
'user_id': self.user_portal.id,
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
with self.assertQueryCount(__system__=83, emp=83):
rec.write({
'name': 'Test2',
'container_id': container_id,
'customer_id': customer_id,
})
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
# write tracking message
self.assertEqual(rec1.message_ids[0].subtype_id, self.env.ref('test_mail.st_mail_test_ticket_container_upd'))
self.assertEqual(rec1.message_ids[0].notified_partner_ids, self.partners | self.user_portal.partner_id)
# creation message
self.assertEqual(rec1.message_ids[1].subtype_id, self.env.ref('test_mail.st_mail_test_ticket_container_upd'))
self.assertEqual(rec1.message_ids[1].notified_partner_ids, self.user_portal.partner_id)
self.assertEqual(len(rec1.message_ids), 2)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'emp')
@warmup
def test_complex_tracking_template(self):
""" Write performance test involving assignation, tracking with template """
customer_id = self.customer.id
self.assertTrue(self.env.registry.ready, "We need to simulate that registery is ready")
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': self.container.id,
'customer_id': False,
'user_id': self.user_portal.id,
'mail_template': self.env.ref('test_mail.mail_test_ticket_tracking_tpl').id,
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
with self.assertQueryCount(__system__=28, emp=29):
rec.write({
'name': 'Test2',
'customer_id': customer_id,
'user_id': self.env.uid,
})
# write template message (sent to customer, mass mailing kept for history)
self.assertEqual(rec1.message_ids[0].subtype_id, self.env['mail.message.subtype'])
self.assertEqual(rec1.message_ids[0].subject, 'Test Template')
# write tracking message
self.assertEqual(rec1.message_ids[1].subtype_id, self.env.ref('mail.mt_note'))
self.assertEqual(rec1.message_ids[1].notified_partner_ids, self.env['res.partner'])
# creation message
self.assertEqual(rec1.message_ids[2].subtype_id, self.env.ref('test_mail.st_mail_test_ticket_container_upd'))
self.assertEqual(rec1.message_ids[2].notified_partner_ids, self.partners | self.user_portal.partner_id)
self.assertEqual(len(rec1.message_ids), 3)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('emp')
@warmup
def test_message_format(self):
"""Test performance of `_message_format` and of `message_format` with
multiple messages with multiple attachments, different authors, various
notifications, and different tracking values.
Those messages might not make sense functionally but they are crafted to
cover as much of the code as possible in regard to number of queries.
"""
name_field = self.env['ir.model.fields']._get(self.container._name, 'name')
customer_id_field = self.env['ir.model.fields']._get(self.container._name, 'customer_id')
messages = self.env['mail.message'].sudo().create([{
'subject': 'Test 0',
'body': '<p>Test 0</p>',
'author_id': self.partners[0].id,
'email_from': self.partners[0].email,
'model': 'mail.test.container',
'res_id': self.container.id,
'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_comment'),
'attachment_ids': [
(0, 0, {
'name': 'test file 0 - %d' % j,
'datas': 'data',
}) for j in range(2)
],
'notification_ids': [
(0, 0, {
'res_partner_id': self.partners[3].id,
'notification_type': 'inbox',
}),
(0, 0, {
'res_partner_id': self.partners[4].id,
'notification_type': 'email',
'notification_status': 'exception',
}),
(0, 0, {
'res_partner_id': self.partners[6].id,
'notification_type': 'email',
'notification_status': 'exception',
}),
],
'tracking_value_ids': [
(0, 0, {
'field': name_field.id,
'field_desc': 'Name',
'old_value_char': 'old 0',
'new_value_char': 'new 0',
}),
(0, 0, {
'field': customer_id_field.id,
'field_desc': 'Customer',
'old_value_integer': self.partners[7].id,
'new_value_integer': self.partners[8].id,
}),
]
}, {
'subject': 'Test 1',
'body': '<p>Test 1</p>',
'author_id': self.partners[1].id,
'email_from': self.partners[1].email,
'model': 'mail.test.container',
'res_id': self.container.id,
'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'),
'attachment_ids': [
(0, 0, {
'name': 'test file 1 - %d' % j,
'datas': 'data',
}) for j in range(2)
],
'notification_ids': [
(0, 0, {
'res_partner_id': self.partners[5].id,
'notification_type': 'inbox',
}),
(0, 0, {
'res_partner_id': self.partners[6].id,
'notification_type': 'email',
'notification_status': 'exception',
}),
],
'tracking_value_ids': [
(0, 0, {
'field': name_field.id,
'field_desc': 'Name',
'old_value_char': 'old 1',
'new_value_char': 'new 1',
}),
(0, 0, {
'field': customer_id_field.id,
'field_desc': 'Customer',
'old_value_integer': self.partners[7].id,
'new_value_integer': self.partners[8].id,
}),
]
}])
with self.assertQueryCount(emp=5):
res = messages.message_format()
self.assertEqual(len(res), 2)
for message in res:
self.assertEqual(len(message['attachment_ids']), 2)
messages.flush()
messages.invalidate_cache()
with self.assertQueryCount(emp=17):
res = messages.message_format()
self.assertEqual(len(res), 2)
for message in res:
self.assertEqual(len(message['attachment_ids']), 2)
@tagged('mail_performance')
class TestMailHeavyPerformancePost(BaseMailPerformance):
def setUp(self):
super(TestMailHeavyPerformancePost, self).setUp()
# record
self.customer = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'customer',
'email': 'customer@example.com',
})
self.record = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({
'name': 'Test record',
'customer_id': self.customer.id,
'alias_name': 'test-alias',
})
# followers
self.user_follower_email = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_follower_email',
'login': 'user_follower_email',
'email': 'user_follower_email@example.com',
'notification_type': 'email',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.user_follower_inbox = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_follower_inbox',
'login': 'user_follower_inbox',
'email': 'user_follower_inbox@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.partner_follower = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'partner_follower',
'email': 'partner_follower@example.com',
})
self.record.message_subscribe([
self.partner_follower.id,
self.user_follower_inbox.partner_id.id,
self.user_follower_email.partner_id.id
])
# partner_ids
self.user_inbox = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_inbox',
'login': 'user_inbox',
'email': 'user_inbox@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.user_email = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_email',
'login': 'user_email',
'email': 'user_email@example.com',
'notification_type': 'email',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.partner = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'partner',
'email': 'partner@example.com',
})
# channels user/partner
self.partner_channel_inbox = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'partner_channel_inbox',
'email': 'partner_channel_inbox@example.com',
})
self.partner_channel_email = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'partner_channel_email',
'email': 'partner_channel_email@example.com',
})
self.user_channel_inbox = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_channel_inbox',
'login': 'user_channel_inbox',
'email': 'user_channel_inbox@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.user_channel_email = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_channel_email',
'login': 'user_channel_email',
'email': 'user_channel_email@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
# channels
self.channel_inbox = self.env['mail.channel'].with_context(self._quick_create_ctx).create({
'name': 'channel_inbox',
'channel_partner_ids': [(4, self.partner_channel_inbox.id), (4, self.user_channel_inbox.partner_id.id)]
})
self.channel_email = self.env['mail.channel'].with_context(self._quick_create_ctx).create({
'name': 'channel_email',
'email_send': True,
'channel_partner_ids': [(4, self.partner_channel_email.id), (4, self.user_channel_email.partner_id.id)]
})
self.vals = [{
'datas': base64.b64encode(bytes("attachement content %s" % i, 'utf-8')),
'name': 'fileText_test%s.txt' % i,
'mimetype': 'text/plain',
'res_model': 'mail.compose.message',
'res_id': 0,
} for i in range(3)]
self.patch(self.env.registry, 'ready', True)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('emp')
@warmup
def test_complete_message_post(self):
# aims to cover as much features of message_post as possible
partner_ids = [self.user_inbox.partner_id.id, self.user_email.partner_id.id, self.partner.id]
channel_ids = [self.channel_inbox.id, self.channel_email.id]
record = self.record.with_user(self.env.user)
attachements = [ # not linear on number of attachements
('attach tuple 1', "attachement tupple content 1"),
('attach tuple 2', "attachement tupple content 2", {'cid': 'cid1'}),
('attach tuple 3', "attachement tupple content 3", {'cid': 'cid2'}),
]
self.attachements = self.env['ir.attachment'].with_user(self.env.user).create(self.vals)
attachement_ids = self.attachements.ids
with self.assertQueryCount(emp=76):
self.cr.sql_log = self.warm and self.cr.sql_log_count
record.with_context({}).message_post(
body='<p>Test body <img src="cid:cid1"> <img src="cid:cid2"></p>',
subject='Test Subject',
message_type='notification',
subtype_xmlid=None,
partner_ids=partner_ids,
channel_ids=channel_ids,
parent_id=False,
attachments=attachements,
attachment_ids=attachement_ids,
add_sign=True,
model_description=False,
mail_auto_delete=True
)
self.cr.sql_log = False
self.assertTrue(record.message_ids[0].body.startswith('<p>Test body <img src="/web/image/'))
self.assertEqual(self.attachements.mapped('res_model'), [record._name for i in range(3)])
self.assertEqual(self.attachements.mapped('res_id'), [record.id for i in range(3)])
# self.assertEqual(record.message_ids[0].notified_partner_ids, [])