[test] Add --check-baseline to generate-bytecode-expectations

Add a new mode to generate-bytecode-expectations to be used in a coming
test that tests that the bytecode expectations generated by
--rebaseline match the current state.

Change-Id: Ic03787cd853f9bf7d9b4412f96a767036c848c61
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1708477
Auto-Submit: Dan Elphick <delphick@chromium.org>
Commit-Queue: Dan Elphick <delphick@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62825}
This commit is contained in:
Dan Elphick 2019-07-19 10:38:43 +01:00 committed by Commit Bot
parent aa478cac4f
commit d251ec411c

View File

@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <cstring>
#include <fstream>
#include <memory>
#include <sstream>
#include <vector>
#include "test/cctest/interpreter/bytecode-expectations-printer.h"
@ -37,6 +39,7 @@ class ProgramOptions final {
read_raw_js_snippet_(false),
read_from_stdin_(false),
rebaseline_(false),
check_baseline_(false),
wrap_(true),
module_(false),
top_level_(false),
@ -58,6 +61,8 @@ class ProgramOptions final {
return output_filename_.empty() && !rebaseline_;
}
bool rebaseline() const { return rebaseline_; }
bool check_baseline() const { return check_baseline_; }
bool baseline() const { return rebaseline_ || check_baseline_; }
bool wrap() const { return wrap_; }
bool module() const { return module_; }
bool top_level() const { return top_level_; }
@ -66,7 +71,7 @@ class ProgramOptions final {
bool async_iteration() const { return async_iteration_; }
bool private_methods() const { return private_methods_; }
bool verbose() const { return verbose_; }
bool suppress_runtime_errors() const { return rebaseline_ && !verbose_; }
bool suppress_runtime_errors() const { return baseline() && !verbose_; }
std::vector<std::string> input_filenames() const { return input_filenames_; }
std::string output_filename() const { return output_filename_; }
std::string test_function_name() const { return test_function_name_; }
@ -77,6 +82,7 @@ class ProgramOptions final {
bool read_raw_js_snippet_;
bool read_from_stdin_;
bool rebaseline_;
bool check_baseline_;
bool wrap_;
bool module_;
bool top_level_;
@ -174,6 +180,8 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
options.read_from_stdin_ = true;
} else if (strcmp(argv[i], "--rebaseline") == 0) {
options.rebaseline_ = true;
} else if (strcmp(argv[i], "--check-baseline") == 0) {
options.check_baseline_ = true;
} else if (strcmp(argv[i], "--no-wrap") == 0) {
options.wrap_ = false;
} else if (strcmp(argv[i], "--module") == 0) {
@ -203,7 +211,13 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
}
}
if (options.rebaseline_ && options.input_filenames_.empty()) {
if (options.rebaseline() && options.check_baseline()) {
REPORT_ERROR("Can't check baseline and rebaseline at the same time.");
std::exit(1);
}
if ((options.check_baseline_ || options.rebaseline_) &&
options.input_filenames_.empty()) {
#if defined(V8_OS_POSIX) || defined(V8_OS_WIN)
if (options.verbose_) {
std::cout << "Looking for golden files in " << kGoldenFilesPath << '\n';
@ -236,24 +250,29 @@ bool ProgramOptions::Validate() const {
return false;
}
if (rebaseline_ && read_raw_js_snippet_) {
REPORT_ERROR("Cannot use --rebaseline on a raw JS snippet.");
return false;
}
if (rebaseline_ && !output_filename_.empty()) {
REPORT_ERROR("Output file cannot be specified together with --rebaseline.");
return false;
}
if (rebaseline_ && read_from_stdin_) {
REPORT_ERROR("Cannot --rebaseline when input is --stdin.");
return false;
}
if (input_filenames_.size() > 1 && !rebaseline_ && !read_raw_js_snippet()) {
if (baseline() && read_raw_js_snippet_) {
REPORT_ERROR(
"Multiple input files, but no --rebaseline or --raw-js specified.");
"Cannot use --rebaseline or --check-baseline on a raw JS snippet.");
return false;
}
if (baseline() && !output_filename_.empty()) {
REPORT_ERROR(
"Output file cannot be specified together with --rebaseline or "
"--check-baseline.");
return false;
}
if (baseline() && read_from_stdin_) {
REPORT_ERROR(
"Cannot --rebaseline or --check-baseline when input is --stdin.");
return false;
}
if (input_filenames_.size() > 1 && !baseline() && !read_raw_js_snippet()) {
REPORT_ERROR(
"Multiple input files, but no --rebaseline, --check-baseline or "
"--raw-js specified.");
return false;
}
@ -331,6 +350,7 @@ V8InitializationScope::V8InitializationScope(const char* exec_path)
: platform_(v8::platform::NewDefaultPlatform()) {
i::FLAG_always_opt = false;
i::FLAG_allow_natives_syntax = true;
i::FLAG_enable_lazy_source_positions = false;
v8::V8::InitializeICUDefaultLocation(exec_path);
v8::V8::InitializeExternalStartupData(exec_path);
@ -462,6 +482,16 @@ bool WriteExpectationsFile(const std::vector<std::string>& snippet_list,
return true;
}
std::string WriteExpectationsToString(
const std::vector<std::string>& snippet_list,
const V8InitializationScope& platform, const ProgramOptions& options) {
std::stringstream output_string;
GenerateExpectationsFile(output_string, snippet_list, platform, options);
return output_string.str();
}
void PrintMessage(v8::Local<v8::Message> message, v8::Local<v8::Value>) {
std::cerr << "INFO: "
<< *v8::String::Utf8Value(message->GetIsolate(), message->Get())
@ -475,11 +505,12 @@ void PrintUsage(const char* exec_path) {
<< "\nUsage: " << exec_path
<< " [OPTIONS]... [INPUT FILES]...\n\n"
"Options:\n"
" --help Print this help message.\n"
" --verbose Emit messages about the progress of the tool.\n"
" --raw-js Read raw JavaScript, instead of the output format.\n"
" --stdin Read from standard input instead of file.\n"
" --help Print this help message.\n"
" --verbose Emit messages about the progress of the tool.\n"
" --raw-js Read raw JavaScript, instead of the output format.\n"
" --stdin Read from standard input instead of file.\n"
" --rebaseline Rebaseline input snippet file.\n"
" --check-baseline Checks the current baseline is valid.\n"
" --no-wrap Do not wrap the snippet in a function.\n"
" --disable-oneshot-opt Disable Oneshot Optimization.\n"
" --print-callee Print bytecode of callee, function should "
@ -496,9 +527,9 @@ void PrintUsage(const char* exec_path) {
" Specify the type of the entries in the constant pool "
"(default: mixed).\n"
"\n"
"When using --rebaseline, flags --no-wrap, --test-function-name \n"
"and --pool-type will be overridden by the options specified in \n"
"the input file header.\n\n"
"When using --rebaseline or --check-baseline, flags --no-wrap,\n"
"--test-function-name and --pool-type will be overridden by the\n"
"options specified in the input file header.\n\n"
"Each raw JavaScript file is interpreted as a single snippet.\n\n"
"This tool is intended as a help in writing tests.\n"
"Please, DO NOT blindly copy and paste the output "
@ -507,6 +538,62 @@ void PrintUsage(const char* exec_path) {
} // namespace
bool CheckBaselineExpectations(const std::string& input_filename,
const std::vector<std::string>& snippet_list,
const V8InitializationScope& platform,
const ProgramOptions& options) {
std::string actual =
WriteExpectationsToString(snippet_list, platform, options);
std::ifstream input_stream(input_filename);
if (!input_stream.is_open()) {
REPORT_ERROR("Could not open " << input_filename << " for reading.");
return 2;
}
bool check_failed = false;
std::string expected((std::istreambuf_iterator<char>(input_stream)),
std::istreambuf_iterator<char>());
if (expected != actual) {
REPORT_ERROR("Mismatch: " << input_filename);
check_failed = true;
if (expected.size() != actual.size()) {
REPORT_ERROR(" Expected size (" << expected.size()
<< ") != actual size (" << actual.size()
<< ")");
}
int line = 1;
for (size_t i = 0; i < std::min(expected.size(), actual.size()); ++i) {
if (expected[i] != actual[i]) {
// Find the start of the line that has the mismatch carefully
// handling the case where it's the first line that mismatches.
size_t start = expected[i] != '\n' ? expected.rfind("\n", i)
: actual.rfind("\n", i);
if (start == std::string::npos) {
start = 0;
} else {
++start;
}
// If there is no new line, then these two lines will consume the
// remaining characters in the string, because npos - start will
// always be longer than the string itself.
std::string expected_line =
expected.substr(start, expected.find("\n", i) - start);
std::string actual_line =
actual.substr(start, actual.find("\n", i) - start);
REPORT_ERROR(" First mismatch on line " << line << ")");
REPORT_ERROR(" Expected : '" << expected_line << "'");
REPORT_ERROR(" Actual : '" << actual_line << "'");
break;
}
if (expected[i] == '\n') line++;
}
}
return check_failed;
}
int main(int argc, char** argv) {
ProgramOptions options = ProgramOptions::FromCommandLine(argc, argv);
@ -524,9 +611,10 @@ int main(int argc, char** argv) {
if (options.read_from_stdin()) {
// Rebaseline will never get here, so we will always take the
// GenerateExpectationsFile at the end of this function.
DCHECK(!options.rebaseline());
DCHECK(!options.rebaseline() && !options.check_baseline());
ExtractSnippets(&snippet_list, std::cin, options.read_raw_js_snippet());
} else {
bool check_failed = false;
for (const std::string& input_filename : options.input_filenames()) {
if (options.verbose()) {
std::cerr << "Processing " << input_filename << '\n';
@ -539,25 +627,35 @@ int main(int argc, char** argv) {
}
ProgramOptions updated_options = options;
if (options.rebaseline()) {
if (options.baseline()) {
updated_options.UpdateFromHeader(input_stream);
CHECK(updated_options.Validate());
}
ExtractSnippets(&snippet_list, input_stream,
options.read_raw_js_snippet());
input_stream.close();
if (options.rebaseline()) {
if (!WriteExpectationsFile(snippet_list, platform, updated_options,
input_filename)) {
return 3;
}
} else if (options.check_baseline()) {
check_failed |= CheckBaselineExpectations(input_filename, snippet_list,
platform, updated_options);
}
if (options.baseline()) {
snippet_list.clear();
}
}
if (check_failed) {
return 4;
}
}
if (!options.rebaseline()) {
if (!options.baseline()) {
if (!WriteExpectationsFile(snippet_list, platform, options,
options.output_filename())) {
return 3;