Skip to content
Open
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
17 changes: 14 additions & 3 deletions src/opentimelineio/composition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,18 @@ Composition::_path_from_child(
ErrorStatus* error_status) const
{
auto current = child->parent();
std::vector<Composition*> parents{ current };
std::vector<Composition*> parents;

if (!current)
{
if (error_status)
{
*error_status = ErrorStatus::NOT_DESCENDED_FROM;
error_status->object_details = this;
}
return parents;
}
parents.push_back(current);

while (current != this)
{
Expand Down Expand Up @@ -311,7 +322,7 @@ Composition::range_of_child(Composable const* child, ErrorStatus* error_status)
const
{
auto parents = _path_from_child(child, error_status);
if (is_error(error_status))
if (parents.empty() || is_error(error_status))
{
return TimeRange();
}
Expand Down Expand Up @@ -363,7 +374,7 @@ Composition::trimmed_range_of_child(
ErrorStatus* error_status) const
{
auto parents = _path_from_child(child, error_status);
if (is_error(error_status))
if (parents.empty() || is_error(error_status))
{
return TimeRange();
}
Expand Down
20 changes: 14 additions & 6 deletions src/opentimelineio/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,14 @@ Item::visible_range(ErrorStatus* error_status) const
std::optional<TimeRange>
Item::trimmed_range_in_parent(ErrorStatus* error_status) const
{
if (!parent() && error_status)
if (!parent())
{
*error_status = ErrorStatus::NOT_A_CHILD;
error_status->object_details = this;
if (error_status)
{
*error_status = ErrorStatus::NOT_A_CHILD;
error_status->object_details = this;
}
return std::nullopt;
}

return parent()->trimmed_range_of_child(this, error_status);
Expand All @@ -99,10 +103,14 @@ Item::trimmed_range_in_parent(ErrorStatus* error_status) const
TimeRange
Item::range_in_parent(ErrorStatus* error_status) const
{
if (!parent() && error_status)
if (!parent())
{
*error_status = ErrorStatus::NOT_A_CHILD;
error_status->object_details = this;
if (error_status)
{
*error_status = ErrorStatus::NOT_A_CHILD;
error_status->object_details = this;
}
return TimeRange();
}

return parent()->range_of_child(this, error_status);
Expand Down
2 changes: 2 additions & 0 deletions src/opentimelineio/transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Transition::range_in_parent(ErrorStatus* error_status) const
"cannot compute range in parent because item has no parent",
this);
}
return std::nullopt;
}

return parent()->range_of_child(this, error_status);
Expand All @@ -84,6 +85,7 @@ Transition::trimmed_range_in_parent(ErrorStatus* error_status) const
"cannot compute trimmed range in parent because item has no parent",
this);
}
return std::nullopt;
}

return parent()->trimmed_range_of_child(this, error_status);
Expand Down
40 changes: 40 additions & 0 deletions tests/test_composition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,46 @@ main(int argc, char** argv)
assertEqual(items[0].value, clip.value);
});

tests.add_test("test_orphan_ranges_report_errors", [] {
SerializableObject::Retainer<Clip> clip = new Clip;
OTIO_NS::ErrorStatus error;

assertEqual(clip->range_in_parent(&error), TimeRange());
assertEqual(error.outcome, OTIO_NS::ErrorStatus::NOT_A_CHILD);

error = OTIO_NS::ErrorStatus();
assertFalse(clip->trimmed_range_in_parent(&error).has_value());
assertEqual(error.outcome, OTIO_NS::ErrorStatus::NOT_A_CHILD);
assertEqual(clip->range_in_parent(), TimeRange());
assertFalse(clip->trimmed_range_in_parent().has_value());

SerializableObject::Retainer<Transition> transition = new Transition;
error = OTIO_NS::ErrorStatus();
assertFalse(transition->range_in_parent(&error).has_value());
assertEqual(error.outcome, OTIO_NS::ErrorStatus::NOT_A_CHILD);

error = OTIO_NS::ErrorStatus();
assertFalse(transition->trimmed_range_in_parent(&error).has_value());
assertEqual(error.outcome, OTIO_NS::ErrorStatus::NOT_A_CHILD);
assertFalse(transition->range_in_parent().has_value());
assertFalse(transition->trimmed_range_in_parent().has_value());

SerializableObject::Retainer<Stack> stack = new Stack;
error = OTIO_NS::ErrorStatus();
assertEqual(stack->range_of_child(clip, &error), TimeRange());
assertEqual(error.outcome, OTIO_NS::ErrorStatus::NOT_DESCENDED_FROM);
assertEqual(stack->range_of_child(clip), TimeRange());
assertEqual(
stack->trimmed_range_of_child(clip).value(),
TimeRange());

error = OTIO_NS::ErrorStatus();
stack->trimmed_range_of_child(clip, &error);
assertEqual(
error.outcome,
OTIO_NS::ErrorStatus::NOT_DESCENDED_FROM);
});

tests.add_test("test_trimmed_range_of_nested_child", [] {
SerializableObject::Retainer<Stack> root = new Stack;
SerializableObject::Retainer<Track> track = new Track;
Expand Down
13 changes: 13 additions & 0 deletions tests/test_composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,9 @@ def test_range_of_child_with_duration(self):
with self.assertRaises(otio.exceptions.NotAChildError):
otio.schema.Clip().trimmed_range_in_parent()

with self.assertRaises(otio.exceptions.NotAChildError):
otio.schema.Clip().range_in_parent()

def test_transformed_time(self):
st = otio.schema.Stack(
name="foo",
Expand Down Expand Up @@ -1035,6 +1038,16 @@ def test_delete_parent_container(self):
del sq
self.assertIsNone(it.parent())

def test_orphan_is_not_descended_from_composition(self):
stack = otio.schema.Stack()
orphan = otio.schema.Clip()

with self.assertRaises(otio.exceptions.NotAChildError):
stack.range_of_child(orphan)

with self.assertRaises(otio.exceptions.NotAChildError):
stack.trimmed_range_of_child(orphan)

def test_transactional(self):
item = otio.core.Item()
trackA = otio.core.Track()
Expand Down
Loading