Skip to content

Commit

Permalink
Fix holes in IObservableVector<IInspectable> (#544)
Browse files Browse the repository at this point in the history
  • Loading branch information
oldnewthing authored Feb 29, 2020
1 parent e347c60 commit 7b70db8
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 23 deletions.
19 changes: 13 additions & 6 deletions strings/base_collections_vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,15 @@ namespace winrt::impl

bool IndexOf(Windows::Foundation::IInspectable const& value, uint32_t& index) const
{
return IndexOf(unbox_value<T>(value), index);
try
{
return IndexOf(unbox_value<T>(value), index);
}
catch (hresult_no_interface const&)
{
index = 0;
return false;
}
}

using base_type::GetMany;
Expand Down Expand Up @@ -167,16 +175,15 @@ namespace winrt::impl

void ReplaceAll(array_view<Windows::Foundation::IInspectable const> values)
{
this->increment_version();
m_values.clear();
m_values.reserve(values.size());
Container new_values;
new_values.reserve(values.size());

std::transform(values.begin(), values.end(), std::back_inserter(m_values), [&](auto && value)
std::transform(values.begin(), values.end(), std::back_inserter(new_values), [&](auto && value)
{
return unbox_value<T>(value);
});

this->call_changed(Windows::Foundation::Collections::CollectionChange::Reset, 0);
base_type::ReplaceAll(std::move(new_values));
}

using base_type::VectorChanged;
Expand Down
33 changes: 20 additions & 13 deletions strings/base_reference_produce.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,26 +288,33 @@ WINRT_EXPORT namespace winrt
{
return value.as<T>();
}
else if constexpr (std::is_enum_v<T>)
else
{
if (auto temp = value.try_as<Windows::Foundation::IReference<T>>())
if (!value)
{
return temp.Value();
throw hresult_no_interface();
}
else
if constexpr (std::is_enum_v<T>)
{
return static_cast<T>(value.as<Windows::Foundation::IReference<std::underlying_type_t<T>>>().Value());
if (auto temp = value.try_as<Windows::Foundation::IReference<T>>())
{
return temp.Value();
}
else
{
return static_cast<T>(value.as<Windows::Foundation::IReference<std::underlying_type_t<T>>>().Value());
}
}
}
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
else if constexpr (std::is_same_v<T, GUID>)
{
return value.as<Windows::Foundation::IReference<guid>>().Value();
}
else if constexpr (std::is_same_v<T, GUID>)
{
return value.as<Windows::Foundation::IReference<guid>>().Value();
}
#endif
else
{
return value.as<Windows::Foundation::IReference<T>>().Value();
else
{
return value.as<Windows::Foundation::IReference<T>>().Value();
}
}
}

Expand Down
68 changes: 64 additions & 4 deletions test/test/single_threaded_observable_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ TEST_CASE("single_threaded_observable_vector")
REQUIRE((vector_i.IndexOf(2, index) && index == 1));
index = 0;
REQUIRE((vector_o.IndexOf(box_value(2), index) && index == 1));

// A vector of integers never contains non-integers.
REQUIRE(!vector_o.IndexOf(nullptr, index));
REQUIRE(!vector_o.IndexOf(box_value(L"Not-an-integer"), index));
}
{
// GetView forwarding.
Expand All @@ -241,6 +245,10 @@ TEST_CASE("single_threaded_observable_vector")

REQUIRE(vector_i.GetAt(0) == 10);
REQUIRE(vector_i.GetAt(1) == 20);

// Can't put non-integers in a vector of integers.
REQUIRE_THROWS_AS(vector_o.SetAt(0, nullptr), hresult_no_interface);
REQUIRE_THROWS_AS(vector_o.SetAt(0, box_value(L"Not-an-integer")), hresult_no_interface);
}
{
// InsertAt forwarding.
Expand All @@ -258,6 +266,10 @@ TEST_CASE("single_threaded_observable_vector")
REQUIRE(vector_i.GetAt(1) == 2);
REQUIRE(vector_i.GetAt(2) == 3);
REQUIRE(vector_i.GetAt(3) == 4);

// Can't put non-integers in a vector of integers.
REQUIRE_THROWS_AS(vector_o.InsertAt(0, nullptr), hresult_no_interface);
REQUIRE_THROWS_AS(vector_o.InsertAt(0, box_value(L"Not-an-integer")), hresult_no_interface);
}
{
// Append forwarding.
Expand All @@ -273,6 +285,10 @@ TEST_CASE("single_threaded_observable_vector")
REQUIRE(vector_i.Size() == 2);
REQUIRE(vector_i.GetAt(0) == 1);
REQUIRE(vector_i.GetAt(1) == 2);

// Can't put non-integers in a vector of integers.
REQUIRE_THROWS_AS(vector_o.Append(nullptr), hresult_no_interface);
REQUIRE_THROWS_AS(vector_o.Append(box_value(L"Not-an-integer")), hresult_no_interface);
}
{
// GetMany boxing.
Expand Down Expand Up @@ -334,13 +350,13 @@ TEST_CASE("single_threaded_observable_vector")
bool changed_i{};
bool changed_o{};

vector_i.VectorChanged([&](IObservableVector<int> const& sender, IVectorChangedEventArgs const&)
auto token_i = vector_i.VectorChanged(auto_revoke, [&](IObservableVector<int> const& sender, IVectorChangedEventArgs const&)
{
changed_i = sender.GetAt(0) == 123;
REQUIRE(sender == vector_i);
});

vector_o.VectorChanged([&](IObservableVector<IInspectable> const& sender, IVectorChangedEventArgs const&)
auto token_o = vector_o.VectorChanged(auto_revoke, [&](IObservableVector<IInspectable> const& sender, IVectorChangedEventArgs const&)
{
changed_o = unbox_value<int>(sender.GetAt(0)) == 123;
REQUIRE(sender == vector_o);
Expand All @@ -357,6 +373,35 @@ TEST_CASE("single_threaded_observable_vector")
REQUIRE(vector_i.Size() == 1);
REQUIRE(vector_i.GetAt(0) == 123);
}
{
bool changed_i{};
bool changed_o{};

auto token_i = vector_i.VectorChanged(auto_revoke, [&](IObservableVector<int> const& sender, IVectorChangedEventArgs const&)
{
changed_i = true;
REQUIRE(sender == vector_i);
});

auto token_o = vector_o.VectorChanged(auto_revoke, [&](IObservableVector<IInspectable> const& sender, IVectorChangedEventArgs const&)
{
changed_i = true;
REQUIRE(sender == vector_o);
});

// Verify strong exception guarantee when replacing with non-integers.
REQUIRE_THROWS_AS(vector_o.ReplaceAll({ box_value(42), nullptr }), hresult_no_interface);
REQUIRE(!changed_i); // unchanged on failure
REQUIRE(!changed_o);
REQUIRE(vector_i.Size() == 1);
REQUIRE(vector_i.GetAt(0) == 123);

REQUIRE_THROWS_AS(vector_o.ReplaceAll({ box_value(L"Not-an-integer") }), hresult_no_interface);
REQUIRE(!changed_i); // unchanged on failure
REQUIRE(!changed_o);
REQUIRE(vector_i.Size() == 1);
REQUIRE(vector_i.GetAt(0) == 123);
}

{
IIterator<int> iterator_i = vector_i.First();
Expand All @@ -368,13 +413,13 @@ TEST_CASE("single_threaded_observable_vector")
bool changed_i{};
bool changed_o{};

vector_i.VectorChanged([&](IObservableVector<int> const& sender, IVectorChangedEventArgs const&)
auto token_i = vector_i.VectorChanged(auto_revoke, [&](IObservableVector<int> const& sender, IVectorChangedEventArgs const&)
{
changed_i = sender.GetAt(0) == 123;
REQUIRE(sender == vector_i);
});

vector_o.VectorChanged([&](IObservableVector<IInspectable> const& sender, IVectorChangedEventArgs const&)
auto token_o = vector_o.VectorChanged(auto_revoke, [&](IObservableVector<IInspectable> const& sender, IVectorChangedEventArgs const&)
{
changed_o = unbox_value<int>(sender.GetAt(0)) == 123;
REQUIRE(sender == vector_o);
Expand All @@ -392,4 +437,19 @@ TEST_CASE("single_threaded_observable_vector")
REQUIRE(vector_i.GetAt(0) == 123);
}
}

{
IObservableVector<IVector<int>> vector_i = single_threaded_observable_vector<IVector<int>>({ });
IObservableVector<IInspectable> vector_o = vector_i.as<IObservableVector<IInspectable>>();

// Verify that nulls are legal if the underlying type derives from IInspectable.
REQUIRE_NOTHROW(vector_o.Append(nullptr));
uint32_t index = 99;
REQUIRE(vector_o.IndexOf(nullptr, index));
REQUIRE(index == 0);

// Verify that type mismatch should report "not found" rather than
// finding the null entry.
REQUIRE(!vector_o.IndexOf(box_value(L"not-a-vector"), index));
}
}

0 comments on commit 7b70db8

Please sign in to comment.