diff --git a/le_utils/constants/exercises.py b/le_utils/constants/exercises.py index 3b88b77..9ad30ad 100644 --- a/le_utils/constants/exercises.py +++ b/le_utils/constants/exercises.py @@ -39,6 +39,7 @@ SINGLE_SELECTION = "single_selection" FREE_RESPONSE = "free_response" PERSEUS_QUESTION = "perseus_question" +QTI = "QTI" question_choices = ( (INPUT_QUESTION, "Input Question"), @@ -46,4 +47,5 @@ (SINGLE_SELECTION, "Single Selection"), (FREE_RESPONSE, "Free Response"), (PERSEUS_QUESTION, "Perseus Question"), + (QTI, "QTI"), ) diff --git a/le_utils/constants/format_presets.py b/le_utils/constants/format_presets.py index decfe52..bf4a7b3 100644 --- a/le_utils/constants/format_presets.py +++ b/le_utils/constants/format_presets.py @@ -159,3 +159,10 @@ def _initialize_preset_list(): PRESETLIST = list(_initialize_preset_list()) + +# Bit order for the File.included_presets renderable bitmask. +# Consumers compute the bit value as 2 ** RENDERABLE_PRESETS_ORDER.index(preset_id). +# APPEND-ONLY: never reorder or remove an existing entry — that would +# silently change the bit value of every preset after it and corrupt +# already-persisted bitmasks. Only append new preset ids at the end. +RENDERABLE_PRESETS_ORDER = [preset.id for preset in PRESETLIST if not preset.supplementary] diff --git a/tests/test_exercises.py b/tests/test_exercises.py new file mode 100644 index 0000000..7630b76 --- /dev/null +++ b/tests/test_exercises.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from le_utils.constants import exercises + + +def test_QTI_is_in_question_choices(): + assert exercises.QTI == "QTI" + assert (exercises.QTI, "QTI") in exercises.question_choices diff --git a/tests/test_presets.py b/tests/test_presets.py index ddbe93c..edcf30d 100644 --- a/tests/test_presets.py +++ b/tests/test_presets.py @@ -16,3 +16,23 @@ def test_format_presets_are_synced(): def test_PRESETLIST_exists(): assert format_presets.PRESETLIST, "PRESETLIST did not genereate properly" + + +def test_RENDERABLE_PRESETS_ORDER_matches_PRESETLIST_ids(): + preset_ids = {preset.id for preset in format_presets.PRESETLIST if not preset.supplementary} + assert set(format_presets.RENDERABLE_PRESETS_ORDER) == preset_ids + + +def test_RENDERABLE_PRESETS_ORDER_excludes_supplementary_presets(): + assert "video_thumbnail" not in format_presets.RENDERABLE_PRESETS_ORDER + assert "qti_thumbnail" not in format_presets.RENDERABLE_PRESETS_ORDER + + +def test_RENDERABLE_PRESETS_ORDER_index_is_stable(): + # Pin known bit positions: reordering silently breaks Studio/Kolibri's + # already-persisted File.included_presets bitmask values. + assert format_presets.RENDERABLE_PRESETS_ORDER.index("high_res_video") == 0 + assert format_presets.RENDERABLE_PRESETS_ORDER.index("low_res_video") == 1 + assert format_presets.RENDERABLE_PRESETS_ORDER.index("video_dependency") == 2 + assert format_presets.RENDERABLE_PRESETS_ORDER.index("qti") == 13 + assert format_presets.RENDERABLE_PRESETS_ORDER.index("kpub") == 17