Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
3a973d42
Commit
3a973d42
authored
Jan 09, 2015
by
Braden MacDonald
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Unit tests for library content analytics
parent
3973317a
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
186 additions
and
52 deletions
+186
-52
common/lib/xmodule/xmodule/modulestore/tests/utils.py
+1
-0
common/lib/xmodule/xmodule/tests/test_library_content.py
+185
-52
No files found.
common/lib/xmodule/xmodule/modulestore/tests/utils.py
View file @
3a973d42
...
@@ -119,6 +119,7 @@ class MixedSplitTestCase(TestCase):
...
@@ -119,6 +119,7 @@ class MixedSplitTestCase(TestCase):
extra
.
update
(
kwargs
)
extra
.
update
(
kwargs
)
return
ItemFactory
.
create
(
return
ItemFactory
.
create
(
category
=
category
,
category
=
category
,
parent
=
parent_block
,
parent_location
=
parent_block
.
location
,
parent_location
=
parent_block
.
location
,
modulestore
=
self
.
store
,
modulestore
=
self
.
store
,
**
extra
**
extra
...
...
common/lib/xmodule/xmodule/tests/test_library_content.py
View file @
3a973d42
...
@@ -5,22 +5,22 @@ Basic unit tests for LibraryContentModule
...
@@ -5,22 +5,22 @@ Basic unit tests for LibraryContentModule
Higher-level tests are in `cms/djangoapps/contentstore/tests/test_libraries.py`.
Higher-level tests are in `cms/djangoapps/contentstore/tests/test_libraries.py`.
"""
"""
from
bson.objectid
import
ObjectId
from
bson.objectid
import
ObjectId
from
mock
import
patch
from
mock
import
Mock
,
patch
from
opaque_keys.edx.locator
import
LibraryLocator
from
opaque_keys.edx.locator
import
LibraryLocator
from
unittest
import
TestCase
from
unittest
import
TestCase
from
xblock.fragment
import
Fragment
from
xblock.fragment
import
Fragment
from
xblock.runtime
import
Runtime
as
VanillaRuntime
from
xblock.runtime
import
Runtime
as
VanillaRuntime
from
xmodule.x_module
import
AUTHOR_VIEW
from
xmodule.library_content_module
import
(
from
xmodule.library_content_module
import
(
LibraryVersionReference
,
LibraryList
,
ANY_CAPA_TYPE_VALUE
,
LibraryContentDescriptor
LibraryVersionReference
,
LibraryList
,
ANY_CAPA_TYPE_VALUE
,
LibraryContentDescriptor
)
)
from
xmodule.modulestore.tests.factories
import
LibraryFactory
,
CourseFactory
,
ItemFactory
from
xmodule.library_tools
import
LibraryToolsService
from
xmodule.modulestore.tests.factories
import
LibraryFactory
,
CourseFactory
from
xmodule.modulestore.tests.utils
import
MixedSplitTestCase
from
xmodule.modulestore.tests.utils
import
MixedSplitTestCase
from
xmodule.tests
import
get_test_system
from
xmodule.tests
import
get_test_system
from
xmodule.validation
import
StudioValidationMessage
from
xmodule.validation
import
StudioValidationMessage
from
xmodule.x_module
import
AUTHOR_VIEW
dummy_render
=
lambda
block
,
_
:
Fragment
(
block
.
data
)
# pylint: disable=invalid-name
dummy_render
=
lambda
block
,
_
:
Fragment
(
block
.
data
)
# pylint: disable=invalid-name
...
@@ -32,46 +32,21 @@ class LibraryContentTest(MixedSplitTestCase):
...
@@ -32,46 +32,21 @@ class LibraryContentTest(MixedSplitTestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
LibraryContentTest
,
self
)
.
setUp
()
super
(
LibraryContentTest
,
self
)
.
setUp
()
self
.
tools
=
LibraryToolsService
(
self
.
store
)
self
.
library
=
LibraryFactory
.
create
(
modulestore
=
self
.
store
)
self
.
library
=
LibraryFactory
.
create
(
modulestore
=
self
.
store
)
self
.
lib_blocks
=
[
self
.
lib_blocks
=
[
ItemFactory
.
create
(
self
.
make_block
(
"html"
,
self
.
library
,
data
=
"Hello world from block {}"
.
format
(
i
))
category
=
"html"
,
parent_location
=
self
.
library
.
location
,
user_id
=
self
.
user_id
,
publish_item
=
False
,
metadata
=
{
"data"
:
"Hello world from block {}"
.
format
(
i
),
},
modulestore
=
self
.
store
,
)
for
i
in
range
(
1
,
5
)
for
i
in
range
(
1
,
5
)
]
]
self
.
course
=
CourseFactory
.
create
(
modulestore
=
self
.
store
)
self
.
course
=
CourseFactory
.
create
(
modulestore
=
self
.
store
)
self
.
chapter
=
ItemFactory
.
create
(
self
.
chapter
=
self
.
make_block
(
"chapter"
,
self
.
course
)
category
=
"chapter"
,
self
.
sequential
=
self
.
make_block
(
"sequential"
,
self
.
chapter
)
parent_location
=
self
.
course
.
location
,
self
.
vertical
=
self
.
make_block
(
"vertical"
,
self
.
sequential
)
user_id
=
self
.
user_id
,
self
.
lc_block
=
self
.
make_block
(
modulestore
=
self
.
store
,
"library_content"
,
)
self
.
vertical
,
self
.
sequential
=
ItemFactory
.
create
(
max_count
=
1
,
category
=
"sequential"
,
source_libraries
=
[
LibraryVersionReference
(
self
.
library
.
location
.
library_key
)]
parent_location
=
self
.
chapter
.
location
,
user_id
=
self
.
user_id
,
modulestore
=
self
.
store
,
)
self
.
vertical
=
ItemFactory
.
create
(
category
=
"vertical"
,
parent_location
=
self
.
sequential
.
location
,
user_id
=
self
.
user_id
,
modulestore
=
self
.
store
,
)
self
.
lc_block
=
ItemFactory
.
create
(
category
=
"library_content"
,
parent_location
=
self
.
vertical
.
location
,
user_id
=
self
.
user_id
,
modulestore
=
self
.
store
,
metadata
=
{
'max_count'
:
1
,
'source_libraries'
:
[
LibraryVersionReference
(
self
.
library
.
location
.
library_key
)]
}
)
)
def
_bind_course_module
(
self
,
module
):
def
_bind_course_module
(
self
,
module
):
...
@@ -80,6 +55,7 @@ class LibraryContentTest(MixedSplitTestCase):
...
@@ -80,6 +55,7 @@ class LibraryContentTest(MixedSplitTestCase):
"""
"""
module_system
=
get_test_system
(
course_id
=
self
.
course
.
location
.
course_key
)
module_system
=
get_test_system
(
course_id
=
self
.
course
.
location
.
course_key
)
module_system
.
descriptor_runtime
=
module
.
runtime
module_system
.
descriptor_runtime
=
module
.
runtime
module_system
.
_services
[
'library_tools'
]
=
self
.
tools
# pylint: disable=protected-access
def
get_module
(
descriptor
):
def
get_module
(
descriptor
):
"""Mocks module_system get_module function"""
"""Mocks module_system get_module function"""
...
@@ -92,6 +68,11 @@ class LibraryContentTest(MixedSplitTestCase):
...
@@ -92,6 +68,11 @@ class LibraryContentTest(MixedSplitTestCase):
module_system
.
get_module
=
get_module
module_system
.
get_module
=
get_module
module
.
xmodule_runtime
=
module_system
module
.
xmodule_runtime
=
module_system
class
TestLibraryContentModule
(
LibraryContentTest
):
"""
Basic unit tests for LibraryContentModule
"""
def
_get_capa_problem_type_xml
(
self
,
*
args
):
def
_get_capa_problem_type_xml
(
self
,
*
args
):
""" Helper function to create empty CAPA problem definition """
""" Helper function to create empty CAPA problem definition """
problem
=
"<problem>"
problem
=
"<problem>"
...
@@ -111,20 +92,8 @@ class LibraryContentTest(MixedSplitTestCase):
...
@@ -111,20 +92,8 @@ class LibraryContentTest(MixedSplitTestCase):
[
"coderesponse"
,
"optionresponse"
]
[
"coderesponse"
,
"optionresponse"
]
]
]
for
problem_type
in
problem_types
:
for
problem_type
in
problem_types
:
ItemFactory
.
create
(
self
.
make_block
(
"problem"
,
self
.
library
,
data
=
self
.
_get_capa_problem_type_xml
(
*
problem_type
))
category
=
"problem"
,
parent_location
=
self
.
library
.
location
,
user_id
=
self
.
user_id
,
publish_item
=
False
,
data
=
self
.
_get_capa_problem_type_xml
(
*
problem_type
),
modulestore
=
self
.
store
,
)
class
TestLibraryContentModule
(
LibraryContentTest
):
"""
Basic unit tests for LibraryContentModule
"""
def
test_lib_content_block
(
self
):
def
test_lib_content_block
(
self
):
"""
"""
Test that blocks from a library are copied and added as children
Test that blocks from a library are copied and added as children
...
@@ -338,3 +307,167 @@ class TestLibraryList(TestCase):
...
@@ -338,3 +307,167 @@ class TestLibraryList(TestCase):
lib_list
=
LibraryList
()
lib_list
=
LibraryList
()
with
self
.
assertRaises
(
ValueError
):
with
self
.
assertRaises
(
ValueError
):
lib_list
.
from_json
([
"Not-a-library-key,whatever"
])
lib_list
.
from_json
([
"Not-a-library-key,whatever"
])
class
TestLibraryContentAnalytics
(
LibraryContentTest
):
"""
Test analytics features of LibraryContentModule
"""
def
setUp
(
self
):
super
(
TestLibraryContentAnalytics
,
self
)
.
setUp
()
self
.
publisher
=
Mock
()
self
.
lc_block
.
refresh_children
()
self
.
lc_block
=
self
.
store
.
get_item
(
self
.
lc_block
.
location
)
self
.
_bind_course_module
(
self
.
lc_block
)
self
.
lc_block
.
xmodule_runtime
.
publish
=
self
.
publisher
def
_assert_event_was_published
(
self
,
event_type
):
"""
Check that a LibraryContentModule analytics event was published by self.lc_block.
"""
self
.
assertTrue
(
self
.
publisher
.
called
)
self
.
assertTrue
(
len
(
self
.
publisher
.
call_args
[
0
]),
3
)
_
,
event_name
,
event_data
=
self
.
publisher
.
call_args
[
0
]
self
.
assertEqual
(
event_name
,
"edx.librarycontentblock.content.{}"
.
format
(
event_type
))
self
.
assertEqual
(
event_data
[
"location"
],
unicode
(
self
.
lc_block
.
location
))
return
event_data
def
test_assigned_event
(
self
):
"""
Test the "assigned" event emitted when a student is assigned specific blocks.
"""
# In the beginning was the lc_block and it assigned one child to the student:
child
=
self
.
lc_block
.
get_child_descriptors
()[
0
]
child_lib_location
,
child_lib_version
=
self
.
store
.
get_block_original_usage
(
child
.
location
)
self
.
assertIsInstance
(
child_lib_version
,
ObjectId
)
event_data
=
self
.
_assert_event_was_published
(
"assigned"
)
block_info
=
{
"usage_key"
:
unicode
(
child
.
location
),
"original_usage_key"
:
unicode
(
child_lib_location
),
"original_usage_version"
:
unicode
(
child_lib_version
),
"descendants"
:
[],
}
self
.
assertEqual
(
event_data
,
{
"location"
:
unicode
(
self
.
lc_block
.
location
),
"added"
:
[
block_info
],
"result"
:
[
block_info
],
})
self
.
publisher
.
reset_mock
()
# Now increase max_count so that one more child will be added:
self
.
lc_block
.
max_count
=
2
del
self
.
lc_block
.
_xmodule
.
_selected_set
# Clear the cache (only needed because we skip saving/re-loading the block) pylint: disable=protected-access
children
=
self
.
lc_block
.
get_child_descriptors
()
self
.
assertEqual
(
len
(
children
),
2
)
child
,
new_child
=
children
if
children
[
0
]
.
location
==
child
.
location
else
reversed
(
children
)
event_data
=
self
.
_assert_event_was_published
(
"assigned"
)
self
.
assertEqual
(
event_data
[
"added"
][
0
][
"usage_key"
],
unicode
(
new_child
.
location
))
self
.
assertEqual
(
len
(
event_data
[
"result"
]),
2
)
def
test_assigned_descendants
(
self
):
"""
Test the "assigned" event emitted includes descendant block information.
"""
# Replace the blocks in the library with a block that has descendants:
with
self
.
store
.
bulk_operations
(
self
.
library
.
location
.
library_key
):
self
.
library
.
children
=
[]
main_vertical
=
self
.
make_block
(
"vertical"
,
self
.
library
)
inner_vertical
=
self
.
make_block
(
"vertical"
,
main_vertical
)
html_block
=
self
.
make_block
(
"html"
,
inner_vertical
)
problem_block
=
self
.
make_block
(
"problem"
,
inner_vertical
)
self
.
lc_block
.
refresh_children
()
# Reload lc_block and set it up for a student:
self
.
lc_block
=
self
.
store
.
get_item
(
self
.
lc_block
.
location
)
self
.
_bind_course_module
(
self
.
lc_block
)
self
.
lc_block
.
xmodule_runtime
.
publish
=
self
.
publisher
# Get the keys of each of our blocks, as they appear in the course:
course_usage_main_vertical
=
self
.
lc_block
.
children
[
0
]
course_usage_inner_vertical
=
self
.
store
.
get_item
(
course_usage_main_vertical
)
.
children
[
0
]
inner_vertical_in_course
=
self
.
store
.
get_item
(
course_usage_inner_vertical
)
course_usage_html
=
inner_vertical_in_course
.
children
[
0
]
course_usage_problem
=
inner_vertical_in_course
.
children
[
1
]
# Trigger a publish event:
self
.
lc_block
.
get_child_descriptors
()
event_data
=
self
.
_assert_event_was_published
(
"assigned"
)
for
block_list
in
(
event_data
[
"added"
],
event_data
[
"result"
]):
self
.
assertEqual
(
len
(
block_list
),
1
)
# The main_vertical is the only root block added, and is the only result.
self
.
assertEqual
(
block_list
[
0
][
"usage_key"
],
unicode
(
course_usage_main_vertical
))
# Check that "descendants" is a flat, unordered list of all of main_vertical's descendants:
descendants_expected
=
{}
for
lib_key
,
course_usage_key
in
(
(
inner_vertical
.
location
,
course_usage_inner_vertical
),
(
html_block
.
location
,
course_usage_html
),
(
problem_block
.
location
,
course_usage_problem
),
):
descendants_expected
[
unicode
(
course_usage_key
)]
=
{
"usage_key"
:
unicode
(
course_usage_key
),
"original_usage_key"
:
unicode
(
lib_key
),
"original_usage_version"
:
unicode
(
self
.
store
.
get_block_original_usage
(
course_usage_key
)[
1
]),
}
self
.
assertEqual
(
len
(
block_list
[
0
][
"descendants"
]),
len
(
descendants_expected
))
for
descendant
in
block_list
[
0
][
"descendants"
]:
self
.
assertEqual
(
descendant
,
descendants_expected
.
get
(
descendant
[
"usage_key"
]))
def
test_removed_overlimit
(
self
):
"""
Test the "removed" event emitted when we un-assign blocks previously assigned to a student.
We go from one blocks assigned to none because max_count has been decreased.
"""
# Decrease max_count to 1, causing the block to be overlimit:
self
.
lc_block
.
get_child_descriptors
()
# We must call an XModule method before we can change max_count - otherwise the change has no effect
self
.
publisher
.
reset_mock
()
# Clear the "assigned" event that was just published.
self
.
lc_block
.
max_count
=
0
del
self
.
lc_block
.
_xmodule
.
_selected_set
# Clear the cache (only needed because we skip saving/re-loading the block) pylint: disable=protected-access
# Check that the event says that one block was removed, leaving no blocks left:
children
=
self
.
lc_block
.
get_child_descriptors
()
self
.
assertEqual
(
len
(
children
),
0
)
event_data
=
self
.
_assert_event_was_published
(
"removed"
)
self
.
assertEqual
(
len
(
event_data
[
"removed"
]),
1
)
self
.
assertEqual
(
event_data
[
"result"
],
[])
self
.
assertEqual
(
event_data
[
"reason"
],
"overlimit"
)
def
test_removed_invalid
(
self
):
"""
Test the "removed" event emitted when we un-assign blocks previously assigned to a student.
We go from two blocks assigned, to one because the others have been deleted from the library.
"""
# Start by assigning two blocks to the student:
self
.
lc_block
.
get_child_descriptors
()
# We must call an XModule method before we can change max_count - otherwise the change has no effect
self
.
lc_block
.
max_count
=
2
del
self
.
lc_block
.
_xmodule
.
_selected_set
# Clear the cache (only needed because we skip saving/re-loading the block) pylint: disable=protected-access
initial_blocks_assigned
=
self
.
lc_block
.
get_child_descriptors
()
self
.
assertEqual
(
len
(
initial_blocks_assigned
),
2
)
self
.
publisher
.
reset_mock
()
# Clear the "assigned" event that was just published.
# Now make sure that one of the assigned blocks will have to be un-assigned.
# To cause an "invalid" event, we delete all blocks from the content library except for one of the two already assigned to the student:
keep_block_key
=
initial_blocks_assigned
[
0
]
.
location
keep_block_lib_usage_key
,
keep_block_lib_version
=
self
.
store
.
get_block_original_usage
(
keep_block_key
)
deleted_block_key
=
initial_blocks_assigned
[
1
]
.
location
self
.
library
.
children
=
[
keep_block_lib_usage_key
]
self
.
store
.
update_item
(
self
.
library
,
self
.
user_id
)
self
.
lc_block
.
refresh_children
()
del
self
.
lc_block
.
_xmodule
.
_selected_set
# Clear the cache (only needed because we skip saving/re-loading the block) pylint: disable=protected-access
# Check that the event says that one block was removed, leaving one block left:
children
=
self
.
lc_block
.
get_child_descriptors
()
self
.
assertEqual
(
len
(
children
),
1
)
event_data
=
self
.
_assert_event_was_published
(
"removed"
)
self
.
assertEqual
(
event_data
[
"removed"
],
[{
"usage_key"
:
unicode
(
deleted_block_key
),
"original_usage_key"
:
None
,
# Note: original_usage_key info is sadly unavailable because the block has been deleted so that info can no longer be retrieved
"original_usage_version"
:
None
,
"descendants"
:
[],
}])
self
.
assertEqual
(
event_data
[
"result"
],
[{
"usage_key"
:
unicode
(
keep_block_key
),
"original_usage_key"
:
unicode
(
keep_block_lib_usage_key
),
"original_usage_version"
:
unicode
(
keep_block_lib_version
),
"descendants"
:
[],
}])
self
.
assertEqual
(
event_data
[
"reason"
],
"invalid"
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment