oslo.reports/oslo_reports/tests/test_views.py
Johannes Kulik 68bc329964 Support integer keys of dicts in text serialization
When the config contains a DictOpt that accepts integer keys, the Guru
Meditation Report fails to serialize. Even if current OpenStack code
might not contain such dicts, it's possible that downstream add such
options. Therefore, we change the code to support both dicts with only
integer keys and - just in case - mixed-key dicts.

Change-Id: I44343a8c306c96fc8dc078a76e744cf8b897d8d8
2021-12-20 11:19:19 +01:00

446 lines
16 KiB
Python

# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from unittest import mock
from oslotest import base
from oslo_reports.models import base as base_model
from oslo_reports.models import with_default_views as mwdv
from oslo_reports import report
from oslo_reports.views import jinja_view as jv
from oslo_reports.views.json import generic as json_generic
from oslo_reports.views.text import generic as text_generic
def mwdv_generator():
return mwdv.ModelWithDefaultViews(data={'string': 'value', 'int': 1})
class TestModelReportType(base.BaseTestCase):
def test_model_with_default_views(self):
model = mwdv_generator()
model.set_current_view_type('text')
self.assertEqual('int = 1\nstring = value', str(model))
model.set_current_view_type('json')
self.assertEqual('{"int": 1, "string": "value"}', str(model))
model.set_current_view_type('xml')
self.assertEqual('<model><int>1</int><string>value</string></model>',
str(model))
def test_recursive_type_propagation_with_nested_models(self):
model = mwdv_generator()
model['submodel'] = mwdv_generator()
model.set_current_view_type('json')
self.assertEqual(model.submodel.views['json'],
model.submodel.attached_view)
def test_recursive_type_propagation_with_nested_dicts(self):
nested_model = mwdv.ModelWithDefaultViews(json_view='abc')
data = {'a': 1, 'b': {'c': nested_model}}
top_model = base_model.ReportModel(data=data)
top_model.set_current_view_type('json')
self.assertEqual(nested_model.attached_view,
nested_model.views['json'])
def test_recursive_type_propagation_with_nested_lists(self):
nested_model = mwdv_generator()
data = {'a': 1, 'b': [nested_model]}
top_model = base_model.ReportModel(data=data)
top_model.set_current_view_type('json')
self.assertEqual(nested_model.attached_view,
nested_model.views['json'])
def test_recursive_type_propogation_on_recursive_structures(self):
nested_model = mwdv_generator()
data = {'a': 1, 'b': [nested_model]}
nested_model['c'] = data
top_model = base_model.ReportModel(data=data)
top_model.set_current_view_type('json')
self.assertEqual(nested_model.attached_view,
nested_model.views['json'])
del nested_model['c']
def test_report_of_type(self):
rep = report.ReportOfType('json')
rep.add_section(lambda x: str(x), mwdv_generator)
self.assertEqual('{"int": 1, "string": "value"}', rep.run())
# NOTE: this also tests views.text.header
def test_text_report(self):
rep = report.TextReport('Test Report')
rep.add_section('An Important Section', mwdv_generator)
rep.add_section('Another Important Section', mwdv_generator)
target_str = ('========================================================================\n' # noqa
'==== Test Report ====\n' # noqa
'========================================================================\n' # noqa
'||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n' # noqa
'\n' # noqa
'\n' # noqa
'========================================================================\n' # noqa
'==== An Important Section ====\n' # noqa
'========================================================================\n' # noqa
'int = 1\n' # noqa
'string = value\n' # noqa
'========================================================================\n' # noqa
'==== Another Important Section ====\n' # noqa
'========================================================================\n' # noqa
'int = 1\n' # noqa
'string = value') # noqa
self.assertEqual(target_str, rep.run())
def test_to_type(self):
model = mwdv_generator()
self.assertEqual('<model><int>1</int><string>value</string></model>',
model.to_xml())
class TestGenericXMLView(base.BaseTestCase):
def setUp(self):
super(TestGenericXMLView, self).setUp()
self.model = mwdv_generator()
self.model.set_current_view_type('xml')
def test_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': 2}
target_str = ('<model>'
'<dt><a>1</a><b>2</b></dt>'
'<int>1</int>'
'<string>value</string></model>')
self.assertEqual(target_str, str(self.model))
def test_list_serialization(self):
self.model['lt'] = ['a', 'b']
target_str = ('<model>'
'<int>1</int>'
'<lt><item>a</item><item>b</item></lt>'
'<string>value</string></model>')
self.assertEqual(target_str, str(self.model))
def test_list_in_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': [2, 3]}
target_str = ('<model>'
'<dt><a>1</a>'
'<b><item>2</item><item>3</item></b></dt>'
'<int>1</int>'
'<string>value</string></model>')
self.assertEqual(target_str, str(self.model))
def test_dict_in_list_serialization(self):
self.model['lt'] = [1, {'b': 2, 'c': 3}]
target_str = ('<model>'
'<int>1</int>'
'<lt><item>1</item>'
'<item><b>2</b><c>3</c></item></lt>'
'<string>value</string></model>')
self.assertEqual(target_str, str(self.model))
def test_submodel_serialization(self):
sm = mwdv_generator()
sm.set_current_view_type('xml')
self.model['submodel'] = sm
target_str = ('<model>'
'<int>1</int>'
'<string>value</string>'
'<submodel>'
'<model><int>1</int><string>value</string></model>'
'</submodel>'
'</model>')
self.assertEqual(target_str, str(self.model))
def test_wrapper_name(self):
self.model.attached_view.wrapper_name = 'cheese'
target_str = ('<cheese>'
'<int>1</int>'
'<string>value</string>'
'</cheese>')
self.assertEqual(target_str, str(self.model))
class TestGenericJSONViews(base.BaseTestCase):
def setUp(self):
super(TestGenericJSONViews, self).setUp()
self.model = mwdv_generator()
self.model.set_current_view_type('json')
def test_basic_kv_view(self):
attached_view = json_generic.BasicKeyValueView()
self.model = base_model.ReportModel(data={'string': 'value', 'int': 1},
attached_view=attached_view)
self.assertEqual('{"int": 1, "string": "value"}',
str(self.model))
def test_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': 2}
target_str = ('{'
'"dt": {"a": 1, "b": 2}, '
'"int": 1, '
'"string": "value"'
'}')
self.assertEqual(target_str, str(self.model))
def test_list_serialization(self):
self.model['lt'] = ['a', 'b']
target_str = ('{'
'"int": 1, '
'"lt": ["a", "b"], '
'"string": "value"'
'}')
self.assertEqual(target_str, str(self.model))
def test_list_in_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': [2, 3]}
target_str = ('{'
'"dt": {"a": 1, "b": [2, 3]}, '
'"int": 1, '
'"string": "value"'
'}')
self.assertEqual(target_str, str(self.model))
def test_dict_in_list_serialization(self):
self.model['lt'] = [1, {'b': 2, 'c': 3}]
target_str = ('{'
'"int": 1, '
'"lt": [1, {"b": 2, "c": 3}], '
'"string": "value"'
'}')
self.assertEqual(target_str, str(self.model))
def test_submodel_serialization(self):
sm = mwdv_generator()
sm.set_current_view_type('json')
self.model['submodel'] = sm
target_str = ('{'
'"int": 1, '
'"string": "value", '
'"submodel": {"int": 1, "string": "value"}'
'}')
self.assertEqual(target_str, str(self.model))
class TestGenericTextViews(base.BaseTestCase):
def setUp(self):
super(TestGenericTextViews, self).setUp()
self.model = mwdv_generator()
self.model.set_current_view_type('text')
def test_multi_view(self):
attached_view = text_generic.MultiView()
self.model = base_model.ReportModel(data={},
attached_view=attached_view)
self.model['1'] = mwdv_generator()
self.model['2'] = mwdv_generator()
self.model['2']['int'] = 2
self.model.set_current_view_type('text')
target_str = ('int = 1\n'
'string = value\n'
'int = 2\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def test_basic_kv_view(self):
attached_view = text_generic.BasicKeyValueView()
self.model = base_model.ReportModel(data={'string': 'value', 'int': 1},
attached_view=attached_view)
self.assertEqual('int = 1\nstring = value\n',
str(self.model))
def test_table_view(self):
column_names = ['Column A', 'Column B']
column_values = ['a', 'b']
attached_view = text_generic.TableView(column_names, column_values,
'table')
self.model = base_model.ReportModel(data={},
attached_view=attached_view)
self.model['table'] = [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]
target_str = (' Column A | Column B \n' # noqa
'------------------------------------------------------------------------\n' # noqa
' 1 | 2 \n' # noqa
' 3 | 4 \n') # noqa
self.assertEqual(target_str, str(self.model))
def test_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': 2}
target_str = ('dt = \n'
' a = 1\n'
' b = 2\n'
'int = 1\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def test_dict_serialization_integer_keys(self):
self.model['dt'] = {3: 4, 5: 6}
target_str = ('dt = \n'
' 3 = 4\n'
' 5 = 6\n'
'int = 1\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def test_dict_serialization_mixed_keys(self):
self.model['dt'] = {'3': 4, 5: 6}
target_str = ('dt = \n'
' 3 = 4\n'
' 5 = 6\n'
'int = 1\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def test_list_serialization(self):
self.model['lt'] = ['a', 'b']
target_str = ('int = 1\n'
'lt = \n'
' a\n'
' b\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def test_list_in_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': [2, 3]}
target_str = ('dt = \n'
' a = 1\n'
' b = \n'
' 2\n'
' 3\n'
'int = 1\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def test_dict_in_list_serialization(self):
self.model['lt'] = [1, {'b': 2, 'c': 3}]
target_str = ('int = 1\n'
'lt = \n'
' 1\n'
' [dict]\n'
' b = 2\n'
' c = 3\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def test_submodel_serialization(self):
sm = mwdv_generator()
sm.set_current_view_type('text')
self.model['submodel'] = sm
target_str = ('int = 1\n'
'string = value\n'
'submodel = \n'
' int = 1\n'
' string = value')
self.assertEqual(target_str, str(self.model))
def test_custom_indent_string(self):
view = text_generic.KeyValueView(indent_str='~~')
self.model['lt'] = ['a', 'b']
self.model.attached_view = view
target_str = ('int = 1\n'
'lt = \n'
'~~a\n'
'~~b\n'
'string = value')
self.assertEqual(target_str, str(self.model))
def get_open_mocks(rv):
file_mock = mock.MagicMock(name='file_obj')
file_mock.read.return_value = rv
open_mock = mock.MagicMock(name='open')
open_mock().__enter__.return_value = file_mock
return (open_mock, file_mock)
class TestJinjaView(base.BaseTestCase):
TEMPL_STR = "int is {{ int }}, string is {{ string }}"
MM_OPEN, MM_FILE = get_open_mocks(TEMPL_STR)
def setUp(self):
super(TestJinjaView, self).setUp()
self.model = base_model.ReportModel(data={'int': 1, 'string': 'value'})
@mock.mock_open(MM_OPEN)
def test_load_from_file(self):
self.model.attached_view = jv.JinjaView(path='a/b/c/d.jinja.txt')
self.assertEqual('int is 1, string is value',
str(self.model))
self.MM_FILE.assert_called_with_once('a/b/c/d.jinja.txt')
def test_direct_pass(self):
self.model.attached_view = jv.JinjaView(text=self.TEMPL_STR)
self.assertEqual('int is 1, string is value',
str(self.model))
def test_load_from_class(self):
class TmpJinjaView(jv.JinjaView):
VIEW_TEXT = TestJinjaView.TEMPL_STR
self.model.attached_view = TmpJinjaView()
self.assertEqual('int is 1, string is value',
str(self.model))
def test_is_deepcopiable(self):
view_orig = jv.JinjaView(text=self.TEMPL_STR)
view_cpy = copy.deepcopy(view_orig)
self.assertIsNot(view_orig, view_cpy)