blob: 3d01c05fd6e1a4b5e19a75845648b2211415b69a [file]
// 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