| // Copyright 2015 The Shaderc 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. |
| |
| // Some of the tests here check code paths that are not checked by |
| // integration tests. |
| // Generally, these would be conditions not generated by the Glslang |
| // compiler. It's easier to write these unit tests than to inject |
| // a dependency on a fake compiler. |
| #include "libshaderc_util/message.h" |
| |
| #include <gmock/gmock.h> |
| |
| #include <ostream> |
| #include <vector> |
| |
| using shaderc_util::MessageType; |
| using shaderc_util::ParseGlslangOutput; |
| using shaderc_util::string_piece; |
| |
| namespace { |
| |
| TEST(ParseGlslangOutputTest, EmptyMessageBody) { |
| string_piece segment_number; |
| string_piece line_number; |
| string_piece rest; |
| EXPECT_EQ(MessageType::Unknown, |
| ParseGlslangOutput("WARNING: ", false, false, &segment_number, |
| &line_number, &rest)); |
| EXPECT_EQ(MessageType::Unknown, |
| ParseGlslangOutput("ERROR: ", false, false, &segment_number, |
| &line_number, &rest)); |
| } |
| |
| TEST(ParseGlslangOutputTest, GlobalError) { |
| string_piece segment_number; |
| string_piece line_number; |
| string_piece rest; |
| EXPECT_EQ( |
| MessageType::GlobalError, |
| ParseGlslangOutput("ERROR: too many functions: got 1666473 of them", |
| false, false, &segment_number, &line_number, &rest)); |
| EXPECT_EQ("too many functions: got 1666473 of them", rest.str()); |
| |
| EXPECT_EQ( |
| MessageType::GlobalError, |
| ParseGlslangOutput( |
| "ERROR: #version: versions before 150 do not allow a profile token", |
| false, false, &segment_number, &line_number, &rest)); |
| EXPECT_EQ("#version: versions before 150 do not allow a profile token", |
| rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, GlobalWarning) { |
| string_piece segment_number; |
| string_piece line_number; |
| string_piece rest; |
| EXPECT_EQ(MessageType::GlobalWarning, |
| ParseGlslangOutput("Warning, version 1000 is unknown.", false, |
| false, &segment_number, &line_number, &rest)); |
| EXPECT_EQ("version 1000 is unknown.", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, InvalidSuffixAfterSegmentNumber) { |
| string_piece segment_number; |
| string_piece line_number; |
| string_piece rest; |
| EXPECT_EQ(MessageType::GlobalWarning, |
| ParseGlslangOutput("WARNING: 12a", false, false, &segment_number, |
| &line_number, &rest)); |
| EXPECT_EQ(MessageType::GlobalError, |
| ParseGlslangOutput("WARNING: 12a", true, false, &segment_number, |
| &line_number, &rest)); |
| EXPECT_EQ(MessageType::GlobalError, |
| ParseGlslangOutput("ERROR: 42!", false, false, &segment_number, |
| &line_number, &rest)); |
| } |
| |
| TEST(ParseGlslangOutputTest, OnlyANumber) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| EXPECT_EQ(MessageType::GlobalWarning, |
| ParseGlslangOutput("WARNING: 12", false, false, &source_name, |
| &line_number, &rest)); |
| EXPECT_TRUE(source_name.empty()); |
| EXPECT_TRUE(line_number.empty()); |
| EXPECT_EQ("12", rest.str()); |
| |
| EXPECT_EQ(MessageType::GlobalError, |
| ParseGlslangOutput("WARNING: 12", true, false, &source_name, |
| &line_number, &rest)); |
| EXPECT_TRUE(source_name.empty()); |
| EXPECT_TRUE(line_number.empty()); |
| EXPECT_EQ("12", rest.str()); |
| |
| EXPECT_EQ(MessageType::GlobalError, |
| ParseGlslangOutput("ERROR: 42", false, false, &source_name, |
| &line_number, &rest)); |
| EXPECT_TRUE(source_name.empty()); |
| EXPECT_TRUE(line_number.empty()); |
| EXPECT_EQ("42", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, InvalidSuffixAfterSegmentNumberColon) { |
| string_piece segment_number; |
| string_piece line_number; |
| string_piece rest; |
| EXPECT_EQ(MessageType::GlobalWarning, |
| ParseGlslangOutput("WARNING: 12:0", false, false, &segment_number, |
| &line_number, &rest)); |
| EXPECT_EQ(MessageType::GlobalError, |
| ParseGlslangOutput("ERROR: 42:1234", false, false, &segment_number, |
| &line_number, &rest)); |
| } |
| |
| TEST(ParseGlslangOutputTest, CompletelyUnrecognized) { |
| string_piece segment_number; |
| string_piece line_number; |
| string_piece rest; |
| EXPECT_EQ(MessageType::Unknown, |
| ParseGlslangOutput("hello world!", false, false, &segment_number, |
| &line_number, &rest)); |
| } |
| |
| TEST(ParseGlslangOutputTest, LocationSpecification) { |
| string_piece segment_number; |
| string_piece line_number; |
| string_piece rest; |
| |
| // Glslang reading from strings can give string segment numbers as |
| // the filename part. |
| EXPECT_EQ( |
| MessageType::Error, |
| ParseGlslangOutput("ERROR: 0:2: '#' : invalid directive: foo", false, |
| false, &segment_number, &line_number, &rest)); |
| EXPECT_EQ("0", segment_number.str()); |
| EXPECT_EQ("2", line_number.str()); |
| EXPECT_EQ("'#' : invalid directive: foo", rest.str()); |
| |
| EXPECT_EQ( |
| MessageType::Warning, |
| ParseGlslangOutput("WARNING: 15:36: The following extension must be " |
| "enabled to use this feature:", |
| false, false, &segment_number, &line_number, &rest)); |
| EXPECT_EQ("15", segment_number.str()); |
| EXPECT_EQ("36", line_number.str()); |
| EXPECT_EQ("The following extension must be enabled to use this feature:", |
| rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, FileName_BaseAndExtension) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| EXPECT_EQ(MessageType::Error, |
| ParseGlslangOutput("ERROR: shader.vert:5: something wrong", false, |
| false, &source_name, &line_number, &rest)); |
| EXPECT_EQ("shader.vert", source_name.str()); |
| EXPECT_EQ("5", line_number.str()); |
| EXPECT_EQ("something wrong", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, FileName_BaseOnly) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| EXPECT_EQ(MessageType::Warning, |
| ParseGlslangOutput("WARNING: file:42: something wrong", false, |
| false, &source_name, &line_number, &rest)); |
| EXPECT_EQ("file", source_name.str()); |
| EXPECT_EQ("42", line_number.str()); |
| EXPECT_EQ("something wrong", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, FileName_HexNumber) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| EXPECT_EQ(MessageType::Warning, |
| ParseGlslangOutput("WARNING: 0xdeedbeef:0: wa:ha:ha", false, false, |
| &source_name, &line_number, &rest)); |
| EXPECT_EQ("0xdeedbeef", source_name.str()); |
| EXPECT_EQ("0", line_number.str()); |
| EXPECT_EQ("wa:ha:ha", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, FileName_ContainsColons) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| EXPECT_EQ(MessageType::Warning, |
| ParseGlslangOutput("WARNING: foo:bar:0: wa:ha:ha", false, false, |
| &source_name, &line_number, &rest)); |
| EXPECT_EQ("foo:bar", source_name.str()); |
| EXPECT_EQ("0", line_number.str()); |
| EXPECT_EQ("wa:ha:ha", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, NoFile) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| EXPECT_EQ(MessageType::Warning, |
| ParseGlslangOutput("WARNING: :12: abc", false, false, &source_name, |
| &line_number, &rest)); |
| EXPECT_EQ("", source_name.str()); |
| EXPECT_EQ("12", line_number.str()); |
| EXPECT_EQ("abc", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, NoLineNumber_InferredAsGlobalNoLocation) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| // No solution since there is no room for digits. |
| EXPECT_EQ(MessageType::GlobalWarning, |
| ParseGlslangOutput("WARNING: foo:: abc", false, false, |
| &source_name, &line_number, &rest)); |
| EXPECT_EQ("", source_name.str()); |
| EXPECT_EQ("", line_number.str()); |
| EXPECT_EQ("foo:: abc", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, NoSpaceAfterColon_InferredAsGlobalNoLocation) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| // No solution since there is no space after the line-number-and-colon. |
| EXPECT_EQ(MessageType::GlobalWarning, |
| ParseGlslangOutput("WARNING: foo:12:abc", false, false, |
| &source_name, &line_number, &rest)); |
| EXPECT_EQ("", source_name.str()); |
| EXPECT_EQ("", line_number.str()); |
| EXPECT_EQ("foo:12:abc", rest.str()); |
| } |
| |
| TEST(ParseGlslangOutputTest, WindowsPath) { |
| string_piece source_name; |
| string_piece line_number; |
| string_piece rest; |
| |
| EXPECT_EQ( |
| MessageType::Error, |
| ParseGlslangOutput(R"(ERROR: C:\path\to\shader.glsl:5: something wrong)", |
| false, false, &source_name, &line_number, &rest)); |
| EXPECT_EQ(R"(C:\path\to\shader.glsl)", source_name.str()); |
| EXPECT_EQ("5", line_number.str()); |
| EXPECT_EQ("something wrong", rest.str()); |
| |
| EXPECT_EQ( |
| MessageType::Warning, |
| ParseGlslangOutput(R"(WARNING: \\path\without\drive.vert:42: BOOM!)", |
| false, false, &source_name, &line_number, &rest)); |
| EXPECT_EQ(R"(\\path\without\drive.vert)", source_name.str()); |
| EXPECT_EQ("42", line_number.str()); |
| EXPECT_EQ("BOOM!", rest.str()); |
| |
| EXPECT_EQ(MessageType::Warning, |
| ParseGlslangOutput(R"(WARNING: X:\123.456\789:0: wa:ha:ha)", false, |
| false, &source_name, &line_number, &rest)); |
| EXPECT_EQ(R"(X:\123.456\789)", source_name.str()); |
| EXPECT_EQ("0", line_number.str()); |
| EXPECT_EQ("wa:ha:ha", rest.str()); |
| } |
| |
| struct PrintFilteredErrorsCase { |
| std::string errors; |
| std::string filename; |
| std::string expected; |
| size_t expected_err_count = 0; |
| size_t expected_warning_count = 0; |
| }; |
| |
| using PrintFilteredErrorsTest = |
| ::testing::TestWithParam<PrintFilteredErrorsCase>; |
| |
| TEST_P(PrintFilteredErrorsTest, FallbackName) { |
| const auto param = GetParam(); |
| size_t err_count = 0; |
| size_t warning_count = 0; |
| std::ostringstream os; |
| constexpr bool kWarningsAsErrors = false; |
| constexpr bool kSuppressWarnings = false; |
| bool result = shaderc_util::PrintFilteredErrors( |
| param.filename, &os, kWarningsAsErrors, kSuppressWarnings, |
| param.errors.c_str(), &warning_count, &err_count); |
| |
| EXPECT_EQ(result, 0 == param.expected_err_count) |
| << "error count: " << err_count |
| << " vs. expected_err_count: " << param.expected_err_count; |
| |
| EXPECT_EQ(warning_count, param.expected_warning_count); |
| const std::string got = os.str(); |
| EXPECT_THAT(got, ::testing::StrEq(param.expected)) |
| << "got: '" << got << "' expected: '" << param.expected << "'"; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| Samples, PrintFilteredErrorsTest, |
| ::testing::ValuesIn(std::vector<PrintFilteredErrorsCase>{ |
| {"", "abc", ""}, |
| // With Glslang's expected file and line number format |
| {"ERROR: abc:123: bah", "abc", "abc:123: error: bah\n", 1}, |
| {"ERROR: abc:123: bah", "def", "abc:123: error: bah\n", 1}, |
| {"ERROR: -1:123: bah", "def", "<command line>:123: error: bah\n", 1}, |
| // Use the fallback name |
| {"no problem", "abc", "abc: no problem\n"}, |
| // Fallback name sanitization: cut at newline |
| {"cut at newline", "abc\ndef", "abc: cut at newline\n"}, |
| {"ERROR: cut at newline", "abc\ndef", "abc: error: cut at newline\n", |
| 1}, |
| // Fallback name sanitization: cut at 1k length |
| {"cut at 1k", std::string(5555, 'x'), |
| std::string(1024, 'x') + ": cut at 1k\n"}, |
| {"ERROR: cut at 1k", std::string(5555, 'x'), |
| std::string(1024, 'x') + ": error: cut at 1k\n", 1}, |
| })); |
| |
| } // anonymous namespace |