Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip cell id test #1

Merged
merged 1 commit into from
Mar 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 43 additions & 37 deletions nbdime/tests/test_merge_notebooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import nbformat

from nbdime.diff_format import op_patch, op_addrange, op_removerange, op_replace
from .utils import sources_to_notebook, outputs_to_notebook, have_git
from .utils import sources_to_notebook, outputs_to_notebook, have_git, strip_cell_ids, new_cell_wo_id
from nbdime.nbmergeapp import _build_arg_parser
from nbdime import merge_notebooks, apply_decisions
from nbdime.diffing.notebooks import diff_notebooks, set_notebook_diff_targets
Expand Down Expand Up @@ -56,15 +56,15 @@ def test_autoresolve_notebook_ec():


def test_merge_cell_sources_neighbouring_inserts():
base = sources_to_notebook([[
base = strip_cell_ids(sources_to_notebook([[
"def f(x):",
" return x**2",
], [
"def g(y):",
" return y + 2",
],
])
local = sources_to_notebook([[
]))
local = strip_cell_ids(sources_to_notebook([[
"def f(x):",
" return x**2",
], [
Expand All @@ -73,8 +73,8 @@ def test_merge_cell_sources_neighbouring_inserts():
"def g(y):",
" return y + 2",
],
])
remote = sources_to_notebook([[
]))
remote = strip_cell_ids(sources_to_notebook([[
"def f(x):",
" return x**2",
], [
Expand All @@ -83,7 +83,7 @@ def test_merge_cell_sources_neighbouring_inserts():
"def g(y):",
" return y + 2",
],
])
]))
if 1:
expected_partial = base
expected_conflicts = [{
Expand Down Expand Up @@ -114,15 +114,15 @@ def test_merge_cell_sources_neighbouring_inserts():


def test_merge_cell_sources_separate_inserts():
base = sources_to_notebook([[
base = strip_cell_ids(sources_to_notebook([[
"def f(x):",
" return x**2",
], [
"def g(y):",
" return y + 2",
],
])
local = sources_to_notebook([[
]))
local = strip_cell_ids(sources_to_notebook([[
"print(f(3))",
], [
"def f(x):",
Expand All @@ -131,8 +131,8 @@ def test_merge_cell_sources_separate_inserts():
"def g(y):",
" return y + 2",
],
])
remote = sources_to_notebook([[
]))
remote = strip_cell_ids(sources_to_notebook([[
"def f(x):",
" return x**2",
], [
Expand All @@ -141,8 +141,8 @@ def test_merge_cell_sources_separate_inserts():
], [
"print(f(7))",
],
])
expected = sources_to_notebook([[
]))
expected = strip_cell_ids(sources_to_notebook([[
"print(f(3))",
], [
"def f(x):",
Expand All @@ -153,13 +153,13 @@ def test_merge_cell_sources_separate_inserts():
], [
"print(f(7))",
],
])
]))
actual, decisions = merge_notebooks(base, local, remote, args)
assert not any([d.conflict for d in decisions])
assert actual == expected


def src2nb(src):
def src2nb(src, strip_ids=False):
"""Convert source strings to a notebook.

src is either a single multiline string to become one cell,
Expand All @@ -171,6 +171,8 @@ def src2nb(src):
src = sources_to_notebook(src)
assert isinstance(src, dict)
assert "cells" in src
if strip_ids:
strip_cell_ids(src)
return src


Expand All @@ -193,23 +195,23 @@ def _check(partial, expected_partial, decisions, expected_conflicts):
assert d[k] == e[k]


def _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args=None):
base = src2nb(base)
local = src2nb(local)
remote = src2nb(remote)
expected_partial = src2nb(expected_partial)
def _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args=None, ignore_cell_ids=False):
base = src2nb(base, strip_ids=ignore_cell_ids)
local = src2nb(local, strip_ids=ignore_cell_ids)
remote = src2nb(remote, strip_ids=ignore_cell_ids)
expected_partial = src2nb(expected_partial, strip_ids=ignore_cell_ids)
merge_args = merge_args or args

partial, decisions = merge_notebooks(base, local, remote, merge_args)

_check(partial, expected_partial, decisions, expected_conflicts)


def _check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args=None):
base = outputs_to_notebook(base)
local = outputs_to_notebook(local)
remote = outputs_to_notebook(remote)
expected_partial = outputs_to_notebook(expected_partial)
def _check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args=None, ignore_cell_ids=False):
base = outputs_to_notebook(base, strip_ids=ignore_cell_ids)
local = outputs_to_notebook(local, strip_ids=ignore_cell_ids)
remote = outputs_to_notebook(remote, strip_ids=ignore_cell_ids)
expected_partial = outputs_to_notebook(expected_partial, strip_ids=ignore_cell_ids)
merge_args = merge_args or args

partial, decisions = merge_notebooks(base, local, remote, merge_args)
Expand Down Expand Up @@ -293,10 +295,10 @@ def test_merge_simple_cell_source_conflicting_insert():
expected_conflicts = [{
"common_path": ("cells",),
"local_diff": [op_addrange(
1, [nbformat.v4.new_code_cell(local[1][0])]),
1, [new_cell_wo_id(local[1][0])]),
],
"remote_diff": [op_addrange(
1, [nbformat.v4.new_code_cell(remote[1][0])]),
1, [new_cell_wo_id(remote[1][0])]),
]
}]
else: # Treat as non-conflict (insert both)
Expand All @@ -306,7 +308,7 @@ def test_merge_simple_cell_source_conflicting_insert():
merge_args = copy.deepcopy(args)
merge_args.merge_strategy = "mergetool"

_check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


@pytest.mark.xfail
Expand Down Expand Up @@ -381,15 +383,15 @@ def test_merge_insert_cells_around_conflicting_cell():
expected_conflicts = [{
"common_path": ("cells",),
"local_diff": [
op_addrange(0, [nbformat.v4.new_code_cell(
op_addrange(0, [new_cell_wo_id(
source=["new local cell"])]),
op_patch(0, [op_patch("source", [
op_addrange(len("".join(source)), "local\n")])]),
],
"remote_diff": [op_patch(0, [op_patch('source', [
op_addrange(len("".join(source)), "remote\n")])])]
}]
_check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


@pytest.mark.xfail
Expand Down Expand Up @@ -669,7 +671,7 @@ def test_merge_output_strategy_local_conflict():
expected_conflicts = []
merge_args = copy.deepcopy(args)
merge_args.output_strategy = "use-local"
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


def test_merge_output_strategy_remote_conflict():
Expand All @@ -681,7 +683,7 @@ def test_merge_output_strategy_remote_conflict():
expected_conflicts = []
merge_args = copy.deepcopy(args)
merge_args.output_strategy = "use-remote"
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


def test_merge_output_strategy_base_conflict():
Expand All @@ -693,7 +695,7 @@ def test_merge_output_strategy_base_conflict():
expected_conflicts = []
merge_args = copy.deepcopy(args)
merge_args.output_strategy = "use-base"
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


@pytest.mark.skip
Expand All @@ -706,7 +708,7 @@ def test_merge_output_strategy_union_conflict():
expected_conflicts = []
merge_args = copy.deepcopy(args)
merge_args.output_strategy = "union"
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


def test_merge_output_strategy_clear_conflict():
Expand All @@ -718,7 +720,7 @@ def test_merge_output_strategy_clear_conflict():
expected_conflicts = []
merge_args = copy.deepcopy(args)
merge_args.output_strategy = "remove"
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


def test_merge_output_strategy_clear_all_conflict():
Expand All @@ -730,7 +732,7 @@ def test_merge_output_strategy_clear_all_conflict():
expected_conflicts = []
merge_args = copy.deepcopy(args)
merge_args.output_strategy = "clear-all"
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args)
_check_outputs(base, local, remote, expected_partial, expected_conflicts, merge_args, True)


# TODO: Make test for output_strategy == 'inline'
Expand Down Expand Up @@ -852,6 +854,10 @@ def test_autoresolve_empty_strategies():
base, local, remote, expected_partial = _make_notebook_with_multi_conflicts(
expected_partial_source, expected_partial_metadata, expected_partial_outputs
)
strip_cell_ids(base)
strip_cell_ids(local)
strip_cell_ids(remote)
strip_cell_ids(expected_partial)

expected_conflicts = [
{
Expand Down
44 changes: 35 additions & 9 deletions nbdime/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@
TEST_TOKEN = 'nbdime-test-token'


@contextmanager
def random_seed(a=0):
import random
old_state = random.getstate()
random.seed(a)
yield
random.setstate(old_state)


def assert_is_valid_notebook(nb):
"""These are the current assumptions on notebooks in these tests. Loosen on demand."""
assert nb["nbformat"] == 4
Expand All @@ -60,16 +69,17 @@ def check_symmetric_diff_and_patch(a, b):
check_diff_and_patch(b, a)


def sources_to_notebook(sources, cell_type='code'):
def sources_to_notebook(sources, cell_type='code', id_seed=0):
assert isinstance(sources, list)
nb = nbformat.v4.new_notebook()
for source in sources:
if isinstance(source, list):
source = "".join(source)
if cell_type == 'code':
nb.cells.append(nbformat.v4.new_code_cell(source))
elif cell_type == 'markdown':
nb.cells.append(nbformat.v4.new_markdown_cell(source))
with random_seed(id_seed):
for source in sources:
if isinstance(source, list):
source = "".join(source)
if cell_type == 'code':
nb.cells.append(nbformat.v4.new_code_cell(source))
elif cell_type == 'markdown':
nb.cells.append(nbformat.v4.new_markdown_cell(source))
return nb


Expand All @@ -85,7 +95,7 @@ def notebook_to_sources(nb, as_str=True):
return sources


def outputs_to_notebook(outputs):
def outputs_to_notebook(outputs, strip_ids=False):
assert isinstance(outputs, list)
assert len(outputs) == 0 or isinstance(outputs[0], list)
nb = nbformat.v4.new_notebook()
Expand All @@ -102,9 +112,25 @@ def outputs_to_notebook(outputs):
)
assert isinstance(output, dict)
cell.outputs.append(output)
if strip_ids:
strip_cell_ids(nb)
return nb


def strip_cell_ids(nb):
for cell in nb["cells"]:
if "id" in cell:
del cell["id"]
return nb


def new_cell_wo_id(*args, **kwargs):
cell = nbformat.v4.new_code_cell(*args, **kwargs)
if "id" in cell:
del cell["id"]
return cell


@contextmanager
def assert_clean_exit():
"""Assert that SystemExit is called with code=0"""
Expand Down