Implement episodic compilation for js code generation

This commit is contained in:
Mustafa Senol Cosar 2019-03-01 18:57:09 +03:00 committed by James E. King III
parent 96c6113dda
commit 3f0d444791
11 changed files with 411 additions and 38 deletions

View File

@ -65,7 +65,7 @@ thrift_SOURCES = src/thrift/audit/t_audit.cpp \
src/thrift/parse/t_type.h \
src/thrift/parse/t_typedef.cc \
src/thrift/parse/t_typedef.h \
src/thrift/platform.h
src/thrift/platform.h
# Specific client generator source
thrift_SOURCES += src/thrift/generate/t_as3_generator.cc \

View File

@ -26,6 +26,7 @@
#include <vector>
#include <list>
#include <cassert>
#include <unordered_map>
#include <stdlib.h>
#include <sys/stat.h>
@ -38,9 +39,11 @@ using std::ostream;
using std::ostringstream;
using std::string;
using std::stringstream;
using std::unordered_map;
using std::vector;
static const string endl = "\n"; // avoid ostream << std::endl flushes
static const string episode_file_name = "thrift.js.episode";
// largest consecutive integer representable by a double (2 ^ 53 - 1)
static const int64_t max_safe_integer = 0x1fffffffffffff;
// smallest consecutive number representable by a double (-2 ^ 53 + 1)
@ -65,6 +68,7 @@ public:
gen_jquery_ = false;
gen_ts_ = false;
gen_es6_ = false;
gen_episode_file_ = false;
bool with_ns_ = false;
@ -79,22 +83,26 @@ public:
with_ns_ = true;
} else if( iter->first.compare("es6") == 0) {
gen_es6_ = true;
} else if( iter->first.compare("imports") == 0) {
parse_imports(program, iter->second);
} else if (iter->first.compare("thrift_package_output_directory") == 0) {
parse_thrift_package_output_directory(iter->second);
} else {
throw "unknown option js:" + iter->first;
throw std::invalid_argument("unknown option js:" + iter->first);
}
}
if (gen_es6_ && gen_jquery_) {
throw "Invalid switch: [-gen js:es6,jquery] options not compatible";
throw std::invalid_argument("invalid switch: [-gen js:es6,jquery] options not compatible");
}
if (gen_node_ && gen_jquery_) {
throw "Invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen "
"js:jquery]";
throw std::invalid_argument("invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen "
"js:jquery]");
}
if (!gen_node_ && with_ns_) {
throw "Invalid switch: [-gen js:with_ns] is only valid when using node.js";
throw std::invalid_argument("invalid switch: [-gen js:with_ns] is only valid when using node.js");
}
// Depending on the processing flags, we will update these to be ES6 compatible
@ -207,6 +215,7 @@ public:
std::string ts_service_includes();
std::string render_includes();
std::string render_ts_includes();
std::string get_import_path(t_program* program);
std::string declare_field(t_field* tfield, bool init = false, bool obj = false);
std::string function_signature(t_function* tfunction,
std::string prefix = "",
@ -215,6 +224,13 @@ public:
std::string type_to_enum(t_type* ttype);
std::string make_valid_nodeJs_identifier(std::string const& name);
/**
* Helper parser functions
*/
void parse_imports(t_program* program, const std::string& imports_string);
void parse_thrift_package_output_directory(const std::string& thrift_package_output_directory);
std::string autogen_comment() override {
return std::string("//\n") + "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
@ -353,6 +369,11 @@ private:
*/
bool gen_es6_;
/**
* True if we will generate an episode file.
*/
bool gen_episode_file_;
/**
* The name of the defined module(s), for TypeScript Definition Files.
*/
@ -363,6 +384,21 @@ private:
*/
bool no_ns_;
/**
* The node modules to use when importing the previously generated files.
*/
vector<string> imports;
/**
* Cache for imported modules.
*/
unordered_map<string, string> module_name_2_import_path;
/**
* The prefix to use when generating the episode file.
*/
string thrift_package_output_directory_;
/**
* The variable decorator for "const" variables. Will default to "var" if in an incompatible language.
*/
@ -381,6 +417,7 @@ private:
/**
* File streams
*/
ofstream_with_content_based_conditional_update f_episode_;
ofstream_with_content_based_conditional_update f_types_;
ofstream_with_content_based_conditional_update f_service_;
ofstream_with_content_based_conditional_update f_types_ts_;
@ -397,14 +434,23 @@ void t_js_generator::init_generator() {
// Make output directory
MKDIR(get_out_dir().c_str());
string outdir = get_out_dir();
const auto outdir = get_out_dir();
// Make output file(s)
string f_types_name = outdir + program_->get_name() + "_types.js";
if (gen_episode_file_) {
const auto f_episode_file_path = outdir + episode_file_name;
f_episode_.open(f_episode_file_path);
}
const auto f_types_name = outdir + program_->get_name() + "_types.js";
f_types_.open(f_types_name.c_str());
if (gen_episode_file_) {
const auto types_module = program_->get_name() + "_types";
f_episode_ << types_module << ":" << thrift_package_output_directory_ << "/" << types_module << endl;
}
if (gen_ts_) {
string f_types_ts_name = outdir + program_->get_name() + "_types.d.ts";
const auto f_types_ts_name = outdir + program_->get_name() + "_types.d.ts";
f_types_ts_.open(f_types_ts_name.c_str());
}
@ -500,8 +546,7 @@ string t_js_generator::render_includes() {
if (gen_node_) {
const vector<t_program*>& includes = program_->get_includes();
for (auto include : includes) {
result += js_const_type_ + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('./" + include->get_name()
+ "_types');\n";
result += js_const_type_ + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('" + get_import_path(include) + "');\n";
}
if (includes.size() > 0) {
result += "\n";
@ -522,8 +567,7 @@ string t_js_generator::render_ts_includes() {
}
const vector<t_program*>& includes = program_->get_includes();
for (auto include : includes) {
result += "import " + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('./" + include->get_name()
+ "_types');\n";
result += "import " + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('" + get_import_path(include) + "');\n";
}
if (includes.size() > 0) {
result += "\n";
@ -532,6 +576,21 @@ string t_js_generator::render_ts_includes() {
return result;
}
string t_js_generator::get_import_path(t_program* program) {
const string import_file_name(program->get_name() + "_types");
if (program->get_recursive()) {
return "./" + import_file_name;
}
const string import_file_name_with_extension = import_file_name + ".js";
auto module_name_and_import_path_iterator = module_name_2_import_path.find(import_file_name);
if (module_name_and_import_path_iterator != module_name_2_import_path.end()) {
return module_name_and_import_path_iterator->second;
}
return "./" + import_file_name;
}
/**
* Close up (or down) some filez.
*/
@ -546,6 +605,9 @@ void t_js_generator::close_generator() {
}
f_types_ts_.close();
}
if (gen_episode_file_){
f_episode_.close();
}
}
/**
@ -657,7 +719,7 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
}
break;
default:
throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
throw std::runtime_error("compiler error: no const of base type " + t_base_type::t_base_name(tbase));
}
} else if (type->is_enum()) {
out << value->get_integer();
@ -676,7 +738,7 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
}
}
if (field_type == NULL) {
throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
throw std::runtime_error("type error: " + type->get_name() + " has no field " + v_iter->first->get_string());
}
if (v_iter != val.begin())
out << ",";
@ -698,7 +760,7 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
if (v_iter != val.begin())
out << "," << endl;
if (ktype->is_base_type() && ((t_base_type*)get_true_type(ktype))->get_base() == t_base_type::TYPE_I64){
out << indent() << "\"" << v_iter->first->get_integer() << "\"";
} else {
@ -846,7 +908,6 @@ void t_js_generator::generate_js_struct_definition(ostream& out,
f_types_ts_ << ts_indent() << (*m_iter)->get_name() << ": "
<< ts_get_type((*m_iter)->get_type()) << ";" << endl;
}
}
}
@ -1124,6 +1185,9 @@ void t_js_generator::generate_js_struct_writer(ostream& out, t_struct* tstruct)
void t_js_generator::generate_service(t_service* tservice) {
string f_service_name = get_out_dir() + service_name_ + ".js";
f_service_.open(f_service_name.c_str());
if (gen_episode_file_) {
f_episode_ << service_name_ << ":" << thrift_package_output_directory_ << "/" << service_name_ << endl;
}
if (gen_ts_) {
string f_service_ts_name = get_out_dir() + service_name_ + ".d.ts";
@ -2136,7 +2200,7 @@ void t_js_generator::generate_deserialize_field(ostream& out,
t_type* type = get_true_type(tfield->get_type());
if (type->is_void()) {
throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
}
string name = prefix + tfield->get_name();
@ -2152,7 +2216,7 @@ void t_js_generator::generate_deserialize_field(ostream& out,
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
switch (tbase) {
case t_base_type::TYPE_VOID:
throw "compiler error: cannot serialize void field in a struct: " + name;
throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
break;
case t_base_type::TYPE_STRING:
out << (type->is_binary() ? "readBinary()" : "readString()");
@ -2176,7 +2240,7 @@ void t_js_generator::generate_deserialize_field(ostream& out,
out << "readDouble()";
break;
default:
throw "compiler error: no JS name for base type " + t_base_type::t_base_name(tbase);
throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
}
} else if (type->is_enum()) {
out << "readI32()";
@ -2317,7 +2381,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str
// Do nothing for void types
if (type->is_void()) {
throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
throw std::runtime_error("CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
}
if (type->is_struct() || type->is_xception()) {
@ -2338,7 +2402,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
switch (tbase) {
case t_base_type::TYPE_VOID:
throw "compiler error: cannot serialize void field in a struct: " + name;
throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
break;
case t_base_type::TYPE_STRING:
out << (type->is_binary() ? "writeBinary(" : "writeString(") << name << ")";
@ -2362,7 +2426,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str
out << "writeDouble(" << name << ")";
break;
default:
throw "compiler error: no JS name for base type " + t_base_type::t_base_name(tbase);
throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
}
} else if (type->is_enum()) {
out << "writeI32(" << name << ")";
@ -2510,7 +2574,7 @@ string t_js_generator::declare_field(t_field* tfield, bool init, bool obj) {
result += " = null";
break;
default:
throw "compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase);
throw std::runtime_error("compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase));
}
} else if (type->is_enum()) {
result += " = null";
@ -2589,7 +2653,7 @@ string t_js_generator::type_to_enum(t_type* type) {
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
switch (tbase) {
case t_base_type::TYPE_VOID:
throw "NO T_VOID CONSTRUCT";
throw std::runtime_error("NO T_VOID CONSTRUCT");
case t_base_type::TYPE_STRING:
return "Thrift.Type.STRING";
case t_base_type::TYPE_BOOL:
@ -2617,7 +2681,7 @@ string t_js_generator::type_to_enum(t_type* type) {
return "Thrift.Type.LIST";
}
throw "INVALID TYPE IN type_to_enum: " + type->get_name();
throw std::runtime_error("INVALID TYPE IN type_to_enum: " + type->get_name());
}
/**
@ -2785,10 +2849,80 @@ std::string t_js_generator::make_valid_nodeJs_identifier(std::string const& name
return str;
}
void t_js_generator::parse_imports(t_program* program, const std::string& imports_string) {
if (program->get_recursive()) {
throw std::invalid_argument("[-gen js:imports=] option is not usable in recursive code generation mode");
}
std::stringstream sstream(imports_string);
std::string import;
while (std::getline(sstream, import, ':')) {
imports.emplace_back(import);
}
if (imports.empty()) {
throw std::invalid_argument("invalid usage: [-gen js:imports=] requires at least one path "
"(multiple paths are separated by ':')");
}
for (auto& import : imports) {
// Strip trailing '/'
if (!import.empty() && import[import.size() - 1] == '/') {
import = import.substr(0, import.size() - 1);
}
if (import.empty()) {
throw std::invalid_argument("empty paths are not allowed in imports");
}
std::ifstream episode_file;
string line;
const auto episode_file_path = import + "/" + episode_file_name;
episode_file.open(episode_file_path);
if (!episode_file) {
throw std::runtime_error("failed to open the file '" + episode_file_path + "'");
}
while (std::getline(episode_file, line)) {
const auto separator_position = line.find(':');
if (separator_position == string::npos) {
// could not find the separator in the line
throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the line '" + line
+ "' does not have a key:value separator ':'");
}
const auto module_name = line.substr(0, separator_position);
const auto import_path = line.substr(separator_position + 1);
if (module_name.empty()) {
throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the module name is empty");
}
if (import_path.empty()) {
throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the import path is empty");
}
const auto module_import_path = import.substr(import.find_last_of('/') + 1) + "/" + import_path;
const auto result = module_name_2_import_path.emplace(module_name, module_import_path);
if (!result.second) {
throw std::runtime_error("multiple providers of import path found for " + module_name
+ "\n\t" + module_import_path + "\n\t" + result.first->second);
}
}
}
}
void t_js_generator::parse_thrift_package_output_directory(const std::string& thrift_package_output_directory) {
thrift_package_output_directory_ = thrift_package_output_directory;
// Strip trailing '/'
if (!thrift_package_output_directory_.empty() && thrift_package_output_directory_[thrift_package_output_directory_.size() - 1] == '/') {
thrift_package_output_directory_ = thrift_package_output_directory_.substr(0, thrift_package_output_directory_.size() - 1);
}
// Check that the thrift_package_output_directory is not empty after stripping
if (thrift_package_output_directory_.empty()) {
throw std::invalid_argument("the thrift_package_output_directory argument must not be empty");
} else {
gen_episode_file_ = true;
}
}
THRIFT_REGISTER_GENERATOR(js,
"Javascript",
" jquery: Generate jQuery compatible code.\n"
" node: Generate node.js compatible code.\n"
" ts: Generate TypeScript definition files.\n"
" with_ns: Create global namespace objects when using node.js\n"
" es6: Create ES6 code with Promises\n")
" es6: Create ES6 code with Promises\n"
" thrift_package_output_directory=<path>:\n"
" Generate episode file and use the <path> as prefix\n"
" imports=<paths_to_modules>:\n"
" ':' separated list of paths of modules that has episode files in their root\n")

View File

@ -982,6 +982,7 @@ void parse(t_program* program, t_program* parent_program) {
void generate(t_program* program, const vector<string>& generator_strings) {
// Oooohh, recursive code generation, hot!!
if (gen_recurse) {
program->set_recursive(true);
const vector<t_program*>& includes = program->get_includes();
for (auto include : includes) {
// Propagate output path from parent to child programs
@ -1017,6 +1018,8 @@ void generate(t_program* program, const vector<string>& generator_strings) {
failure("Error: %s\n", s.c_str());
} catch (const char* exc) {
failure("Error: %s\n", exc);
} catch (const std::invalid_argument& invalid_argument_exception) {
failure("Error: %s\n", invalid_argument_exception.what());
}
}

View File

@ -58,9 +58,9 @@
class t_program : public t_doc {
public:
t_program(std::string path, std::string name)
: path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope) {}
: path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope), recursive_(false) {}
t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) {
t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false), recursive_(false) {
name_ = program_name(path);
scope_ = new t_scope();
}
@ -355,6 +355,10 @@ public:
const std::vector<std::string>& get_c_includes() const { return c_includes_; }
void set_recursive(const bool recursive) { recursive_ = recursive; }
bool get_recursive() const { return recursive_; }
private:
// File path
std::string path_;
@ -400,6 +404,9 @@ private:
// C extra includes
std::vector<std::string> c_includes_;
// Recursive code generation
bool recursive_;
};
#endif

View File

@ -34,6 +34,8 @@ check: deps
clean-local:
$(RM) -r test/gen-*
$(RM) -r $(top_srcdir)/node_modules
$(RM) -r test/episodic-code-generation-test/gen*
$(RM) -r test/episodic-code-generation-test/node_modules
EXTRA_DIST = \
examples \

View File

@ -0,0 +1,77 @@
#!/usr/bin/env node
/*
* 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.
*/
const assert = require("assert");
const test = require("tape");
const thrift = require("thrift");
const program = require("commander");
program
.option("--host <host>", "Set the thrift server host to connect", "localhost")
.option("--port <port>", "Set the thrift server port number to connect", 9090)
.parse(process.argv);
const Service = require("./gen-2/second-episode/gen-nodejs/Service");
const Types = require("types-package/first-episode/Types_types");
const host = program.host;
const port = program.port;
const options = {
transport: thrift.TBufferedTransport,
protocol: thrift.TJSONProtocol
};
const connection = thrift.createConnection(host, port, options);
const testDriver = function(client, callback) {
test("NodeJS episodic compilation client-server test", function(assert) {
const type1Object = new Types.Type1();
type1Object.number = 42;
type1Object.message = "The answer";
client.testEpisode(type1Object, function(err, response) {
assert.error(err, "no callback error");
assert.equal(response.number, type1Object.number + 1);
assert.equal(
response.message,
type1Object.message + " [Hello from the server]"
);
assert.end();
callback("Server successfully tested");
});
});
};
connection.on("error", function(err) {
assert(false, err);
});
const client = thrift.createClient(Service, connection);
runTests();
function runTests() {
testDriver(client, function(status) {
console.log(status);
connection.destroy();
});
}
exports.expressoTest = function() {};

View File

@ -0,0 +1,3 @@
{
"name": "types-package"
}

View File

@ -0,0 +1,50 @@
#!/usr/bin/env node
/*
* 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.
*/
const thrift = require("../../lib/thrift");
const program = require("commander");
program
.option("--port <port>", "Set the thrift server port", 9090)
.parse(process.argv);
const Service = require("./gen-2/second-episode/gen-nodejs/Service");
const Types = require("types-package/first-episode/Types_types");
const port = program.port;
const options = {
transport: thrift.TBufferedTransport,
protocol: thrift.TJSONProtocol
};
const ServiceHandler = {
testEpisode: function(receivedType1Object) {
const type1Object = new Types.Type1();
type1Object.number = receivedType1Object.number + 1;
type1Object.message =
receivedType1Object.message + " [Hello from the server]";
return type1Object;
}
};
const server = thrift.createServer(Service, ServiceHandler, options);
server.listen(port);

View File

@ -23,6 +23,12 @@ fi
DIR="$( cd "$( dirname "$0" )" && pwd )"
EPISODIC_DIR=${DIR}/episodic-code-generation-test
THRIFT_FILES_DIR=${DIR}/../../../test
THRIFT_COMPILER=${DIR}/../../../compiler/cpp/thrift
ISTANBUL="$DIR/../../../node_modules/istanbul/lib/cli.js"
REPORT_PREFIX="${DIR}/../coverage/report"
@ -54,26 +60,68 @@ testServer()
return $RET
}
testEpisodicCompilation()
{
RET=0
if [ -n "${COVER}" ]; then
${ISTANBUL} cover ${EPISODIC_DIR}/server.js --dir ${REPORT_PREFIX}${COUNT} --handle-sigint &
COUNT=$((COUNT+1))
else
node ${EPISODIC_DIR}/server.js &
fi
SERVERPID=$!
sleep 0.1
if [ -n "${COVER}" ]; then
${ISTANBUL} cover ${EPISODIC_DIR}/client.js --dir ${REPORT_PREFIX}${COUNT} || RET=1
COUNT=$((COUNT+1))
else
node ${EPISODIC_DIR}/client.js || RET=1
fi
kill -2 $SERVERPID || RET=1
wait $SERVERPID
return $RET
}
TESTOK=0
#generating thrift code
# 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
${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/Int64Test.thrift
${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/ThriftTest.thrift
${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift
${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/Int64Test.thrift
mkdir ${DIR}/gen-nodejs-es6
${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/ThriftTest.thrift
${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/JsDeepConstructorTest.thrift
${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/Int64Test.thrift
${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/ThriftTest.thrift
${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift
${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/Int64Test.thrift
#unit tests
# generate episodic compilation test code
TYPES_PACKAGE=${EPISODIC_DIR}/node_modules/types-package
# generate the first episode
mkdir --parents ${EPISODIC_DIR}/gen-1/first-episode
${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-1/first-episode --gen js:node,thrift_package_output_directory=first-episode ${THRIFT_FILES_DIR}/Types.thrift
# create a "package" from the first episode and "install" it, the episode file must be at the module root
mkdir --parents ${TYPES_PACKAGE}/first-episode
cp --force ${EPISODIC_DIR}/episodic_compilation.package.json ${TYPES_PACKAGE}/package.json
cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/Types_types.js ${TYPES_PACKAGE}/first-episode/
cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/thrift.js.episode ${TYPES_PACKAGE}
# generate the second episode
mkdir --parents ${EPISODIC_DIR}/gen-2/second-episode
${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-2/second-episode --gen js:node,imports=${TYPES_PACKAGE} ${THRIFT_FILES_DIR}/Service.thrift
if [ -f ${EPISODIC_DIR}/gen-2/second-episode/Types_types.js ]; then
TESTOK=1
fi
# unit tests
node ${DIR}/binary.test.js || TESTOK=1
node ${DIR}/int64.test.js || TESTOK=1
node ${DIR}/deep-constructor.test.js || TESTOK=1
#integration tests
# integration tests
for type in tcp multiplex websocket http
do
@ -91,6 +139,8 @@ do
done
done
# episodic compilation test
testEpisodicCompilation
if [ -n "${COVER}" ]; then
${ISTANBUL} report --dir "${DIR}/../coverage" --include "${DIR}/../coverage/report*/coverage.json" lcov cobertura html

24
test/Service.thrift Normal file
View File

@ -0,0 +1,24 @@
/*
* 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.
*/
include "Types.thrift"
service Service {
Types.Type1 testEpisode(1:Types.Type1 arg)
}

23
test/Types.thrift Normal file
View File

@ -0,0 +1,23 @@
/*
* 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.
*/
struct Type1 {
1: i32 number,
2: string message,
}