| // 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 |