[utils][webapi-plugins.spec][common][tizen-wrt.gyp][tizen] Common bundle tests. 02/208802/3
authorMichal Michalski <m.michalski2@partner.samsung.com>
Fri, 28 Jun 2019 11:23:21 +0000 (13:23 +0200)
committerMichal Michalski <m.michalski2@partner.samsung.com>
Thu, 4 Jul 2019 15:16:23 +0000 (17:16 +0200)
http://suprem.sec.samsung.net/jira/browse/TWDAPI-225

Tests for common Bundle implementation.

[Verification] Pass rate 100%.

Signed-off-by: Michal Michalski <m.michalski2@partner.samsung.com>
Change-Id: Ib8ae4415b45f018efc3406c071115782920798f9

packaging/webapi-plugins.spec
src/common/common_ut.gyp [new file with mode: 0644]
src/common/ut/bundle_ut.cc [new file with mode: 0644]
src/common/ut/common_ut_extension.cc [new file with mode: 0644]
src/common/ut/common_ut_extension.h [new file with mode: 0644]
src/tizen-wrt.gyp
src/tizen/js/ut/bundle_ut.js [new file with mode: 0644]
src/utils/js/ut/common_ut.js [new file with mode: 0644]

index 89f1fae..6a5ddfa 100644 (file)
@@ -6,6 +6,8 @@
 %define crosswalk_extensions tizen-extensions-crosswalk
 
 %define crosswalk_extensions_path %{_libdir}/%{crosswalk_extensions}
+%define tizen_ut_build 0
+
 
 Name:       webapi-plugins
 Version:    2.45
@@ -689,6 +691,9 @@ GYP_OPTIONS="--depth=. -Dtizen=1 -Dextension_build_type=Debug -Dextension_host_o
 GYP_OPTIONS="$GYP_OPTIONS -Ddisplay_type=%{display_type}"
 GYP_OPTIONS="$GYP_OPTIONS -Dcrosswalk_extensions_path=%{crosswalk_extensions_path}"
 
+# ut flags
+GYP_OPTIONS="$GYP_OPTIONS -Dtizen_ut_build=%{tizen_ut_build}"
+
 # feature flags
 GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_account_support=%{?tizen_mobile_feature_account_support}"
 GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_alarm_support=%{?tizen_mobile_feature_alarm_support}"
@@ -1177,6 +1182,13 @@ install -p -m 644 plugins.json %{buildroot}%{crosswalk_extensions_path}/common/p
 
 %if "%{?unified_build}" == "1" || "%{?profile}" == "mobile"
 mkdir -p %{buildroot}%{crosswalk_extensions_path}/mobile
+
+# tizen ut mobile
+%if "%{?tizen_ut_build}"  == "1"
+mkdir -p %{buildroot}/usr/bin
+install -p -m 755 out/bin_mobile/bundle_ut %{buildroot}/usr/bin/
+%endif
+
 install -p -m 644 out/bin_mobile/libtizen*.so %{buildroot}%{crosswalk_extensions_path}/mobile
 # execute desc_gentool
 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:%{buildroot}%{crosswalk_extensions_path}/mobile out/Default/desc_gentool \
@@ -1309,6 +1321,12 @@ fi
 %{crosswalk_extensions_path}/mobile/plugins.json
 %manifest webapi-plugins.manifest
 
+# UT files
+%if "%{?tizen_ut_build}" == "1"
+%license GTEST.BSD-3-Clause
+/usr/bin/bundle_ut
+%endif
+
 # mobile-extension-emulator
 %ifarch %{ix86} x86_64
 %post mobile-extension-emulator
