diff --git a/lms/djangoapps/course_blocks/transformers/tests/helpers.py b/lms/djangoapps/course_blocks/transformers/tests/helpers.py index 9be79333a4..2ae2123ad1 100644 --- a/lms/djangoapps/course_blocks/transformers/tests/helpers.py +++ b/lms/djangoapps/course_blocks/transformers/tests/helpers.py @@ -3,13 +3,14 @@ Test helpers for testing course block transformers. """ from mock import patch from course_modes.models import CourseMode +from lms.djangoapps.courseware.access import has_access from openedx.core.lib.block_structure.transformers import BlockStructureTransformers +from openedx.core.lib.block_structure.tests.helpers import clear_registered_transformers_cache from student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase -from lms.djangoapps.courseware.access import has_access from ...api import get_course_blocks @@ -30,6 +31,7 @@ class TransformerRegistryTestMixin(object): def tearDown(self): self.patcher.stop() + clear_registered_transformers_cache() class CourseStructureTestCase(TransformerRegistryTestMixin, ModuleStoreTestCase): diff --git a/openedx/core/lib/block_structure/tests/helpers.py b/openedx/core/lib/block_structure/tests/helpers.py index ff9c3b39ed..70863a7aa4 100644 --- a/openedx/core/lib/block_structure/tests/helpers.py +++ b/openedx/core/lib/block_structure/tests/helpers.py @@ -7,6 +7,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError from ..block_structure import BlockStructureBlockData from ..transformer import BlockStructureTransformer, FilteringTransformerMixin +from ..transformer_registry import TransformerRegistry class MockXBlock(object): @@ -164,11 +165,19 @@ class MockFilteringTransformer(FilteringTransformerMixin, BlockStructureTransfor return [block_structure.create_universal_filter()] +def clear_registered_transformers_cache(): + """ + Test helper to clear out any cached values of registered transformers. + """ + TransformerRegistry.get_write_version_hash.cache.clear() + + @contextmanager def mock_registered_transformers(transformers): """ Context manager for mocking the transformer registry to return the given transformers. """ + clear_registered_transformers_cache() with patch( 'openedx.core.lib.block_structure.transformer_registry.TransformerRegistry.get_registered_transformers' ) as mock_available_transforms: diff --git a/openedx/core/lib/block_structure/tests/test_transformer_registry.py b/openedx/core/lib/block_structure/tests/test_transformer_registry.py index 59e5340c14..96ff1ffad4 100644 --- a/openedx/core/lib/block_structure/tests/test_transformer_registry.py +++ b/openedx/core/lib/block_structure/tests/test_transformer_registry.py @@ -7,7 +7,7 @@ from nose.plugins.attrib import attr from unittest import TestCase from ..transformer_registry import TransformerRegistry -from .helpers import MockTransformer, mock_registered_transformers +from .helpers import MockTransformer, mock_registered_transformers, clear_registered_transformers_cache class TestTransformer1(MockTransformer): @@ -37,6 +37,10 @@ class TransformerRegistryTestCase(TestCase): """ Test cases for TransformerRegistry. """ + def tearDown(self): + super(TransformerRegistryTestCase, self).tearDown() + clear_registered_transformers_cache() + @ddt.data( # None case ([], []), @@ -61,3 +65,18 @@ class TransformerRegistryTestCase(TestCase): TransformerRegistry.find_unregistered(transformers), set(expected_unregistered), ) + + def test_write_version_hash(self): + # hash with TestTransformer1 + with mock_registered_transformers([TestTransformer1]): + version_hash_1 = TransformerRegistry.get_write_version_hash() + self.assertEqual(version_hash_1, '+2nc5o2YRerVfAtItQBQ/6jVkkw=') + + # should return the same value again + self.assertEqual(version_hash_1, TransformerRegistry.get_write_version_hash()) + + # hash with TestTransformer1 and TestTransformer2 + with mock_registered_transformers([TestTransformer1, TestTransformer2]): + version_hash_2 = TransformerRegistry.get_write_version_hash() + self.assertEqual(version_hash_2, '5GwhvmSM9hknjUslzPnKDA5QaCo=') + self.assertNotEqual(version_hash_1, version_hash_2) diff --git a/openedx/core/lib/block_structure/tests/test_transformers.py b/openedx/core/lib/block_structure/tests/test_transformers.py index af68c00072..dda1adba25 100644 --- a/openedx/core/lib/block_structure/tests/test_transformers.py +++ b/openedx/core/lib/block_structure/tests/test_transformers.py @@ -59,7 +59,7 @@ class TestBlockStructureTransformers(ChildrenMapTestMixin, TestCase): with patch( 'openedx.core.lib.block_structure.tests.helpers.MockTransformer.collect' ) as mock_collect_call: - self.transformers.collect(block_structure=MagicMock()) + BlockStructureTransformers.collect(block_structure=MagicMock()) self.assertTrue(mock_collect_call.called) def test_transform(self): diff --git a/openedx/core/lib/block_structure/transformer_registry.py b/openedx/core/lib/block_structure/transformer_registry.py index 98547357b6..9c7b141f2c 100644 --- a/openedx/core/lib/block_structure/transformer_registry.py +++ b/openedx/core/lib/block_structure/transformer_registry.py @@ -2,7 +2,11 @@ Block Structure Transformer Registry implemented using the platform's PluginManager. """ +from base64 import b64encode +from hashlib import sha1 + from openedx.core.lib.api.plugins import PluginManager +from openedx.core.lib.cache_utils import memoized class TransformerRegistry(PluginManager): @@ -30,6 +34,22 @@ class TransformerRegistry(PluginManager): else: return set() + @classmethod + @memoized + def get_write_version_hash(cls): + """ + Returns a deterministic hash value of the WRITE_VERSION of all + registered transformers. + """ + hash_obj = sha1() + + sorted_transformers = sorted(cls.get_registered_transformers(), key=lambda t: t.name()) + for transformer in sorted_transformers: + hash_obj.update(transformer.name().encode('utf-8')) + hash_obj.update(str(transformer.WRITE_VERSION)) + + return b64encode(hash_obj.digest()) + @classmethod def find_unregistered(cls, transformers): """