/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2015 Microsoft Corporation. All rights reserved. // // This code is licensed under the MIT License (MIT). // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Guide; namespace { void use(int&) {} struct BaseClass {}; struct DerivedClass : BaseClass {}; } SUITE(array_view_tests) { TEST(basics) { auto ptr = as_array_view(new int[10], 10); fill(ptr.begin(), ptr.end(), 99); for (int num : ptr) { CHECK(num == 99); } delete[] ptr.data(); static_bounds bounds{ 3 }; #ifdef CONFIRM_COMPILATION_ERRORS array_view av(nullptr, bounds); av.extent(); av.extent<2>(); av[8][4][3]; #endif } TEST (array_view_convertible) { #ifdef CONFIRM_COMPILATION_ERRORS array_view av1(nullptr, b1); #endif auto f = [&]() { array_view av1(nullptr); }; CHECK_THROW(f(), fail_fast); array_view av1(nullptr); #ifdef CONFIRM_COMPILATION_ERRORS static_bounds b12(b11); b12 = b11; b11 = b12; array_view av1 = nullptr; array_view av2(av1); array_view av2(av1); #endif array_view avd; #ifdef CONFIRM_COMPILATION_ERRORS array_view avb = avd; #endif array_view avcd = avd; } TEST(boundary_checks) { int arr[10][2]; auto av = as_array_view(arr); fill(begin(av), end(av), 0); av[2][0] = 1; av[1][1] = 3; // out of bounds CHECK_THROW(av[1][3] = 3, fail_fast); CHECK_THROW((av[{1, 3}] = 3), fail_fast); CHECK_THROW(av[10][2], fail_fast); CHECK_THROW((av[{10,2}]), fail_fast); } void overloaded_func(array_view exp, int expected_value) { for (auto val : exp) { CHECK(val == expected_value); } } void overloaded_func(array_view exp, char expected_value) { for (auto val : exp) { CHECK(val == expected_value); } } void fixed_func(array_view exp, int expected_value) { for (auto val : exp) { CHECK(val == expected_value); } } TEST(array_view_parameter_test) { auto data = new int[4][3][5]; auto av = as_array_view(data, 4); CHECK(av.size() == 60); fill(av.begin(), av.end(), 34); int count = 0; for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); CHECK(count == 34 * 60); overloaded_func(av, 34); overloaded_func(av.as_array_view(dim<>(4), dim<>(3), dim<>(5)), 34); //fixed_func(av, 34); delete[] data; } TEST(md_access) { unsigned int width = 5, height = 20; unsigned int imgSize = width * height; auto image_ptr = new int[imgSize][3]; // size check will be done auto image_view = as_array_view(image_ptr, imgSize).as_array_view(dim<>(height), dim<>(width), dim<3>()); iota(image_view.begin(), image_view.end(), 1); int expected = 0; for (unsigned int i = 0; i < height; i++) { for (unsigned int j = 0; j < width; j++) { CHECK(expected + 1 == image_view[i][j][0]); CHECK(expected + 2 == image_view[i][j][1]); CHECK(expected + 3 == image_view[i][j][2]); auto val = image_view[{i, j, 0}]; CHECK(expected + 1 == val); val = image_view[{i, j, 1}]; CHECK(expected + 2 == val); val = image_view[{i, j, 2}]; CHECK(expected + 3 == val); expected += 3; } } } TEST(array_view_factory_test) { { int * arr = new int[150]; auto av = as_array_view(arr, dim<10>(), dim<>(3), dim<5>()); fill(av.begin(), av.end(), 24); overloaded_func(av, 24); delete[] arr; array stdarr{ 0 }; auto av2 = as_array_view(stdarr); overloaded_func(av2.as_array_view(dim<>(1), dim<3>(), dim<5>()), 0); string str = "ttttttttttttttt"; // size = 15 auto t = str.data(); auto av3 = as_array_view(str); overloaded_func(av3.as_array_view(dim<>(1), dim<3>(), dim<5>()), 't'); } { int a[3][4][5]; auto av = as_array_view(a); const int (*b)[4][5]; b = a; auto bv = as_array_view(b, 3); CHECK(av == bv); const std::array arr = {0.0, 0.0, 0.0}; auto cv = as_array_view(arr); vector vec(3); auto dv = as_array_view(vec); #ifdef CONFIRM_COMPILATION_ERRORS auto dv2 = as_array_view(std::move(vec)); #endif } } TEST (array_view_reshape_test) { int a[3][4][5]; auto av = as_array_view(a); auto av2 = av.as_array_view(dim<60>()); auto av3 = av2.as_array_view(dim<3>(), dim<4>(), dim<5>()); auto av4 = av3.as_array_view(dim<4>(), dim<>(3), dim<5>()); auto av5 = av4.as_array_view(dim<3>(), dim<4>(), dim<5>()); auto av6 = av5.as_array_view(dim<12>(), dim<>(5)); fill(av6.begin(), av6.end(), 1); auto av7 = av6.as_bytes(); auto av8 = av7.as_array_view(); CHECK(av8.size() == av6.size()); for (size_t i = 0; i < av8.size(); i++) { CHECK(av8[i] == 1); } #ifdef CONFIRM_COMPILATION_ERRORS struct Foo {char c[11];}; auto av9 = av7.as_array_view(); #endif } TEST (array_view_section_test) { int a[30][4][5]; auto av = as_array_view(a); auto sub = av.section({15, 0, 0}, Guide::index<3>{2, 2, 2}); auto subsub = sub.section({1, 0, 0}, Guide::index<3>{1, 1, 1}); } TEST(constructors) { array_view av(nullptr); CHECK(av.length() == 0); array_view av2; CHECK(av2.length() == 0); array_view av3(nullptr, 0); CHECK(av3.length() == 0); // Constructing from a nullptr + length is specifically disallowed auto f = [&]() {array_view av4(nullptr, 2);}; CHECK_THROW(f(), fail_fast); int arr1[2][3]; array_view av5(arr1); array arr2; array_view av6(arr2); vector vec1(19); array_view av7(vec1); CHECK(av7.length() == 19); array_view av8; CHECK(av8.length() == 0); array_view av9(arr2); CHECK(av9.length() == 15); #ifdef CONFIRM_COMPILATION_ERRORS array_view av10; DerivedClass *p = nullptr; array_view av11(p, 0); #endif } TEST(copyandassignment) { array_view av1; int arr[] = {3, 4, 5}; av1 = arr; array_view, dynamic_range> av2; av2 = av1; } TEST(array_view_first) { int arr[5] = { 1, 2, 3, 4, 5 }; { array_view av = arr; CHECK((av.first<2>().bounds() == static_bounds())); CHECK(av.first<2>().length() == 2); CHECK(av.first(2).length() == 2); } { array_view av = arr; CHECK((av.first<0>().bounds() == static_bounds())); CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); } { array_view av = arr; CHECK((av.first<5>().bounds() == static_bounds())); CHECK(av.first<5>().length() == 5); CHECK(av.first(5).length() == 5); } { array_view av = arr; #ifdef CONFIRM_COMPILATION_ERRORS CHECK(av.first<6>().bounds() == static_bounds()); CHECK(av.first<6>().length() == 6); #endif CHECK_THROW(av.first(6).length(), fail_fast); } { array_view av; CHECK((av.first<0>().bounds() == static_bounds())); CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); } } TEST(array_view_last) { int arr[5] = { 1, 2, 3, 4, 5 }; { array_view av = arr; CHECK((av.last<2>().bounds() == static_bounds())); CHECK(av.last<2>().length() == 2); CHECK(av.last(2).length() == 2); } { array_view av = arr; CHECK((av.last<0>().bounds() == static_bounds())); CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); } { array_view av = arr; CHECK((av.last<5>().bounds() == static_bounds())); CHECK(av.last<5>().length() == 5); CHECK(av.last(5).length() == 5); } { array_view av = arr; #ifdef CONFIRM_COMPILATION_ERRORS CHECK((av.last<6>().bounds() == static_bounds())); CHECK(av.last<6>().length() == 6); #endif CHECK_THROW(av.last(6).length(), fail_fast); } { array_view av; CHECK((av.last<0>().bounds() == static_bounds())); CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); } } TEST(custmized_array_view_size) { double (*arr)[3][4] = new double[100][3][4]; array_view, dynamic_range, 3, 4> av1(arr, (char)10); struct EffectiveStructure { double* v1; char v2; }; CHECK(sizeof(av1) == sizeof(EffectiveStructure)); CHECK_THROW(av1[10][3][4], fail_fast); array_view av2 = av1.as_array_view(dim<>(5), dim<6>(), dim<4>()); } TEST(array_view_sub) { int arr[5] = { 1, 2, 3, 4, 5 }; { array_view av = arr; CHECK((av.sub<2,2>().bounds() == static_bounds())); CHECK((av.sub<2,2>().length() == 2)); CHECK(av.sub(2,2).length() == 2); } { array_view av = arr; CHECK((av.sub<0,0>().bounds() == static_bounds())); CHECK((av.sub<0,0>().length() == 0)); CHECK(av.sub(0,0).length() == 0); } { array_view av = arr; CHECK((av.sub<0,5>().bounds() == static_bounds())); CHECK((av.sub<0,5>().length() == 5)); CHECK(av.sub(0,5).length() == 5); } { array_view av = arr; #ifdef CONFIRM_COMPILATION_ERRORS CHECK((av.sub<5,0>().bounds() == static_bounds())); CHECK((av.sub<5,0>().length() == 0)); #endif CHECK_THROW(av.sub(5,0).length(), fail_fast); } { array_view av; CHECK((av.sub<0,0>().bounds() == static_bounds())); CHECK((av.sub<0,0>().length() == 0)); CHECK(av.sub(0,0).length() == 0); } } void AssertNullEmptyProperties(array_view& av) { CHECK(av.length() == 0); CHECK(av.data() == nullptr); CHECK(!av); } template void AssertContentsMatch(T a1, U a2) { CHECK(a1.length() == a2.length()); for (size_t i = 0; i < a1.length(); ++i) CHECK(a1[i] == a2[i]); } TEST(TestNullConstruction) { array_view av; AssertNullEmptyProperties(av); array_view av2(nullptr); AssertNullEmptyProperties(av2); } TEST(ArrayConstruction) { int a[] = { 1, 2, 3, 4 }; array_view av = { &a[1], 3 }; CHECK(av.length() == 3); array_view av3 = { a, 2 }; CHECK(av3.length() == 2); array_view av2 = a; CHECK(av2.length() == 4); } TEST(NonConstConstConversions) { int a[] = { 1, 2, 3, 4 }; #ifdef CONFIRM_COMPILATION_ERRORS array_view cav = a; array_view av = cav; #else array_view av = a; array_view cav = av; #endif AssertContentsMatch(av, cav); } TEST(FixedSizeConversions) { int arr[] = { 1, 2, 3, 4 }; // converting to an array_view from an equal size array is ok array_view av4 = arr; CHECK(av4.length() == 4); // converting to dynamic_range a_v is always ok { array_view av = av4; } { array_view av = arr; } // initialization or assignment to static array_view that REDUCES size is NOT ok #ifdef CONFIRM_COMPILATION_ERRORS { array_view av2 = arr; } { array_view av2 = av4; } #endif { array_view av = arr; array_view av2 = av; } #ifdef CONFIRM_COMPILATION_ERRORS { array_view av = arr; array_view av2 = av.as_array_view(dim<2>(), dim<2>()); } #endif { array_view av = arr; auto f = [&]() {array_view av2 = av.as_array_view(dim<>(2), dim<>(2));}; CHECK_THROW(f(), fail_fast); } // but doing so explicitly is ok // you can convert statically { array_view av2 = {arr, 2}; } { array_view av2 = av4.first<1>(); } // ...or dynamically { // NB: implicit conversion to array_view from array_view array_view av2 = av4.first(1); } // initialization or assignment to static array_view that requires size INCREASE is not ok. int arr2[2] = { 1, 2 }; #ifdef CONFIRM_COMPILATION_ERRORS { array_view av4 = arr2; } { array_view av2 = arr2; array_view av4 = av2; } #endif { auto f = [&]() {array_view av4 = {arr2, 2};}; CHECK_THROW(f(), fail_fast); } // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one array_view av = arr2; auto f = [&](){ array_view av2 = av; }; CHECK_THROW(f(), fail_fast); } TEST(AsWriteableBytes) { int a[] = { 1, 2, 3, 4 }; { #ifdef CONFIRM_COMPILATION_ERRORS // you should not be able to get writeable bytes for const objects array_view av = a; auto wav = av.as_writeable_bytes(); #endif } { array_view av; auto wav = av.as_writeable_bytes(); CHECK(wav.length() == av.length()); CHECK(wav.length() == 0); CHECK(wav.bytes() == 0); } { array_view av = a; auto wav = av.as_writeable_bytes(); CHECK(wav.data() == (byte*)&a[0]); CHECK(wav.length() == sizeof(a)); } } TEST(ArrayViewComparison) { int arr[10][2]; auto av1 = as_array_view(arr); array_view av2 = av1; CHECK(av1 == av2); array_view, 20> av3 = av1.as_array_view(dim<>(20)); CHECK(av3 == av2 && av3 == av1); } } int main(int, const char *[]) { return UnitTest::RunAllTests(); }