diff --git a/src/common/common_ut.gyp b/src/common/common_ut.gyp
new file mode 100644 (file)
index 0000000..1ceb42f
--- /dev/null
@@ -0,0 +1,39 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'bundle_ut',
+      'type': 'executable',
+      'dependencies': [
+        'common.gyp:tizen_common'
+      ],
+      'include_dirs': [
+        '../googletest/include/',
+        '../googletest/',
+        '../googlemock/include/',
+        '../googlemock/'
+      ],
+      'sources': [
+        '../googletest/src/gtest-all.cc',
+        '../googlemock/src/gmock-all.cc',
+        'ut/common_ut_extension.cc',
+        'ut/bundle_ut.cc'
+      ],
+      'libraries': [
+        '-lbundle',
+        '-pthread'
+      ],
+      'conditions': [
+        ['tizen == 1', {
+          'variables': {
+            'packages': [
+              'capi-base-common'
+            ]
+          }
+        }]
+      ]
+    },
+  ],
+}
diff --git a/src/common/ut/bundle_ut.cc b/src/common/ut/bundle_ut.cc
new file mode 100644 (file)
index 0000000..5b9c406
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd 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 "tizen.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "bundle.h"
+#include "bundle_internal.h"
+#include "common/json-utils.h"
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+using namespace std;
+using testing::_;
+using testing::StrEq;
+
+namespace {
+
+template <typename T, typename V>
+picojson::value vec2json(vector<V> vec) {
+  picojson::array array;
+  for (auto v : vec) {
+    array.push_back(picojson::value(static_cast<T>(v)));
+  }
+  return picojson::value(array);
+}
+
+}  // namespace
+
+MATCHER_P(KeyValStrEq, expected, "match keyval_t string value") {
+  auto* kv = const_cast<bundle_keyval_t*>(static_cast<const bundle_keyval_t*>(arg));
+
+  if (BUNDLE_TYPE_STR != bundle_keyval_get_type(kv)) {
+    return false;
+  }
+
+  void* untyped = nullptr;
+  size_t size = 0;
+
+  int ret = bundle_keyval_get_basic_val(kv, &untyped, &size);
+  if (BUNDLE_ERROR_NONE != ret) {
+    return false;
+  }
+
+  string value = static_cast<char*>(untyped);
+  return value == expected;
+}
+
+MATCHER_P(KeyValStrArrEq, expected, "match keyval_t string array value") {
+  auto* kv = const_cast<bundle_keyval_t*>(static_cast<const bundle_keyval_t*>(arg));
+
+  if (BUNDLE_TYPE_STR_ARRAY != bundle_keyval_get_type(kv)) {
+    return false;
+  }
+
+  void** untyped = nullptr;
+  size_t array_size = 0;
+  size_t* elem_size = nullptr;
+
+  int ret = bundle_keyval_get_array_val(kv, &untyped, &array_size, &elem_size);
+  if (BUNDLE_ERROR_NONE != ret) {
+    return false;
+  }
+
+  if (array_size != expected.size()) {
+    return false;
+  }
+
+  char** value = reinterpret_cast<char**>(untyped);
+  for (size_t i = 0; i < array_size; ++i) {
+    if (string(value[i]) != expected[i]) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+MATCHER_P(KeyValBytesEq, expected, "match keyval_t bytes value") {
+  auto* kv = const_cast<bundle_keyval_t*>(static_cast<const bundle_keyval_t*>(arg));
+
+  if (BUNDLE_TYPE_BYTE != bundle_keyval_get_type(kv)) {
+    return false;
+  }
+
+  void* untyped = nullptr;
+  size_t size = 0;
+
+  int ret = bundle_keyval_get_basic_val(kv, &untyped, &size);
+  if (BUNDLE_ERROR_NONE != ret) {
+    return false;
+  }
+
+  if (size != expected.size()) {
+    return false;
+  }
+
+  auto bytes = static_cast<unsigned char*>(untyped);
+  for (unsigned int i = 0; i < size; ++i) {
+    if (bytes[i] != expected[i]) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+MATCHER_P(KeyValBytesArrEq, expected, "match keyval_t bytes array value") {
+  auto* kv = const_cast<bundle_keyval_t*>(static_cast<const bundle_keyval_t*>(arg));
+
+  if (BUNDLE_TYPE_BYTE_ARRAY != bundle_keyval_get_type(kv)) {
+    return false;
+  }
+
+  void** untyped = nullptr;
+  size_t array_size = 0;
+  size_t* elem_size = nullptr;
+
+  int ret = bundle_keyval_get_array_val(kv, &untyped, &array_size, &elem_size);
+  if (BUNDLE_ERROR_NONE != ret) {
+    return false;
+  }
+
+  if (array_size != expected.size()) {
+    return false;
+  }
+
+  unsigned char** value = reinterpret_cast<unsigned char**>(untyped);
+  for (size_t i = 0; i < array_size; ++i) {
+    for (size_t j = 0; j < elem_size[i]; ++j) {
+      if (expected[i][j] != value[i][j]) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+class JsonToBundleTest : public testing::Test {
+ public:
+  virtual void SetUp() {
+    bundleData = bundle_create();
+    ASSERT_NE(bundleData, nullptr);
+  }
+
+  virtual void TearDown() {
+    int ret = bundle_free(bundleData);
+    ASSERT_EQ(ret, BUNDLE_ERROR_NONE);
+  }
+
+  // Replacing bundle_keyval_t with void and cast it in matcher is required because
+  // bundle_keyval_t has only public declaration, not implementation.
+  // This causes incomplete type error which I couldn't resolve in any other way.
+  using BundleIterator = void(const char*, const int, const void*, void*);
+  using BundleIteratorMock = testing::MockFunction<BundleIterator>;
+
+  void CheckBundle(bundle* b, BundleIteratorMock& mock) {
+    bundle_foreach(b,
+                   [](const char* key, const int type, const bundle_keyval_t* kv, void* ud) {
+                     auto* mock = static_cast<testing::MockFunction<BundleIterator>*>(ud);
+                     mock->Call(key, type, kv, nullptr);
+                   },
+                   &mock);
+  }
+
+  bundle* bundleData;
+  BundleIteratorMock bundleIteratorMock;
+};
+
+TEST_F(JsonToBundleTest, BytesArrayConversion) {
+  vector<vector<unsigned char>> value = {{0, 1, 2}, {100, 101, 102}, {200, 201, 202}};
+
+  picojson::object json;
+  picojson::array array;
+  array.push_back(vec2json<double>(value[0]));
+  array.push_back(vec2json<double>(value[1]));
+  array.push_back(vec2json<double>(value[2]));
+  json["key"] = picojson::value(array);
+
+  auto result = common::JsonToBundle(json, bundleData);
+  ASSERT_TRUE(result);
+
+  EXPECT_CALL(bundleIteratorMock,
+              Call(StrEq("key"), BUNDLE_TYPE_BYTE_ARRAY, KeyValBytesArrEq(value), nullptr));
+
+  CheckBundle(bundleData, bundleIteratorMock);
+}
+
+TEST_F(JsonToBundleTest, BytesConversion) {
+  vector<unsigned char> value = {0, 126, 255};
+
+  picojson::object json;
+  json["key"] = vec2json<double>(value);
+
+  auto result = common::JsonToBundle(json, bundleData);
+  ASSERT_TRUE(result);
+
+  EXPECT_CALL(bundleIteratorMock,
+              Call(StrEq("key"), BUNDLE_TYPE_BYTE, KeyValBytesEq(value), nullptr));
+
+  CheckBundle(bundleData, bundleIteratorMock);
+}
+
+TEST_F(JsonToBundleTest, BytesConversionInvalidByteValue) {
+  picojson::object json;
+  json["key"] = vec2json<double>(vector<double>({1.0, 256.0, 2.0}));
+
+  auto result = common::JsonToBundle(json, bundleData);
+  ASSERT_FALSE(result);
+
+  json["key"] = vec2json<double>(vector<double>({1.0, -1.0, 2.0}));
+  result = common::JsonToBundle(json, bundleData);
+  ASSERT_FALSE(result);
+}
+
+TEST_F(JsonToBundleTest, StringConversion) {
+  string value = "tizen";
+  picojson::object json;
+  json["key"] = picojson::value(value);
+
+  auto result = common::JsonToBundle(json, bundleData);
+  ASSERT_TRUE(result);
+
+  EXPECT_CALL(bundleIteratorMock, Call(StrEq("key"), BUNDLE_TYPE_STR, KeyValStrEq(value), nullptr));
+
+  CheckBundle(bundleData, bundleIteratorMock);
+}
+
+TEST_F(JsonToBundleTest, StringArrayConversion) {
+  vector<string> value = {"str1", "str2", "str3"};
+
+  picojson::object json;
+  json["key"] = picojson::value(picojson::array(
+      {picojson::value(value[0]), picojson::value(value[1]), picojson::value(value[2])}));
+
+  auto result = common::JsonToBundle(json, bundleData);
+  ASSERT_TRUE(result);
+
+  EXPECT_CALL(bundleIteratorMock,
+              Call(StrEq("key"), BUNDLE_TYPE_STR_ARRAY, KeyValStrArrEq(value), nullptr));
+
+  CheckBundle(bundleData, bundleIteratorMock);
+}
+
+TEST_F(JsonToBundleTest, UnsupportedType) {
+  picojson::object json;
+  json["key"] = picojson::value(true);
+
+  auto result = common::JsonToBundle(json, bundleData);
+  ASSERT_FALSE(result);
+
+  picojson::object json2;
+  json["key"] = picojson::value(picojson::object());
+
+  result = common::JsonToBundle(json, bundleData);
+  ASSERT_FALSE(result);
+}
+
+class BundleToJsonTest : public testing::Test {
+ public:
+  virtual void SetUp() {
+    bundleData = bundle_create();
+    ASSERT_NE(bundleData, nullptr);
+  }
+
+  virtual void TearDown() {
+    ASSERT_EQ(bundle_free(bundleData), BUNDLE_ERROR_NONE);
+  }
+
+  bundle* bundleData;
+};
+
+TEST_F(BundleToJsonTest, StringConversion) {
+  string key = "key", value = "tizen";
+  ASSERT_EQ(bundle_add_str(bundleData, key.c_str(), value.c_str()), BUNDLE_ERROR_NONE);
+
+  picojson::object json;
+  auto result = common::BundleToJson(bundleData, &json);
+
+  ASSERT_TRUE(result);
+  ASSERT_EQ(json[key].get<string>(), value);
+}
+
+TEST_F(BundleToJsonTest, StringArrayConversion) {
+  string key = "key";
+  vector<const char*> value = {"value1", "value2"};
+
+  ASSERT_EQ(bundle_add_str_array(bundleData, key.c_str(), value.data(), value.size()),
+            BUNDLE_ERROR_NONE);
+
+  picojson::object json;
+  auto result = common::BundleToJson(bundleData, &json);
+  ASSERT_TRUE(result);
+
+  const auto& array = json[key].get<picojson::array>();
+  ASSERT_EQ(array.size(), value.size());
+  EXPECT_EQ(array[0].get<std::string>(), std::string(value[0]));
+  EXPECT_EQ(array[1].get<std::string>(), std::string(value[1]));
+}
+
+TEST_F(BundleToJsonTest, BytesConversion) {
+  string key = "key";
+  vector<unsigned char> bytes = {0, 126, 255};
+
+  ASSERT_EQ(bundle_add_byte(bundleData, key.c_str(), bytes.data(), bytes.size()),
+            BUNDLE_ERROR_NONE);
+
+  picojson::object json;
+  auto result = common::BundleToJson(bundleData, &json);
+  ASSERT_TRUE(result);
+
+  const auto& array = json[key].get<picojson::array>();
+  ASSERT_EQ(array.size(), bytes.size());
+  EXPECT_EQ(array[0].get<double>(), bytes[0]);
+  EXPECT_EQ(array[1].get<double>(), bytes[1]);
+  EXPECT_EQ(array[2].get<double>(), bytes[2]);
+}
+
+TEST_F(BundleToJsonTest, BytesArrayConversion) {
+  string key = "key";
+  vector<vector<unsigned char>> value = {{0, 126, 255}, {1, 127, 254}};
+
+  ASSERT_EQ(bundle_add_byte_array(bundleData, key.c_str(), nullptr, value.size()),
+            BUNDLE_ERROR_NONE);
+  ASSERT_EQ(
+      bundle_set_byte_array_element(bundleData, key.c_str(), 0, value[0].data(), value[0].size()),
+      BUNDLE_ERROR_NONE);
+  ASSERT_EQ(
+      bundle_set_byte_array_element(bundleData, key.c_str(), 1, value[1].data(), value[1].size()),
+      BUNDLE_ERROR_NONE);
+
+  picojson::object json;
+  auto result = common::BundleToJson(bundleData, &json);
+  ASSERT_TRUE(result);
+
+  auto& array = json[key].get<picojson::array>();
+  ASSERT_EQ(array.size(), value.size());
+
+  auto& bytes = array[0].get<picojson::array>();
+  ASSERT_EQ(bytes.size(), value[0].size());
+  EXPECT_EQ(bytes[0].get<double>(), value[0][0]);
+  EXPECT_EQ(bytes[1].get<double>(), value[0][1]);
+  EXPECT_EQ(bytes[2].get<double>(), value[0][2]);
+
+  bytes = array[1].get<picojson::array>();
+  ASSERT_EQ(bytes.size(), value[1].size());
+  EXPECT_EQ(bytes[0].get<double>(), value[1][0]);
+  EXPECT_EQ(bytes[1].get<double>(), value[1][1]);
+  EXPECT_EQ(bytes[2].get<double>(), value[1][2]);
+}
+
+int main(int argc, char* argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/src/common/ut/common_ut_extension.cc b/src/common/ut/common_ut_extension.cc
new file mode 100644 (file)
index 0000000..f70a17c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd 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 "common/ut/common_ut_extension.h"
+
+common::Extension* CreateExtension() {
+  return new CommonUtExtension;
+}
+
+CommonUtExtension::CommonUtExtension() {
+  SetExtensionName("tizen");
+  SetJavaScriptAPI("");
+}
+
+CommonUtExtension::~CommonUtExtension() {
+}
diff --git a/src/common/ut/common_ut_extension.h b/src/common/ut/common_ut_extension.h
new file mode 100644 (file)
index 0000000..b569b20
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd 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.
+ */
+
+#ifndef TIZEN_COMMON_UT_EXTENSION_H_
+#define TIZEN_COMMON_UT_EXTENSION_H_
+
+#include "common/extension.h"
+
+class CommonUtExtension : public common::Extension {
+ public:
+  CommonUtExtension();
+  virtual ~CommonUtExtension();
+};
+
+#endif  // TIZEN_COMMON_UT_EXTENSION_H_
index 196dce8..78c1231 100755 (executable)
       ],
       'conditions': [
         [
+          'tizen_ut_build==1', {
+            'dependencies': [
+              'common/common_ut.gyp:*'
+            ]
+          }
+        ],
+        [
           'tizen_feature_account_support==1', {
             'dependencies': [
               'account/account.gyp:*',
diff --git a/src/tizen/js/ut/bundle_ut.js b/src/tizen/js/ut/bundle_ut.js
new file mode 100644 (file)
index 0000000..6b03fd2
--- /dev/null
@@ -0,0 +1,226 @@
+function TestDefaultCtor() {
+    var bundle = new tizen.Bundle();
+    var counter = 0;
+    bundle.forEach(function(key, value, type) {
+        counter++;
+    });
+    assertEqual(0, counter, 'bundle should be empty');
+}
+
+function TestJsonCtor() {
+    var json = {
+        key1: 'value',
+        key2: ['value1', 'value2'],
+        key3: new Uint8Array([1, 2, 3]),
+        key4: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])]
+    };
+
+    var bundle = new tizen.Bundle(json);
+    var counter = 0;
+    forEachOwnProperty(bundle.data, function(k, v) {
+        counter++;
+    });
+    assertEqual(counter, 4, 'invalid number of entries');
+}
+
+function TestSetter() {
+    var bundle = new tizen.Bundle();
+    bundle.set('string', 'hello, world');
+    assertEqual(bundle.data['string'], 'hello, world', 'invalid string value');
+
+    bundle.set('stringArray', ['hello', 'world']);
+    assertArrayEqual(
+        bundle.data['stringArray'],
+        ['hello', 'world'],
+        'invalid string array value'
+    );
+
+    bundle.set('bytes', new Uint8Array([1, 2, 3, 4]));
+    assertArrayEqual(bundle.data['bytes'], [1, 2, 3, 4], 'invalid bytes value');
+
+    bundle.set('bytesArray', [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])]);
+    assertEqual(bundle.data['bytesArray'].length, 2, 'wrong number of byte streams');
+    assertArrayEqual(bundle.data['bytesArray'][0], [1, 2, 3], 'wrong byte stream');
+    assertArrayEqual(bundle.data['bytesArray'][1], [4, 5, 6], 'wrong byte stream');
+
+    bundle.set('other', function() {});
+    assertEqual(
+        bundle.typeOf('other') == tizen.BundleValueType.STRING,
+        true,
+        'other type should be converted to string'
+    );
+    assertEqual(bundle.data['other'], 'function () {}', 'invalid value for other type');
+
+    bundle.set('someKey', undefined);
+    assertEqual(bundle.data['someKey'], 'undefined');
+}
+
+function TestGetters() {
+    var json = {
+        key1: 'value',
+        key2: ['value1', 'value2'],
+        key3: new Uint8Array([1, 2, 3]),
+        key4: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])]
+    };
+
+    var bundle = new tizen.Bundle(json);
+
+    var strVal = bundle.get('key1');
+    assertEqual(strVal, 'value', 'string getter returned invalid value');
+
+    var strArrVal = bundle.get('key2');
+    assertArrayEqual(
+        strArrVal,
+        ['value1', 'value2'],
+        'string array getter returned invalid value'
+    );
+
+    var bytesVal = bundle.get('key3');
+    assertArrayEqual(bytesVal, [1, 2, 3], 'bytes getter returned invalid value');
+
+    var bytesArrVal = bundle.get('key4');
+    assertEqual(bytesArrVal.length, 2, 'wrong number of byte streams');
+    assertArrayEqual(bytesArrVal[0], [1, 2, 3], 'byte stream invalid');
+    assertArrayEqual(bytesArrVal[1], [4, 5, 6], 'byte stream invalid');
+
+    try {
+        var notVal = bundle.get('not-a-key');
+        assertEqual(true, false, 'expected exception was not thrown');
+    } catch (err) {
+        assertEqual(err.code, WebAPIException.NOT_FOUND_ERR, 'expected NOT_FOUND_ERR');
+    }
+}
+
+function TestTypeOf() {
+    var json = {
+        key1: 'value',
+        key2: ['value1', 'value2'],
+        key3: new Uint8Array([1, 2, 3]),
+        key4: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])],
+        key5: []
+    };
+
+    var bundle = new tizen.Bundle(json);
+    assertEqual(
+        bundle.typeOf('key1'),
+        tizen.BundleValueType.STRING,
+        'expected STRING type'
+    );
+    assertEqual(
+        bundle.typeOf('key2'),
+        tizen.BundleValueType.STRING_ARRAY,
+        'expected STRING_ARRAY type'
+    );
+    assertEqual(
+        bundle.typeOf('key3'),
+        tizen.BundleValueType.BYTES,
+        'expected BYTES type'
+    );
+    assertEqual(
+        bundle.typeOf('key4'),
+        tizen.BundleValueType.BYTES_ARRAY,
+        'expected BYTES_ARRAY'
+    );
+    assertEqual(
+        bundle.typeOf('key5'),
+        tizen.BundleValueType.STRING_ARRAY,
+        'empty array should be treated like STRING_ARRAY'
+    );
+
+    try {
+        type = bundle.typeOf('notKey');
+        assertEqual(false, true, 'exception was not thrown');
+    } catch (err) {
+        assertEqual(
+            err.code,
+            WebAPIException.NOT_FOUND_ERR,
+            'expected NOT_FOUND_ERR exception'
+        );
+    }
+}
+
+function TestForEach() {
+    var json = {
+        key1: 'value',
+        key2: ['value1', 'value2'],
+        key3: new Uint8Array([1, 2, 3]),
+        key4: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])]
+    };
+
+    var bundle = new tizen.Bundle(json);
+    var assertions = {
+        key1: {
+            active: true,
+            check: function(key, value, type) {
+                assertEqual(value, 'value', 'invalid value for key ' + key);
+                assertEqual(type, tizen.BundleValueType.STRING, 'should be STRING');
+            }
+        },
+        key2: {
+            active: true,
+            check: function(key, value, type) {
+                assertArrayEqual(
+                    value,
+                    ['value1', 'value2'],
+                    'invalid value for key ' + key
+                );
+                assertEqual(
+                    type,
+                    tizen.BundleValueType.STRING_ARRAY,
+                    'should be STRING_ARRAY'
+                );
+            }
+        },
+        key3: {
+            active: true,
+            check: function(key, value, type) {
+                assertArrayEqual(value, [1, 2, 3], 'invalid byte stream');
+                assertEqual(type, tizen.BundleValueType.BYTES, 'should be BYTES');
+            }
+        },
+        key4: {
+            active: true,
+            check: function(key, value, type) {
+                assertEqual(value.length, 2, 'invalid number of byte streams');
+                assertArrayEqual(value[0], [1, 2, 3], 'invalid byte stream');
+                assertArrayEqual(value[1], [4, 5, 6], 'invalid byte stream');
+                assertEqual(type, tizen.BundleValueType.BYTES_ARRAY);
+            }
+        }
+    };
+
+    bundle.forEach(function(key, value, type) {
+        assertEqual(assertions[key].active, true, 'inactive assertion triggered');
+        assertions[key].check(key, value, type);
+        assertions[key].active = false;
+    });
+
+    for (var id in assertions) {
+        if (assertions.hasOwnProperty(id)) {
+            assertEqual(false, assertions[id].active, 'unused assertion with id ' + id);
+        }
+    }
+}
+
+function TestToJson() {
+    var json = {
+        key1: 'value',
+        key2: ['value1', 'value2'],
+        key3: new Uint8Array([1, 2, 3]),
+        key4: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])]
+    };
+
+    var bundle = new tizen.Bundle(json);
+    result = bundle.toJSON();
+    assertEqual(JSON.stringify(json), JSON.stringify(result), 'invalid json');
+}
+
+var testcases = [
+    TestDefaultCtor,
+    TestJsonCtor,
+    TestSetter,
+    TestGetters,
+    TestTypeOf,
+    TestForEach,
+    TestToJson
+];
diff --git a/src/utils/js/ut/common_ut.js b/src/utils/js/ut/common_ut.js
new file mode 100644 (file)
index 0000000..60832dc
--- /dev/null
@@ -0,0 +1,54 @@
+function forEachOwnProperty(obj, cb) {
+    for (var prop in obj) {
+        if (obj.hasOwnProperty(prop)) {
+            cb(prop, obj[prop]);
+        }
+    }
+}
+
+function assert(test, condition, message, expected, actual) {
+    if (!condition) {
+        throw {
+            type: 'AssertionError',
+            where: test,
+            msg: message,
+            expected: expected,
+            actual: actual
+        };
+    }
+}
+
+function assertEqual(a, b, message) {
+    assert(arguments.callee.caller.name, a == b, message, a, b);
+}
+
+function assertArrayEqual(a, b, message) {
+    assert(
+        arguments.callee.caller.name,
+        a.length == b.length,
+        message + ' | array size differs',
+        a.length,
+        b.length
+    );
+    for (var i = 0; i < a.length; ++i) {
+        assert(
+            arguments.callee.caller.name,
+            a[i] == b[i],
+            message + ' | array elements not equal at ' + i,
+            a[i],
+            b[i]
+        );
+    }
+}
+
+function RunTests(testcases) {
+    testcases.forEach(function(tc) {
+        try {
+            tc();
+            console.info(tc.name + ': PASS');
+        } catch (err) {
+            console.info(tc.name + ': FAIL');
+            console.error(err);
+        }
+    });
+}