blob: 041f11045d01fc5f6aa340d6bcf469098d0f39f5 [file] [log] [blame] [edit]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/shell/josh/console/console.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/memfs/memfs.h>
#include <stdlib.h>
#include <array>
#include <fstream>
#include <string>
#include <gtest/gtest.h>
namespace shell {
class ConsoleTest : public ::testing::Test {
public:
// Generate a random file. The last six characters of the name template must be XXXXXX
std::string GetRandomFile(const char *name_template) {
std::unique_ptr<char[]> buffer(new char[strlen(name_template) + 1]);
strcpy(buffer.get(), name_template);
mkstemp(buffer.get());
return buffer.get();
}
protected:
void SetUp() override {
loop_ = new async::Loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_EQ(loop_->StartThread(), ZX_OK);
ASSERT_EQ(ZX_OK, memfs_install_at(loop_->dispatcher(), "/test_tmp", &fs_));
// Make sure file creation is OK so memfs is running OK.
char tmpfs_test_file[] = "/test_tmp/write.test.XXXXXX";
ASSERT_NE(mkstemp(tmpfs_test_file), -1);
}
void TearDown() override {
// Synchronously clean up.
sync_completion_t unmounted;
memfs_free_filesystem(fs_, &unmounted);
sync_completion_wait(&unmounted, zx::duration::infinite().get());
fs_ = nullptr;
loop_->Shutdown();
delete loop_;
loop_ = nullptr;
}
void AssertJsArgsEq(int argc, const char **argv, std::string_view expect_list) {
// Test script
std::string random_script = GetRandomFile("/test_tmp/script.js.XXXXXX");
std::ofstream test_script;
test_script.open(random_script);
test_script << "var expectArgs = [\n"
<< expect_list << "\n];\n"
<< R"(
if (scriptArgs.length != expectArgs.length)
throw `*** [Error] Failed parsing all script args, expecting ${expectArgs.length}, got ${scriptArgs.length}`;
for (i = 0; i < expectArgs.length; i++) {
if (scriptArgs[i] != expectArgs[i])
throw `*** [Error] Failed to parse all script arg[0], expecting "${expectArgs[i]}", got "${scriptArgs[i]}"`;
}
)";
test_script.close();
int real_argc = argc + 3;
const char *real_argv[real_argc];
// Will run above test script
real_argv[0] = "test_program";
real_argv[1] = "-r";
real_argv[2] = random_script.c_str();
for (int i = 0; i < argc; i++) {
real_argv[i + 3] = argv[i];
}
ASSERT_EQ(0, shell::ConsoleMain(real_argc, real_argv));
}
async::Loop *loop_;
memfs_filesystem_t *fs_;
};
// Sanity check test to make sure Hello World works.
TEST_F(ConsoleTest, Sanity) {
std::string filename = GetRandomFile("/test_tmp/tmp.XXXXXX");
// Generate the js command to run.
std::string command;
std::string expected = "Hello World";
// Write out the test file
command += "file = std.open('" + filename + "', 'rw+');\n";
command += "file.puts('" + expected + "');\n";
command += "file.flush();\n";
std::array argv{
"test_program",
"-j",
"/pkg/data/lib/",
"-f",
"/pkg/data/fidling",
"-c",
command.c_str(),
"--",
// The following are addition parameters to JS env
"PARAM1=AAA",
"PARAM2=BBB",
"PARAM3=CCC",
};
ASSERT_EQ(0, shell::ConsoleMain(argv.size(), argv.data()));
std::ifstream in(filename.c_str());
std::string actual((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
// Sanity check test to make sure Hello World script works.
TEST_F(ConsoleTest, ScriptSanity) {
std::string random_filename = GetRandomFile("/test_tmp/tmp.XXXXXX");
std::string random_script_name = GetRandomFile("/test_tmp/script.js.XXXXXX");
// Write js into the script file
std::string expected = "Hello World";
std::ofstream test_script;
test_script.open(random_script_name);
// Write out the test file
test_script << "file = std.open('" + random_filename + "', 'rw+');\n";
test_script << "file.puts('" + expected + "');\n";
test_script << "file.flush();\n";
test_script.close();
std::array argv{
"test_program",
"-j",
"/pkg/data/lib/",
"-f",
"/pkg/data/fidling",
"-r",
random_script_name.c_str(),
"--",
// The following are addition parameters to JS script
"PARAM1=AAA",
"PARAM2=BBB",
"PARAM3=CCC",
};
ASSERT_EQ(0, shell::ConsoleMain(argv.size(), argv.data()));
std::ifstream in(random_filename.c_str());
std::string actual((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
TEST_F(ConsoleTest, JsScriptArgsParsing) {
// No double dash
// josh --foo A B C => []
std::array argv_no_dash{
// The following are addition parameters to JS script
"AAA",
"BBB",
"CCC",
};
AssertJsArgsEq(argv_no_dash.size(), argv_no_dash.data(), R"()");
// All remaining params are js args
// josh --foo -- A B C => [A, B, C]
std::array argv_standard_dash{
"--",
// The following are addition parameters to JS script
"AAA",
"BBB",
"CCC",
};
AssertJsArgsEq(argv_standard_dash.size(), argv_standard_dash.data(), R"(
"AAA",
"BBB",
"CCC",
)");
// Some remaining params are js args
// josh --foo A -- B C => [B, C]
std::array argv1{
"AAA",
"--",
// The following are addition parameters to JS script
"BBB",
"CCC",
};
AssertJsArgsEq(argv1.size(), argv1.data(), R"(
"BBB",
"CCC",
)");
// Some remaining params are js args
// josh --foo -- A -- B C => [A, --, B, C]
std::array argv2{
"--",
"AAA",
"--",
// The following are addition parameters to JS script
"BBB",
"CCC",
};
AssertJsArgsEq(argv2.size(), argv2.data(), R"(
"AAA",
"--",
"BBB",
"CCC",
)");
// No remaining params is js arg
// josh --foo A B C -- => []
std::array argv3{
"AAA", "BBB", "CCC", "--",
// The following (none) are addition parameters to JS script
};
AssertJsArgsEq(argv3.size(), argv3.data(), R"()");
// No remaining params is js arg
// josh --foo -- A B C -- => [A, B, C, --]
std::array argv4{
"--", "AAA", "BBB", "CCC", "--",
// The following (none) are addition parameters to JS script
};
AssertJsArgsEq(argv4.size(), argv4.data(), R"(
"AAA",
"BBB",
"CCC",
"--",
)");
}
TEST_F(ConsoleTest, CatchError) {
// Test script - unhandled error
std::string random_script_unhandled = GetRandomFile("/test_tmp/script.js.XXXXXX");
std::ofstream test_script_unhandled;
test_script_unhandled.open(random_script_unhandled);
test_script_unhandled << R"(
function throw_error() {
throw "This is an unhandled error!";
}
throw_error();
)";
test_script_unhandled.close();
std::array argv_unhandled{
"test_program",
"-j",
"/pkg/data/lib/",
"-f",
"/pkg/data/fidling",
"-r",
random_script_unhandled.c_str(),
"--",
};
ASSERT_EQ(1, shell::ConsoleMain(argv_unhandled.size(), argv_unhandled.data()));
// Test script - handled error
std::string random_script_handled = GetRandomFile("/test_tmp/script.js.XXXXXX");
std::ofstream test_script_handled;
test_script_handled.open(random_script_handled);
test_script_handled << R"(
function throw_error() {
throw "This is a handled error!";
}
try {
throw_error();
} catch (e) {
// Do nothing
}
)";
test_script_handled.close();
std::array argv_handled{
"test_program",
"-j",
"/pkg/data/lib/",
"-f",
"/pkg/data/fidling",
"-r",
random_script_handled.c_str(),
"--",
};
ASSERT_EQ(0, shell::ConsoleMain(argv_handled.size(), argv_handled.data()));
}
// Sanity check test to make sure async errors are caught.
TEST_F(ConsoleTest, CatchErrorAsync) {
// Test script - unhandled error
std::string random_script_unhandled = GetRandomFile("/test_tmp/script.js.XXXXXX");
std::ofstream test_script_unhandled;
test_script_unhandled.open(random_script_unhandled);
test_script_unhandled << R"(
async function throw_async_error() {
throw "This is an unhandled async error!";
}
throw_async_error();
)";
test_script_unhandled.close();
std::array argv_unhandled{
"test_program",
"-j",
"/pkg/data/lib/",
"-f",
"/pkg/data/fidling",
"-r",
random_script_unhandled.c_str(),
"--",
};
ASSERT_EQ(1, shell::ConsoleMain(argv_unhandled.size(), argv_unhandled.data()));
// Test script - handled error
std::string random_script_handled = GetRandomFile("/test_tmp/script.js.XXXXXX");
std::ofstream test_script_handled;
test_script_handled.open(random_script_handled);
test_script_handled << R"(
async function throw_async_error() {
try {
throw "This is a handled async error!";
} catch (e) {
// Do nothing
}
}
throw_async_error();
)";
test_script_handled.close();
std::array argv_handled{
"test_program",
"-j",
"/pkg/data/lib/",
"-f",
"/pkg/data/fidling",
"-r",
random_script_handled.c_str(),
"--",
};
ASSERT_EQ(0, shell::ConsoleMain(argv_handled.size(), argv_handled.data()));
}
} // namespace shell