summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/cpp/src/generate/t_js_generator.cc61
-rw-r--r--lib/js/Gruntfile.js21
-rw-r--r--lib/js/src/thrift.js68
-rw-r--r--lib/js/test/deep-constructor.test.js195
-rwxr-xr-xlib/js/test/test-deep-constructor.html49
-rw-r--r--lib/nodejs/lib/thrift/json_protocol.js10
-rw-r--r--lib/nodejs/lib/thrift/thrift.js67
-rw-r--r--lib/nodejs/test/deep-constructor.test.js233
-rw-r--r--lib/nodejs/test/test-cases.js17
-rwxr-xr-xlib/nodejs/test/testAll.sh2
-rw-r--r--lib/nodejs/test/test_driver.js11
-rw-r--r--package.json1
-rw-r--r--test/JsDeepConstructorTest.thrift12
13 files changed, 737 insertions, 10 deletions
diff --git a/compiler/cpp/src/generate/t_js_generator.cc b/compiler/cpp/src/generate/t_js_generator.cc
index 970a179f9..2c90e4ced 100644
--- a/compiler/cpp/src/generate/t_js_generator.cc
+++ b/compiler/cpp/src/generate/t_js_generator.cc
@@ -41,6 +41,7 @@ static const string endl = "\n"; // avoid ostream << std::endl flushes
#include "t_oop_generator.h"
+
/**
* JS code generator.
*/
@@ -181,6 +182,8 @@ public:
+ "//\n";
}
+ t_type* get_contained_type(t_type* t);
+
std::vector<std::string> js_namespace_pieces(t_program* p) {
std::string ns = p->get_namespace("js");
@@ -601,6 +604,22 @@ void t_js_generator::generate_js_struct(t_struct* tstruct, bool is_exception) {
}
/**
+ * Return type of contained elements for a container type. For maps
+ * this is type of value (keys are always strings in js)
+ */
+t_type* t_js_generator::get_contained_type(t_type* t) {
+ t_type* etype;
+ if (t->is_list()) {
+ etype = ((t_list*)t)->get_elem_type();
+ } else if (t->is_set()) {
+ etype = ((t_set*)t)->get_elem_type();
+ } else {
+ etype = ((t_map*)t)->get_val_type();
+ }
+ return etype;
+}
+
+/**
* Generates a struct definition for a thrift data type. This is nothing in JS
* where the objects are all just associative arrays (unless of course we
* decide to start using objects for them...)
@@ -685,9 +704,47 @@ void t_js_generator::generate_js_struct_definition(ofstream& out,
}
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
out << indent() << indent() << "if (args." << (*m_iter)->get_name() << " !== undefined) {"
- << endl << indent() << indent() << indent() << "this." << (*m_iter)->get_name()
- << " = args." << (*m_iter)->get_name() << ";" << endl;
+ << endl << indent() << indent() << indent() << "this." << (*m_iter)->get_name();
+
+ if (t->is_struct()) {
+ out << (" = new " + js_type_namespace(t->get_program()) + t->get_name() +
+ "(args."+(*m_iter)->get_name() +");");
+ out << endl;
+ } else if (t->is_container()) {
+ t_type* etype = get_contained_type(t);
+ string copyFunc = t->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
+ string type_list = "";
+
+ while (etype->is_container()) {
+ if (type_list.length() > 0) {
+ type_list += ", ";
+ }
+ type_list += etype->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
+ etype = get_contained_type(etype);
+ }
+
+ if (etype->is_struct()) {
+ if (type_list.length() > 0) {
+ type_list += ", ";
+ }
+ type_list += js_type_namespace(etype->get_program()) + etype->get_name();
+ }
+ else {
+ if (type_list.length() > 0) {
+ type_list += ", ";
+ }
+ type_list += "null";
+ }
+
+ out << (" = " + copyFunc + "(args." + (*m_iter)->get_name() +
+ ", [" + type_list + "]);");
+ out << endl;
+ } else {
+ out << " = args." << (*m_iter)->get_name() << ";" << endl;
+ }
+
if (!(*m_iter)->get_req()) {
out << indent() << indent() << "} else {" << endl << indent() << indent() << indent()
<< "throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.UNKNOWN, "
diff --git a/lib/js/Gruntfile.js b/lib/js/Gruntfile.js
index 3298d5cf2..32c88346b 100644
--- a/lib/js/Gruntfile.js
+++ b/lib/js/Gruntfile.js
@@ -35,7 +35,7 @@ module.exports = function(grunt) {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
- },
+ },
shell: {
InstallThriftJS: {
command: 'mkdir test/build; mkdir test/build/js; cp src/thrift.js test/build/js/thrift.js'
@@ -48,6 +48,9 @@ module.exports = function(grunt) {
},
ThriftGenJQ: {
command: 'thrift -gen js:jquery -gen js:node -o test ../../test/ThriftTest.thrift'
+ },
+ ThriftGenDeepConstructor: {
+ command: 'thrift -gen js -o test ../../test/JsDeepConstructorTest.thrift'
}
},
external_daemon: {
@@ -123,6 +126,13 @@ module.exports = function(grunt) {
'https://localhost:8089/testws.html'
]
}
+ },
+ ThriftDeepConstructor: {
+ options: {
+ urls: [
+ 'http://localhost:8088/test-deep-constructor.html'
+ ]
+ }
}
},
jshint: {
@@ -147,13 +157,14 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-external-daemon');
grunt.loadNpmTasks('grunt-shell');
- grunt.registerTask('test', ['jshint', 'shell:InstallThriftJS', 'shell:InstallThriftNodeJSDep', 'shell:ThriftGen',
- 'external_daemon:ThriftTestServer', 'external_daemon:ThriftTestServer_TLS',
+ grunt.registerTask('test', ['jshint', 'shell:InstallThriftJS', 'shell:InstallThriftNodeJSDep', 'shell:ThriftGen',
+ 'external_daemon:ThriftTestServer', 'external_daemon:ThriftTestServer_TLS',
+ 'shell:ThriftGenDeepConstructor', 'qunit:ThriftDeepConstructor',
'qunit:ThriftJS', 'qunit:ThriftJS_TLS',
'shell:ThriftGenJQ', 'qunit:ThriftJSJQ', 'qunit:ThriftJSJQ_TLS'
]);
- grunt.registerTask('default', ['jshint', 'shell:InstallThriftJS', 'shell:InstallThriftNodeJSDep', 'shell:ThriftGen',
- 'external_daemon:ThriftTestServer', 'external_daemon:ThriftTestServer_TLS',
+ grunt.registerTask('default', ['jshint', 'shell:InstallThriftJS', 'shell:InstallThriftNodeJSDep', 'shell:ThriftGen',
+ 'external_daemon:ThriftTestServer', 'external_daemon:ThriftTestServer_TLS',
'qunit:ThriftJS', 'qunit:ThriftJS_TLS',
'shell:ThriftGenJQ', 'qunit:ThriftJSJQ', 'qunit:ThriftJSJQ_TLS',
'concat', 'uglify', 'jsdoc'
diff --git a/lib/js/src/thrift.js b/lib/js/src/thrift.js
index af45d9c47..9bd1198ae 100644
--- a/lib/js/src/thrift.js
+++ b/lib/js/src/thrift.js
@@ -1203,7 +1203,7 @@ Thrift.Protocol.prototype = {
r.size = list.shift();
this.rpos.push(this.rstack.length);
- this.rstack.push(list);
+ this.rstack.push(list.shift());
return r;
},
@@ -1439,3 +1439,69 @@ Thrift.Multiplexer.prototype.createClient = function (serviceName, SCl, transpor
+var copyList, copyMap;
+
+copyList = function(lst, types) {
+
+ if (!lst) {return lst; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var len = lst.length, result = [], i, val;
+ for (i = 0; i < len; i++) {
+ val = lst[i];
+ if (type === null) {
+ result.push(val);
+ }
+ else if (type === copyMap || type === copyList) {
+ result.push(type(val, types.slice(1)));
+ }
+ else {
+ result.push(new Type(val));
+ }
+ }
+ return result;
+};
+
+copyMap = function(obj, types){
+
+ if (!obj) {return obj; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var result = {}, val;
+ for(var prop in obj) {
+ if(obj.hasOwnProperty(prop)) {
+ val = obj[prop];
+ if (type === null) {
+ result[prop] = val;
+ }
+ else if (type === copyMap || type === copyList) {
+ result[prop] = type(val, types.slice(1));
+ }
+ else {
+ result[prop] = new Type(val);
+ }
+ }
+ }
+ return result;
+};
+
+Thrift.copyMap = copyMap;
+Thrift.copyList = copyList;
diff --git a/lib/js/test/deep-constructor.test.js b/lib/js/test/deep-constructor.test.js
new file mode 100644
index 000000000..9a1980943
--- /dev/null
+++ b/lib/js/test/deep-constructor.test.js
@@ -0,0 +1,195 @@
+function serialize(data) {
+ var transport = new Thrift.Transport("/service");
+ var protocol = new Thrift.Protocol(transport);
+ protocol.writeMessageBegin("", 0, 0);
+ data.write(protocol);
+ protocol.writeMessageEnd();
+ return transport.send_buf;
+}
+
+function deserialize(serialized, type) {
+ var transport = new Thrift.Transport("/service");
+ transport.setRecvBuffer(serialized);
+ var protocol = new Thrift.Protocol(transport);
+ protocol.readMessageBegin();
+ var data = new type();
+ data.read(protocol);
+ protocol.readMessageEnd();
+ return data;
+}
+
+
+function createThriftObj() {
+
+ return new Complex({
+
+ struct_field: new Simple({value: 'a'}),
+
+ struct_list_field: [
+ new Simple({value: 'b'}),
+ new Simple({value: 'c'}),
+ ],
+
+ struct_set_field: [
+ new Simple({value: 'd'}),
+ new Simple({value: 'e'}),
+ ],
+
+ struct_map_field: {
+ A: new Simple({value: 'f'}),
+ B: new Simple({value: 'g'})
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ new Simple({value: 'h'}),
+ new Simple({value: 'i'})
+ ]
+ }
+ ]
+ ],
+
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: new Simple({value: 'j'})
+ },
+ {
+ DB: new Simple({value: 'k'})
+ }
+ ]
+ }
+ }
+ );
+}
+
+
+function createJsObj() {
+
+ return {
+
+ struct_field: {value: 'a'},
+
+ struct_list_field: [
+ {value: 'b'},
+ {value: 'c'},
+ ],
+
+ struct_set_field: [
+ {value: 'd'},
+ {value: 'e'},
+ ],
+
+ struct_map_field: {
+ A: {value: 'f'},
+ B: {value: 'g'}
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ {value: 'h'},
+ {value: 'i'}
+ ]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: {value: 'j'}
+ },
+ {
+ DB: {value: 'k'}
+ }
+ ]
+ }
+ };
+}
+
+
+function assertValues(obj, assert) {
+ assert.equal(obj.struct_field.value, 'a');
+ assert.equal(obj.struct_list_field[0].value, 'b');
+ assert.equal(obj.struct_list_field[1].value, 'c');
+ assert.equal(obj.struct_set_field[0].value, 'd');
+ assert.equal(obj.struct_set_field[1].value, 'e');
+ assert.equal(obj.struct_map_field.A.value, 'f');
+ assert.equal(obj.struct_map_field.B.value, 'g');
+ assert.equal(obj.struct_nested_containers_field[0][0].C[0].value, 'h');
+ assert.equal(obj.struct_nested_containers_field[0][0].C[1].value, 'i');
+ assert.equal(obj.struct_nested_containers_field2.D[0].DA.value, 'j');
+ assert.equal(obj.struct_nested_containers_field2.D[1].DB.value, 'k');
+}
+
+var cases = {
+
+ "Serialize/deserialize simple struct should return equal object": function(assert){
+ var tObj = new Simple({value: 'a'});
+ var received = deserialize(serialize(tObj), Simple);
+ assert.ok(tObj !== received);
+ assert.deepEqual(received, tObj);
+ },
+
+
+ "Serialize/deserialize should return equal object": function(assert){
+ var tObj = createThriftObj();
+ var received = deserialize(serialize(tObj), Complex);
+ assert.ok(tObj !== received);
+ assert.deepEqual(received, tObj);
+ },
+
+ "Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(assert) {
+ var tObj1 = createThriftObj();
+ var tObj2 = new Complex(createJsObj());
+ assertValues(tObj2, assert);
+ assert.equal(serialize(tObj2), serialize(tObj1));
+ },
+
+ "Modifications to args object should not affect constructed Thrift object": function (assert) {
+
+ var args = createJsObj();
+ assertValues(args, assert);
+
+ var tObj = new Complex(args);
+ assertValues(tObj, assert);
+
+ args.struct_field.value = 'ZZZ';
+ args.struct_list_field[0].value = 'ZZZ';
+ args.struct_list_field[1].value = 'ZZZ';
+ args.struct_set_field[0].value = 'ZZZ';
+ args.struct_set_field[1].value = 'ZZZ';
+ args.struct_map_field.A.value = 'ZZZ';
+ args.struct_map_field.B.value = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[0] = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[1] = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DA = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DB = 'ZZZ';
+
+ assertValues(tObj, assert);
+ },
+
+ "nulls are ok": function(assert) {
+ var tObj = new Complex({
+ struct_field: null,
+ struct_list_field: null,
+ struct_set_field: null,
+ struct_map_field: null,
+ struct_nested_containers_field: null,
+ struct_nested_containers_field2: null
+ });
+ var received = deserialize(serialize(tObj), Complex);
+ assert.ok(tObj !== received);
+ assert.deepEqual(tObj, received);
+ }
+
+};
+
+Object.keys(cases).forEach(function(caseName) {
+ test(caseName, cases[caseName]);
+});
diff --git a/lib/js/test/test-deep-constructor.html b/lib/js/test/test-deep-constructor.html
new file mode 100755
index 000000000..5835dc84b
--- /dev/null
+++ b/lib/js/test/test-deep-constructor.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Thrift Javascript Bindings: Unit Test</title>
+
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/JsDeepConstructorTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <!-- jQuery -->
+ <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.js" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="http://code.jquery.com/qunit/qunit-1.14.0.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.14.0.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="deep-constructor.test.js" charset="utf-8"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Thrift Javascript Bindings: Deep Constructor Test (<a href="https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob;f=test/JsDeepConstructorTest.thrift;hb=HEAD">JsDeepConstructorTest.thrift</a>)</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+</body>
+</html>
diff --git a/lib/nodejs/lib/thrift/json_protocol.js b/lib/nodejs/lib/thrift/json_protocol.js
index 77339f7f1..e98650c50 100644
--- a/lib/nodejs/lib/thrift/json_protocol.js
+++ b/lib/nodejs/lib/thrift/json_protocol.js
@@ -546,9 +546,15 @@ TJSONProtocol.prototype.readFieldEnd = function() {
*/
TJSONProtocol.prototype.readMapBegin = function() {
var map = this.rstack.pop();
+ var first = map.shift();
+ if (first instanceof Array) {
+ this.rstack.push(map);
+ map = first;
+ first = map.shift();
+ }
var r = {};
- r.ktype = TJSONProtocol.RType[map.shift()];
+ r.ktype = TJSONProtocol.RType[first];
r.vtype = TJSONProtocol.RType[map.shift()];
r.size = map.shift();
@@ -582,7 +588,7 @@ TJSONProtocol.prototype.readListBegin = function() {
r.size = list.shift();
this.rpos.push(this.rstack.length);
- this.rstack.push(list);
+ this.rstack.push(list.shift());
return r;
};
diff --git a/lib/nodejs/lib/thrift/thrift.js b/lib/nodejs/lib/thrift/thrift.js
index 89c789d9f..e6edf9efe 100644
--- a/lib/nodejs/lib/thrift/thrift.js
+++ b/lib/nodejs/lib/thrift/thrift.js
@@ -151,3 +151,70 @@ exports.objectLength = function(obj) {
exports.inherits = function(constructor, superConstructor) {
util.inherits(constructor, superConstructor);
};
+
+var copyList, copyMap;
+
+copyList = function(lst, types) {
+
+ if (!lst) {return lst; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var len = lst.length, result = [], i, val;
+ for (i = 0; i < len; i++) {
+ val = lst[i];
+ if (type === null) {
+ result.push(val);
+ }
+ else if (type === copyMap || type === copyList) {
+ result.push(type(val, types.slice(1)));
+ }
+ else {
+ result.push(new Type(val));
+ }
+ }
+ return result;
+};
+
+copyMap = function(obj, types){
+
+ if (!obj) {return obj; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var result = {}, val;
+ for(var prop in obj) {
+ if(obj.hasOwnProperty(prop)) {
+ val = obj[prop];
+ if (type === null) {
+ result[prop] = val;
+ }
+ else if (type === copyMap || type === copyList) {
+ result[prop] = type(val, types.slice(1));
+ }
+ else {
+ result[prop] = new Type(val);
+ }
+ }
+ }
+ return result;
+};
+
+module.exports.copyMap = copyMap;
+module.exports.copyList = copyList;
diff --git a/lib/nodejs/test/deep-constructor.test.js b/lib/nodejs/test/deep-constructor.test.js
new file mode 100644
index 000000000..41e7dd0b6
--- /dev/null
+++ b/lib/nodejs/test/deep-constructor.test.js
@@ -0,0 +1,233 @@
+var ttypes = require('./gen-nodejs/JsDeepConstructorTest_types');
+var thrift = require('thrift');
+var test = require('tape');
+var bufferEquals = require('buffer-equals');
+
+function serializeBinary(data) {
+ var buff;
+ var transport = new thrift.TBufferedTransport(null, function(msg){
+ buff = msg;
+ });
+ var prot = new thrift.TBinaryProtocol(transport);
+ data.write(prot);
+ prot.flush();
+ return buff;
+
+}
+
+
+function deserializeBinary(serialized, type) {
+ var t = new thrift.TFramedTransport(serialized);
+ var p = new thrift.TBinaryProtocol(t);
+ var data = new type();
+ data.read(p);
+ return data;
+}
+
+
+function serializeJSON(data) {
+ var buff;
+ var transport = new thrift.TBufferedTransport(null, function(msg){
+ buff = msg;
+ });
+ var protocol = new thrift.TJSONProtocol(transport);
+ protocol.writeMessageBegin("", 0, 0);
+ data.write(protocol);
+ protocol.writeMessageEnd();
+ protocol.flush();
+ return buff;
+}
+
+
+function deserializeJSON(serialized, type) {
+ var transport = new thrift.TFramedTransport(serialized);
+ var protocol = new thrift.TJSONProtocol(transport);
+ protocol.readMessageBegin();
+ var data = new type();
+ data.read(protocol);
+ protocol.readMessageEnd();
+ return data;
+}
+
+
+function createThriftObj() {
+
+ return new ttypes.Complex({
+
+ struct_field: new ttypes.Simple({value: 'a'}),
+
+ struct_list_field: [
+ new ttypes.Simple({value: 'b'}),
+ new ttypes.Simple({value: 'c'}),
+ ],
+
+ struct_set_field: [
+ new ttypes.Simple({value: 'd'}),
+ new ttypes.Simple({value: 'e'}),
+ ],
+
+ struct_map_field: {
+ A: new ttypes.Simple({value: 'f'}),
+ B: new ttypes.Simple({value: 'g'})
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ new ttypes.Simple({value: 'h'}),
+ new ttypes.Simple({value: 'i'})
+ ]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: new ttypes.Simple({value: 'j'})
+ },
+ {
+ DB: new ttypes.Simple({value: 'k'})
+ }
+ ]
+ }
+ }
+ );
+}
+
+
+function createJsObj() {
+
+ return {
+
+ struct_field: {value: 'a'},
+
+ struct_list_field: [
+ {value: 'b'},
+ {value: 'c'},
+ ],
+
+ struct_set_field: [
+ {value: 'd'},
+ {value: 'e'},
+ ],
+
+ struct_map_field: {
+ A: {value: 'f'},
+ B: {value: 'g'}
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ {value: 'h'},
+ {value: 'i'}
+ ]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: {value: 'j'}
+ },
+ {
+ DB: {value: 'k'}
+ }
+ ]
+ }
+ };
+}
+
+
+function assertValues(obj, assert) {
+ assert.equals(obj.struct_field.value, 'a');
+ assert.equals(obj.struct_list_field[0].value, 'b');
+ assert.equals(obj.struct_list_field[1].value, 'c');
+ assert.equals(obj.struct_set_field[0].value, 'd');
+ assert.equals(obj.struct_set_field[1].value, 'e');
+ assert.equals(obj.struct_map_field.A.value, 'f');
+ assert.equals(obj.struct_map_field.B.value, 'g');
+ assert.equals(obj.struct_nested_containers_field[0][0].C[0].value, 'h');
+ assert.equals(obj.struct_nested_containers_field[0][0].C[1].value, 'i');
+ assert.equals(obj.struct_nested_containers_field2.D[0].DA.value, 'j');
+ assert.equals(obj.struct_nested_containers_field2.D[1].DB.value, 'k');
+}
+
+function createTestCases(serialize, deserialize) {
+
+ var cases = {
+
+ "Serialize/deserialize should return equal object": function(assert){
+ var tObj = createThriftObj();
+ var received = deserialize(serialize(tObj), ttypes.Complex);
+ assert.ok(tObj !== received, 'not the same object');
+ assert.deepEqual(tObj, received);
+ assert.end();
+ },
+
+ "Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(assert) {
+ var tObj1 = createThriftObj();
+ var tObj2 = new ttypes.Complex(createJsObj());
+ assertValues(tObj2, assert);
+ var s1 = serialize(tObj1);
+ var s2 = serialize(tObj2);
+ assert.ok(bufferEquals(s1, s2));
+ assert.end();
+ },
+
+ "Modifications to args object should not affect constructed Thrift object": function (assert) {
+
+ var args = createJsObj();
+ assertValues(args, assert);
+
+ var tObj = new ttypes.Complex(args);
+ assertValues(tObj, assert);
+
+ args.struct_field.value = 'ZZZ';
+ args.struct_list_field[0].value = 'ZZZ';
+ args.struct_list_field[1].value = 'ZZZ';
+ args.struct_set_field[0].value = 'ZZZ';
+ args.struct_set_field[1].value = 'ZZZ';
+ args.struct_map_field.A.value = 'ZZZ';
+ args.struct_map_field.B.value = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[0] = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[1] = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DA = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DB = 'ZZZ';
+
+ assertValues(tObj, assert);
+ assert.end();
+ },
+
+ "nulls are ok": function(assert) {
+ var tObj = new ttypes.Complex({
+ struct_field: null,
+ struct_list_field: null,
+ struct_set_field: null,
+ struct_map_field: null,
+ struct_nested_containers_field: null,
+ struct_nested_containers_field2: null
+ });
+ var received = deserialize(serialize(tObj), ttypes.Complex);
+ assert.ok(tObj !== received);
+ assert.deepEqual(tObj, received);
+ assert.end();
+ }
+
+ };
+ return cases;
+}
+
+
+function run(name, cases){
+ Object.keys(cases).forEach(function(caseName) {
+ test(name + ': ' + caseName, cases[caseName]);
+ });
+}
+
+run('binary', createTestCases(serializeBinary, deserializeBinary));
+run('json', createTestCases(serializeJSON, deserializeJSON));
diff --git a/lib/nodejs/test/test-cases.js b/lib/nodejs/test/test-cases.js
index c396ca9bd..787229533 100644
--- a/lib/nodejs/test/test-cases.js
+++ b/lib/nodejs/test/test-cases.js
@@ -107,6 +107,22 @@ var crazy = new ttypes.Insanity({
})]
});
+var crazy2 = new ttypes.Insanity({
+ "userMap":{ "5":5, "8":8 },
+ "xtructs":[{
+ "string_thing":"Goodbye4",
+ "byte_thing":4,
+ "i32_thing":4,
+ "i64_thing":4
+ }, {
+ "string_thing":"Hello2",
+ "byte_thing":2,
+ "i32_thing":2,
+ "i64_thing":2
+ }]
+});
+
+
var insanity = {
"1":{ "2": crazy, "3": crazy },
"2":{ "6":{ "userMap":{}, "xtructs":[] } }
@@ -119,4 +135,5 @@ module.exports.deep = deep;
module.exports.out = out;
module.exports.out2 = out2;
module.exports.crazy = crazy;
+module.exports.crazy2 = crazy2;
module.exports.insanity = insanity;
diff --git a/lib/nodejs/test/testAll.sh b/lib/nodejs/test/testAll.sh
index fd1142585..38b284ac9 100755
--- a/lib/nodejs/test/testAll.sh
+++ b/lib/nodejs/test/testAll.sh
@@ -71,10 +71,12 @@ TESTOK=0
#generating thrift code
${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/ThriftTest.thrift
+${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/JsDeepConstructorTest.thrift
#unit tests
node ${DIR}/binary.test.js || TESTOK=1
+node ${DIR}/deep-constructor.test.js || TESTOK=1
#integration tests
diff --git a/lib/nodejs/test/test_driver.js b/lib/nodejs/test/test_driver.js
index 6e472adf4..27ffd63ec 100644
--- a/lib/nodejs/test/test_driver.js
+++ b/lib/nodejs/test/test_driver.js
@@ -80,6 +80,11 @@ exports.ThriftTestDriver = function(client, callback) {
checkRecursively(testCases.insanity, response, 'testInsanity');
});
+ client.testInsanity(testCases.crazy2, function(err, response) {
+ assert.error(err, 'testInsanity2: no callback error');
+ checkRecursively(testCases.insanity, response, 'testInsanity2');
+ });
+
client.testException('TException', function(err, response) {
assert.ok(err instanceof TException, 'testException: correct error type');
assert.ok(!response, 'testException: no response');
@@ -161,6 +166,12 @@ exports.ThriftTestDriverPromise = function(client, callback) {
})
.fail(fail('testInsanity'));
+ client.testInsanity(testCases.crazy2)
+ .then(function(response) {
+ checkRecursively(testCases.insanity, response, 'testInsanity2');
+ })
+ .fail(fail('testInsanity2'));
+
client.testException('TException')
.then(function(response) {
fail('testException: TException');
diff --git a/package.json b/package.json
index 0fbb80eaa..c4688b62f 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
"ws": "~0.4.32"
},
"devDependencies": {
+ "buffer-equals": "^1.0.3",
"commander": "2.1.x",
"connect": "2.7.x",
"istanbul": "^0.3.5",
diff --git a/test/JsDeepConstructorTest.thrift b/test/JsDeepConstructorTest.thrift
new file mode 100644
index 000000000..9150854b2
--- /dev/null
+++ b/test/JsDeepConstructorTest.thrift
@@ -0,0 +1,12 @@
+struct Simple {
+ 1: string value
+}
+
+struct Complex {
+ 1: Simple struct_field
+ 2: list<Simple> struct_list_field
+ 3: set<Simple> struct_set_field
+ 4: map<string,Simple> struct_map_field
+ 5: list<set<map<string,list<Simple>>>> struct_nested_containers_field
+ 6: map<string, list<map<string,Simple>> > struct_nested_containers_field2
+}