blob: 3f627c5b88ba29ff757065673d7a7a14964fefe3 [file] [log] [blame]
/*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/dom/typed_array.h"
#include <limits>
#include "cobalt/dom/float32_array.h"
#include "cobalt/dom/float64_array.h"
#include "cobalt/dom/uint8_array.h"
#include "cobalt/script/testing/mock_exception_state.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace dom {
namespace {
using script::testing::MockExceptionState;
using testing::_;
using testing::SaveArg;
using testing::StrictMock;
template <typename SubArrayType>
class TypedArrayTest : public ::testing::Test {};
typedef ::testing::Types<Uint8Array, Float32Array, Float64Array> TypedArrays;
TYPED_TEST_CASE(TypedArrayTest, TypedArrays);
TYPED_TEST(TypedArrayTest, CreateArray) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
EXPECT_EQ(kLength, array->length());
for (uint32 i = 0; i < kLength; ++i) {
EXPECT_EQ(0, array->Get(i));
}
}
TYPED_TEST(TypedArrayTest, CreateArrayCopy) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> source =
new TypeParam(NULL, kLength, &exception_state);
for (uint32 i = 0; i < kLength; ++i) {
source->Set(i, static_cast<uint8>(i));
}
scoped_refptr<TypeParam> copy = new TypeParam(NULL, source, &exception_state);
for (uint32 i = 0; i < kLength; ++i) {
EXPECT_EQ(source->Get(i), copy->Get(i));
}
}
TYPED_TEST(TypedArrayTest, CopyArrayWithSet) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> source =
new TypeParam(NULL, kLength, &exception_state);
for (uint32 i = 0; i < kLength; ++i) {
source->Set(i, static_cast<uint8>(i));
}
scoped_refptr<TypeParam> copy =
new TypeParam(NULL, kLength, &exception_state);
copy->Set(source, &exception_state);
for (uint32 i = 0; i < kLength; ++i) {
EXPECT_EQ(source->Get(i), copy->Get(i));
}
}
TYPED_TEST(TypedArrayTest, CopyArrayWithSetOffset) {
static const uint32 kLength = 5;
static const uint32 kOffset = 3;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> source =
new TypeParam(NULL, kLength, &exception_state);
for (uint32 i = 0; i < kLength; ++i) {
source->Set(i, static_cast<uint8>(i));
}
scoped_refptr<TypeParam> copy =
new TypeParam(NULL, kLength + kOffset, &exception_state);
copy->Set(source, kOffset, &exception_state);
for (uint32 i = 0; i < kOffset; ++i) {
EXPECT_EQ(0, copy->Get(i));
}
for (uint32 i = 0; i < kLength; ++i) {
EXPECT_EQ(source->Get(i), copy->Get(i + kOffset));
}
}
TYPED_TEST(TypedArrayTest, CopyArrayWithSetOffsetOnTypeParamWithNonZeroOffset) {
// As the following illustration, |dst_buffer| is an array buffer providing
// the storage. |dst| is a three element view starts from the second byte
// of |dst_buffer|.
// | 0 | 0 | 0 | 0 | 0 | <== dst_buffer
// <- dst ->
// |src_buffer| and |src| are independent of |dst_buffer| and |dst|.
// | 1 | 2 | 3 | 4 | 5 | 6 | <== src_buffer
// <- src ->
// So after calling set on |dst| with offset of 1 the content of
// |dst_buffer| and |dst| should be:
// | 0 | 0 | 3 | 4 | 0 | <== dst_buffer
// <- dst ->
StrictMock<MockExceptionState> exception_state;
scoped_refptr<ArrayBuffer> dst_buffer =
new ArrayBuffer(NULL, 5 * TypeParam::kBytesPerElement);
scoped_refptr<TypeParam> dst = new TypeParam(
NULL, dst_buffer, 1 * TypeParam::kBytesPerElement, 3, &exception_state);
scoped_refptr<ArrayBuffer> src_buffer =
new ArrayBuffer(NULL, 6 * TypeParam::kBytesPerElement);
scoped_refptr<TypeParam> src = new TypeParam(
NULL, src_buffer, 2 * TypeParam::kBytesPerElement, 2, &exception_state);
// Use a TypedArray to fill |src_buffer| with values.
scoped_refptr<TypeParam> fill =
new TypeParam(NULL, src_buffer, &exception_state);
for (uint32 i = 0; i < fill->length(); ++i) {
fill->Set(i, static_cast<uint8>(i + 1));
}
dst->Set(src, 1, &exception_state);
EXPECT_EQ(0, dst->Get(0));
EXPECT_EQ(3, dst->Get(1));
EXPECT_EQ(4, dst->Get(2));
}
TYPED_TEST(TypedArrayTest, SetItem) {
static const uint32 kLength = 3;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
array->Set(1, 0xff);
EXPECT_EQ(0, array->Get(0));
EXPECT_EQ(0xff, array->Get(1));
EXPECT_EQ(0, array->Get(2));
}
TYPED_TEST(TypedArrayTest, ArrayView) {
// Test creating a second array that's a view of the first one.
// Write to the view and then check the original was modified.
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> view =
new TypeParam(NULL, array->buffer(), &exception_state);
for (uint32 i = 0; i < kLength; ++i) {
view->Set(i, static_cast<uint8>(i + 1));
}
for (uint32 i = 0; i < kLength; ++i) {
EXPECT_EQ(i + 1, array->Get(i));
}
}
TYPED_TEST(TypedArrayTest, ArrayViewLengthZero) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> view =
new TypeParam(NULL, array->buffer(),
kLength * TypeParam::kBytesPerElement, &exception_state);
EXPECT_EQ(0, view->length());
}
TYPED_TEST(TypedArrayTest, ArrayViewOffset) {
// Test creating a second array that's a view of the first one.
// Write to the view and then check the original was modified.
static const uint32 kLength = 5;
static const uint32 kViewOffset = 3;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> view = new TypeParam(
NULL, array->buffer(), kViewOffset * TypeParam::kBytesPerElement,
&exception_state);
for (uint32 i = 0; i < kLength - kViewOffset; ++i) {
view->Set(i, 0xff);
}
EXPECT_EQ(0, array->Get(0));
EXPECT_EQ(0, array->Get(1));
EXPECT_EQ(0, array->Get(2));
EXPECT_EQ(0xff, array->Get(3));
EXPECT_EQ(0xff, array->Get(4));
}
TYPED_TEST(TypedArrayTest, ArrayViewOffsetAndLength) {
// Test creating a second array that's a view of the first one.
// Write to the view and then check the original was modified.
static const uint32 kLength = 5;
static const uint32 kViewOffset = 3;
static const uint32 kViewLength = kLength - kViewOffset;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> view = new TypeParam(
NULL, array->buffer(), kViewOffset * TypeParam::kBytesPerElement,
kViewLength, &exception_state);
for (uint32 i = 0; i < kViewLength; ++i) {
view->Set(i, static_cast<uint8>(kViewOffset + i));
}
EXPECT_EQ(0, array->Get(0));
EXPECT_EQ(0, array->Get(1));
EXPECT_EQ(0, array->Get(2));
EXPECT_EQ(3, array->Get(3));
EXPECT_EQ(4, array->Get(4));
}
TYPED_TEST(TypedArrayTest, CreateSubArrayOnlyStart) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> subarray = array->Subarray(NULL, 1);
for (uint32 i = 0; i < kLength; ++i) {
array->Set(i, static_cast<uint8>(i));
}
EXPECT_EQ(4, subarray->length());
EXPECT_EQ(1, subarray->Get(0));
EXPECT_EQ(2, subarray->Get(1));
EXPECT_EQ(3, subarray->Get(2));
EXPECT_EQ(4, subarray->Get(3));
}
TYPED_TEST(TypedArrayTest, CreateSubArrayStartEnd) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> subarray = array->Subarray(NULL, 1, 4);
for (uint32 i = 0; i < kLength; ++i) {
array->Set(i, static_cast<uint8>(i));
}
EXPECT_EQ(3, subarray->length());
EXPECT_EQ(1, subarray->Get(0));
EXPECT_EQ(2, subarray->Get(1));
EXPECT_EQ(3, subarray->Get(2));
}
TYPED_TEST(TypedArrayTest, CreateSubArrayStartEndOnTypeParamWithNonZeroOffset) {
// As the following illustration, |array_buffer| owns the whole buffer,
// |array| is a view starts from the second byte of |array_buffer|, |subarray|
// is a subarray of |array| starts from the second byte, which is in turn a
// view of |array_buffer| starts from the third byte.
// | 0 | 1 | 2 | 3 | 4 | <== array_buffer
// <- array ->
// <-subarray ->
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<ArrayBuffer> array_buffer =
new ArrayBuffer(NULL, kLength * TypeParam::kBytesPerElement);
scoped_refptr<TypeParam> array =
new TypeParam(NULL, array_buffer, 1 * TypeParam::kBytesPerElement,
kLength - 1, &exception_state);
// The length passed to Subarray(NULL, ) is actually one element longer than
// what
// |array| has. This should be clamped inside Subarray(NULL, ) and the size
// of
// |subarray| should still be three.
scoped_refptr<TypeParam> subarray =
array->Subarray(NULL, 1, static_cast<int>(array->length()));
// Use a TypedArray to fill |array_buffer| with values.
scoped_refptr<TypeParam> fill =
new TypeParam(NULL, array_buffer, &exception_state);
for (uint32 i = 0; i < fill->length(); ++i) {
fill->Set(i, static_cast<uint8>(i));
}
EXPECT_EQ(1, array->Get(0));
EXPECT_EQ(2, subarray->Get(0));
}
TYPED_TEST(TypedArrayTest, CreateSubArrayNegativeOnlyStart) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> subarray = array->Subarray(NULL, -2);
for (uint32 i = 0; i < kLength; ++i) {
array->Set(i, static_cast<uint8>(i));
}
// subarray[0] -> array[3]
// subarray[1] -> array[4]
EXPECT_EQ(2, subarray->length());
EXPECT_EQ(3, subarray->Get(0));
EXPECT_EQ(4, subarray->Get(1));
}
TYPED_TEST(TypedArrayTest, CreateSubArrayNegativeStartEnd) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> subarray = array->Subarray(NULL, -3, -1);
for (uint32 i = 0; i < kLength; ++i) {
array->Set(i, static_cast<uint8>(i));
}
// subarray[0] -> array[2]
// subarray[1] -> array[3]
EXPECT_EQ(2, subarray->length());
EXPECT_EQ(2, subarray->Get(0));
EXPECT_EQ(3, subarray->Get(1));
}
TYPED_TEST(TypedArrayTest, CreateSubArrayInvalidLength) {
static const uint32 kLength = 5;
StrictMock<MockExceptionState> exception_state;
scoped_refptr<TypeParam> array =
new TypeParam(NULL, kLength, &exception_state);
scoped_refptr<TypeParam> subarray = array->Subarray(NULL, 12, 5);
EXPECT_EQ(0, subarray->length());
}
TYPED_TEST(TypedArrayTest, ExceptionInConstructor) {
// This test doesn't apply to any typed array whose element size is 1.
if (TypeParam::kBytesPerElement == 1) {
return;
}
StrictMock<MockExceptionState> exception_state;
script::MessageType message_type;
EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
.WillOnce(SaveArg<0>(&message_type));
// Create an ArrayBuffer whose size isn't a multiple of
// TypeParam::kBytesPerElement.
scoped_refptr<ArrayBuffer> array_buffer =
new ArrayBuffer(NULL, TypeParam::kBytesPerElement + 1);
// The size of the array_buffer isn't a multiple of
// TypeParam::kBytesPerElement.
scoped_refptr<TypeParam> array =
new TypeParam(NULL, array_buffer, &exception_state);
EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
.WillOnce(SaveArg<0>(&message_type));
// Neither the size of the array_buffer nor the byte_offset is a multiple of
// TypeParam::kBytesPerElement, but SetSimpleException() should only be called
// once.
array = new TypeParam(NULL, array_buffer, 1, &exception_state);
EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
.WillOnce(SaveArg<0>(&message_type));
// Now the size of the array_buffer is a multiple of
// TypeParam::kBytesPerElement but the byte_offset isn't.
array_buffer = new ArrayBuffer(NULL, TypeParam::kBytesPerElement);
array = new TypeParam(NULL, array_buffer, 1, &exception_state);
EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
.WillOnce(SaveArg<0>(&message_type));
// Both the size of the array_buffer and the byte_offset are multiples of
// TypeParam::kBytesPerElement but array_buffer cannot hold 2 elements.
array = new TypeParam(NULL, array_buffer, 0, 2, &exception_state);
EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
// The size of the array_buffer isn't a multiple of
// TypeParam::kBytesPerElement but the byte_offset is. Also the whole typed
// array can fit into the array_buffer. The constructor should run without
// raising any exception.
array_buffer = new ArrayBuffer(NULL, TypeParam::kBytesPerElement * 2 + 1);
array = new TypeParam(NULL, array_buffer, TypeParam::kBytesPerElement, 1,
&exception_state);
}
TYPED_TEST(TypedArrayTest, ExceptionInSet) {
// This test doesn't apply to any typed array whose element size is 1.
if (TypeParam::kBytesPerElement == 1) {
return;
}
StrictMock<MockExceptionState> exception_state;
script::MessageType message_type;
EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
.WillOnce(SaveArg<0>(&message_type));
static const uint32 kLength = 5;
scoped_refptr<TypeParam> source =
new TypeParam(NULL, kLength + 1, &exception_state);
scoped_refptr<TypeParam> dest =
new TypeParam(NULL, kLength, &exception_state);
dest->Set(source, &exception_state);
EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
.WillOnce(SaveArg<0>(&message_type));
source = new TypeParam(NULL, kLength, &exception_state);
dest = new TypeParam(NULL, kLength + 1, &exception_state);
dest->Set(source, 2, &exception_state);
EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
dest->Set(source, 1, &exception_state);
}
} // namespace
} // namespace dom
} // namespace cobalt