199 lines
7.7 KiB
Python
199 lines
7.7 KiB
Python
# coding=utf-8
|
||
"""
|
||
Tests capa util
|
||
"""
|
||
|
||
|
||
import unittest
|
||
|
||
import codejail.safe_exec
|
||
import ddt
|
||
from django.test.utils import TestContextDecorator
|
||
from lxml import etree
|
||
|
||
from xmodule.capa.tests.helpers import mock_capa_system
|
||
from xmodule.capa.util import (
|
||
compare_with_tolerance,
|
||
contextualize_text,
|
||
get_inner_html_from_xpath,
|
||
remove_markup,
|
||
sanitize_html,
|
||
)
|
||
|
||
|
||
@ddt.ddt
|
||
class UtilTest(unittest.TestCase):
|
||
"""Tests for util"""
|
||
|
||
def setUp(self):
|
||
super().setUp()
|
||
self.system = mock_capa_system()
|
||
|
||
def test_compare_with_tolerance(self): # pylint: disable=too-many-statements
|
||
"""Test numeric comparison with relative and absolute tolerances."""
|
||
# Test default tolerance '0.001%' (it is relative)
|
||
result = compare_with_tolerance(100.0, 100.0)
|
||
assert result
|
||
result = compare_with_tolerance(100.001, 100.0)
|
||
assert result
|
||
result = compare_with_tolerance(101.0, 100.0)
|
||
assert not result
|
||
# Test absolute percentage tolerance
|
||
result = compare_with_tolerance(109.9, 100.0, "10%", False)
|
||
assert result
|
||
result = compare_with_tolerance(110.1, 100.0, "10%", False)
|
||
assert not result
|
||
# Test relative percentage tolerance
|
||
result = compare_with_tolerance(111.0, 100.0, "10%", True)
|
||
assert result
|
||
result = compare_with_tolerance(112.0, 100.0, "10%", True)
|
||
assert not result
|
||
# Test absolute tolerance (string)
|
||
result = compare_with_tolerance(109.9, 100.0, "10.0", False)
|
||
assert result
|
||
result = compare_with_tolerance(110.1, 100.0, "10.0", False)
|
||
assert not result
|
||
# Test relative tolerance (string)
|
||
result = compare_with_tolerance(111.0, 100.0, "0.1", True)
|
||
assert result
|
||
result = compare_with_tolerance(112.0, 100.0, "0.1", True)
|
||
assert not result
|
||
# Test absolute tolerance (float)
|
||
result = compare_with_tolerance(109.9, 100.0, 10.0, False)
|
||
assert result
|
||
result = compare_with_tolerance(110.1, 100.0, 10.0, False)
|
||
assert not result
|
||
# Test relative tolerance (float)
|
||
result = compare_with_tolerance(111.0, 100.0, 0.1, True)
|
||
assert result
|
||
result = compare_with_tolerance(112.0, 100.0, 0.1, True)
|
||
assert not result
|
||
# Infinite values #
|
||
infinity = float("Inf")
|
||
# Test relative tolerance (float)
|
||
result = compare_with_tolerance(infinity, 100.0, 1.0, True)
|
||
assert not result
|
||
result = compare_with_tolerance(100.0, infinity, 1.0, True)
|
||
assert not result
|
||
result = compare_with_tolerance(infinity, infinity, 1.0, True)
|
||
assert result
|
||
# Test absolute tolerance (float)
|
||
result = compare_with_tolerance(infinity, 100.0, 1.0, False)
|
||
assert not result
|
||
result = compare_with_tolerance(100.0, infinity, 1.0, False)
|
||
assert not result
|
||
result = compare_with_tolerance(infinity, infinity, 1.0, False)
|
||
assert result
|
||
# Test relative tolerance (string)
|
||
result = compare_with_tolerance(infinity, 100.0, "1.0", True)
|
||
assert not result
|
||
result = compare_with_tolerance(100.0, infinity, "1.0", True)
|
||
assert not result
|
||
result = compare_with_tolerance(infinity, infinity, "1.0", True)
|
||
assert result
|
||
# Test absolute tolerance (string)
|
||
result = compare_with_tolerance(infinity, 100.0, "1.0", False)
|
||
assert not result
|
||
result = compare_with_tolerance(100.0, infinity, "1.0", False)
|
||
assert not result
|
||
result = compare_with_tolerance(infinity, infinity, "1.0", False)
|
||
assert result
|
||
# Test absolute tolerance for smaller values
|
||
result = compare_with_tolerance(100.01, 100.0, 0.01, False)
|
||
assert result
|
||
result = compare_with_tolerance(100.001, 100.0, 0.001, False)
|
||
assert result
|
||
result = compare_with_tolerance(100.01, 100.0, "0.01%", False)
|
||
assert result
|
||
result = compare_with_tolerance(100.002, 100.0, 0.001, False)
|
||
assert not result
|
||
result = compare_with_tolerance(0.4, 0.44, 0.01, False)
|
||
assert not result
|
||
result = compare_with_tolerance(100.01, 100.0, 0.010, False)
|
||
assert result
|
||
|
||
# Test complex_number instructor_complex
|
||
result = compare_with_tolerance(0.4, complex(0.44, 0), 0.01, False)
|
||
assert not result
|
||
result = compare_with_tolerance(100.01, complex(100.0, 0), 0.010, False)
|
||
assert result
|
||
result = compare_with_tolerance(110.1, complex(100.0, 0), "10.0", False)
|
||
assert not result
|
||
result = compare_with_tolerance(111.0, complex(100.0, 0), "10%", True)
|
||
assert result
|
||
|
||
def test_sanitize_html(self):
|
||
"""
|
||
Test for html sanitization with nh3.
|
||
"""
|
||
allowed_tags = ["div", "p", "audio", "pre", "span"]
|
||
for tag in allowed_tags:
|
||
queue_msg = f"<{tag}>Test message</{tag}>"
|
||
assert sanitize_html(queue_msg) == queue_msg
|
||
|
||
not_allowed_tag = "script"
|
||
queue_msg = f"<{not_allowed_tag}>Test message</{not_allowed_tag}>"
|
||
expected = ""
|
||
assert sanitize_html(queue_msg) == expected
|
||
|
||
def test_get_inner_html_from_xpath(self):
|
||
"""
|
||
Test for getting inner html as string from xpath node.
|
||
"""
|
||
xpath_node = etree.XML('<hint style="smtng">aa<a href="#">bb</a>cc</hint>')
|
||
assert get_inner_html_from_xpath(xpath_node) == 'aa<a href="#">bb</a>cc'
|
||
|
||
def test_remove_markup(self):
|
||
"""
|
||
Test for markup removal with nh3.
|
||
"""
|
||
assert (
|
||
remove_markup("The <mark>Truth</mark> is <em>Out There</em> & you need to <strong>find</strong> it")
|
||
== "The Truth is Out There & you need to find it"
|
||
)
|
||
|
||
@ddt.data("When the root level failš the whole hierarchy won’t work anymore.", "あなたあなたあなた")
|
||
def test_contextualize_text(self, context_value):
|
||
"""Verify that variable substitution works as intended with non-ascii characters."""
|
||
key = "answer0"
|
||
text = "$answer0"
|
||
context = {key: context_value}
|
||
contextual_text = contextualize_text(text, context)
|
||
assert context_value == contextual_text
|
||
|
||
def test_contextualize_text_with_non_ascii_context(self):
|
||
"""Verify that variable substitution works as intended with non-ascii characters."""
|
||
key = "あなた$a $b"
|
||
text = "$" + key
|
||
context = {"a": "あなたあなたあなた", "b": "あなたhi"}
|
||
expected_text = "$あなたあなたあなたあなた あなたhi"
|
||
contextual_text = contextualize_text(text, context)
|
||
assert expected_text == contextual_text
|
||
|
||
|
||
class UseUnsafeCodejail(TestContextDecorator):
|
||
"""
|
||
Tell codejail to run in unsafe mode for the scope of the decorator.
|
||
Use this as a decorator on Django TestCase classes or methods.
|
||
|
||
This is needed because codejail has significant OS-level setup requirements
|
||
which we don't even attempt to fulfill for unit testing purposes. Running
|
||
tests in unsafe mode (that is, running code executions in-process, with no
|
||
sandboxing) is only safe because we control the contents of the unit tests.
|
||
It's not a perfect replica of how safe mode operates but it's generally good
|
||
enough for testing the integration and overall behavior.
|
||
"""
|
||
|
||
def __init__(self):
|
||
self.old_be_unsafe = None
|
||
super().__init__()
|
||
|
||
def enable(self):
|
||
"""Enable unsafe mode for codejail within the test scope."""
|
||
self.old_be_unsafe = codejail.safe_exec.ALWAYS_BE_UNSAFE
|
||
codejail.safe_exec.ALWAYS_BE_UNSAFE = True
|
||
|
||
def disable(self):
|
||
"""Restore the previous codejail unsafe mode state."""
|
||
codejail.safe_exec.ALWAYS_BE_UNSAFE = self.old_be_unsafe
|