| // Copyright 2015 The Cobalt Authors. 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 "cobalt/layout_tests/web_platform_test_parser.h" | |
| #include <map> | |
| #include <memory> | |
| #include <utility> | |
| #include "base/files/file_path.h" | |
| #include "base/files/file_util.h" | |
| #include "base/optional.h" | |
| #include "base/strings/string_number_conversions.h" | |
| #include "base/strings/string_split.h" | |
| #include "base/strings/string_util.h" | |
| #include "base/test/scoped_task_environment.h" | |
| #include "cobalt/base/cobalt_paths.h" | |
| #include "cobalt/layout_tests/test_utils.h" | |
| #include "cobalt/script/global_environment.h" | |
| #include "cobalt/script/javascript_engine.h" | |
| #include "cobalt/script/source_code.h" | |
| namespace cobalt { | |
| namespace layout_tests { | |
| namespace { | |
| std::string ExpectationToString(WebPlatformTestInfo::State state) { | |
| switch (state) { | |
| case WebPlatformTestInfo::kPass: | |
| return "PASS"; | |
| case WebPlatformTestInfo::kFail: | |
| return "FAIL"; | |
| case WebPlatformTestInfo::kDisable: | |
| return "DISABLE"; | |
| } | |
| NOTREACHED(); | |
| return "FAIL"; | |
| } | |
| WebPlatformTestInfo::State StringToExpectation( | |
| const std::string& lower_case_string) { | |
| if (base::LowerCaseEqualsASCII(lower_case_string, "pass")) { | |
| return WebPlatformTestInfo::kPass; | |
| } else if (base::LowerCaseEqualsASCII(lower_case_string, "fail")) { | |
| return WebPlatformTestInfo::kFail; | |
| } else if (base::LowerCaseEqualsASCII(lower_case_string, "disable")) { | |
| return WebPlatformTestInfo::kDisable; | |
| } else { | |
| NOTREACHED() << "Invalid test expectation " << lower_case_string; | |
| return WebPlatformTestInfo::kFail; | |
| } | |
| } | |
| base::Optional<WebPlatformTestInfo> ParseWebPlatformTestCaseLine( | |
| const std::string& line_string) { | |
| std::vector<std::string> test_case_tokens = base::SplitString( | |
| line_string, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | |
| if (test_case_tokens.size() < 2) { | |
| DLOG(WARNING) << "Failed to parse: " << line_string; | |
| return base::nullopt; | |
| } | |
| for (size_t i = 0; i < test_case_tokens.size(); ++i) { | |
| TrimWhitespaceASCII(test_case_tokens[i], base::TRIM_ALL, | |
| &test_case_tokens[i]); | |
| } | |
| std::string test_expect = base::ToLowerASCII(test_case_tokens[1]); | |
| WebPlatformTestInfo::State expectation = StringToExpectation(test_expect); | |
| if (expectation == WebPlatformTestInfo::kDisable) { | |
| return base::nullopt; | |
| } else { | |
| WebPlatformTestInfo test_info; | |
| test_info.url = test_case_tokens[0]; | |
| test_info.expectation = expectation; | |
| for (size_t i = 2; i < test_case_tokens.size(); ++i) { | |
| test_info.exceptions.insert(test_case_tokens[i]); | |
| } | |
| return test_info; | |
| } | |
| } | |
| } // namespace | |
| std::ostream& operator<<(std::ostream& out, | |
| const WebPlatformTestInfo& test_info) { | |
| return out << test_info.url | |
| << " Expected: " << ExpectationToString(test_info.expectation); | |
| } | |
| std::vector<WebPlatformTestInfo> EnumerateWebPlatformTests( | |
| const std::string& top_level, const char* precondition) { | |
| if (precondition) { | |
| // Evaluate the javascript precondition. Enumerate the web platform tests | |
| // only if the precondition is true. | |
| base::test::ScopedTaskEnvironment task_env_; | |
| std::unique_ptr<script::JavaScriptEngine> engine = | |
| script::JavaScriptEngine::CreateEngine(); | |
| scoped_refptr<script::GlobalEnvironment> global_environment = | |
| engine->CreateGlobalEnvironment(); | |
| global_environment->CreateGlobalObject(); | |
| std::string result; | |
| bool success = global_environment->EvaluateScript( | |
| script::SourceCode::CreateSourceCode( | |
| precondition, base::SourceLocation(__FILE__, __LINE__, 1)), | |
| &result); | |
| if (!success) { | |
| DLOG(ERROR) << "Failed to evaluate precondition: " | |
| << "\"" << precondition << "\""; | |
| // Continue to enumerate tests like normal. | |
| } else if (result != "true") { | |
| DLOG(WARNING) << "Skipping Web Platform Tests for " | |
| << "\"" << top_level << "\""; | |
| return std::vector<WebPlatformTestInfo>(); | |
| } | |
| } | |
| base::FilePath test_dir(GetTestInputRootDirectory() | |
| .Append("web-platform-tests") | |
| .Append(top_level)); | |
| base::FilePath tests_list_file( | |
| test_dir.Append(FILE_PATH_LITERAL("web_platform_tests.txt"))); | |
| std::string test_list; | |
| if (!base::ReadFileToString(tests_list_file, &test_list)) { | |
| DLOG(ERROR) << "Could not open '" << tests_list_file.value() << "'."; | |
| return std::vector<WebPlatformTestInfo>(); | |
| } else { | |
| // base::SplitString the file contents into lines, and then read each line | |
| // one by one as the name of the test file. | |
| std::vector<std::string> line_tokens = base::SplitString( | |
| test_list, "\n\r", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | |
| const char kCommentChar = '#'; | |
| typedef std::map<std::string, WebPlatformTestInfo> TestInfoMap; | |
| TestInfoMap all_test_infos; | |
| for (std::vector<std::string>::iterator iter = line_tokens.begin(); | |
| iter != line_tokens.end(); ++iter) { | |
| std::string trimmed_line; | |
| TrimWhitespaceASCII(*iter, base::TRIM_ALL, &trimmed_line); | |
| // Skip commented-out lines. | |
| if (trimmed_line.size() > 0 && trimmed_line[0] == kCommentChar) { | |
| continue; | |
| } | |
| base::Optional<WebPlatformTestInfo> parsed_test_info = | |
| ParseWebPlatformTestCaseLine(trimmed_line); | |
| if (parsed_test_info) { | |
| WebPlatformTestInfo& test_info = *parsed_test_info; | |
| test_info.url = top_level + "/" + test_info.url; | |
| std::pair<TestInfoMap::iterator, bool> ret = | |
| all_test_infos.insert(std::make_pair(test_info.url, test_info)); | |
| // If it's the same url, merge the exceptions into the original one. | |
| if (ret.second == false) { | |
| // Ensure that neither expectation is set to disable, and that the | |
| // expectations are different. | |
| DCHECK_NE(ret.first->second.expectation, | |
| WebPlatformTestInfo::kDisable); | |
| DCHECK_NE(test_info.expectation, WebPlatformTestInfo::kDisable); | |
| DCHECK_NE(ret.first->second.expectation, test_info.expectation); | |
| // There should be at least one test in the list of exceptions for | |
| // the new one. Append these to the existing exceptions list. | |
| DCHECK_GT(test_info.exceptions.size(), size_t(0)); | |
| ret.first->second.exceptions.insert(test_info.exceptions.begin(), | |
| test_info.exceptions.end()); | |
| } | |
| } | |
| } | |
| std::vector<WebPlatformTestInfo> test_info_list; | |
| for (TestInfoMap::iterator it = all_test_infos.begin(); | |
| it != all_test_infos.end(); ++it) { | |
| test_info_list.push_back(it->second); | |
| } | |
| return test_info_list; | |
| } | |
| } | |
| } // namespace layout_tests | |
| } // namespace cobalt |