// Copyright 2019 The Amber Authors. // // 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 parseried. // See the License for the specific language governing permissions and // limitations under the License. #include "gtest/gtest.h" #include "src/amberscript/parser.h" namespace amber { namespace amberscript { using AmberScriptParserTest = testing::Test; TEST_F(AmberScriptParserTest, PipelineWithUnknownShader) { std::string in = R"( PIPELINE graphics my_pipeline ATTACH my_shader END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("3: unknown shader in ATTACH command", r.Error()); } TEST_F(AmberScriptParserTest, DuplicateShadersInAPipeline) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_shader END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: can not add duplicate shader to pipeline", r.Error()); } TEST_F(AmberScriptParserTest, AttachInvalidToken) { std::string in = R"(PIPELINE graphics my_pipeline ATTACH 1234 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("2: invalid token in ATTACH command", r.Error()); } TEST_F(AmberScriptParserTest, AttachExtraParameter) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH PIPELINE graphics my_pipeline ATTACH my_shader INVALID END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("4: unknown ATTACH parameter: INVALID", r.Error()); } TEST_F(AmberScriptParserTest, AttachMissingValue) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH PIPELINE graphics my_pipeline ATTACH END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("5: invalid token in ATTACH command", r.Error()); } TEST_F(AmberScriptParserTest, ComputeShaderInGraphicsPipeline) { std::string in = R"(SHADER compute my_shader GLSL void main() { gl_FragColor = vec3(2, 3, 4); } END PIPELINE graphics my_pipeline ATTACH my_shader END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()) << r.Error(); EXPECT_EQ("9: can not add a compute shader to a graphics pipeline", r.Error()); } struct ShaderTypeData { const char* name; ShaderType type; }; using AmberScriptParserPipelineAttachTest = testing::TestWithParam; TEST_P(AmberScriptParserPipelineAttachTest, GraphicsShaderInComputePipeline) { auto test_data = GetParam(); std::string in = "SHADER " + std::string(test_data.name) + R"( my_shader GLSL void main() { gl_FragColor = vec3(2, 3, 4); } END PIPELINE compute my_pipeline ATTACH my_shader END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()) << r.Error(); EXPECT_EQ("9: only compute shaders allowed in a compute pipeline", r.Error()); } INSTANTIATE_TEST_SUITE_P( AmberScriptParserPipelineAttachTests, AmberScriptParserPipelineAttachTest, testing::Values( ShaderTypeData{"vertex", kShaderTypeVertex}, ShaderTypeData{"fragment", kShaderTypeFragment}, ShaderTypeData{"geometry", kShaderTypeGeometry}, ShaderTypeData{"tessellation_evaluation", kShaderTypeTessellationEvaluation}, ShaderTypeData{ "tessellation_control", kShaderTypeTessellationControl})); // NOLINT(whitespace/parens) TEST_F(AmberScriptParserTest, PipelineEntryPoint) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics my_pipeline ATTACH my_shader ENTRY_POINT green ATTACH my_fragment END )"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()) << r.Error(); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(2U, shaders.size()); ASSERT_TRUE(shaders[0].GetShader() != nullptr); EXPECT_EQ(kShaderTypeVertex, shaders[0].GetShader()->GetType()); EXPECT_EQ("green", shaders[0].GetEntryPoint()); ASSERT_TRUE(shaders[1].GetShader() != nullptr); EXPECT_EQ(kShaderTypeFragment, shaders[1].GetShader()->GetType()); EXPECT_EQ("main", shaders[1].GetEntryPoint()); } TEST_F(AmberScriptParserTest, PipelineEntryPointWithInvalidValue) { std::string in = R"( SHADER compute my_compute GLSL # Compute Shader END PIPELINE compute my_pipeline ATTACH my_compute ENTRY_POINT 1234 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: missing shader name in ATTACH ENTRY_POINT command", r.Error()); } TEST_F(AmberScriptParserTest, PipelineEntryPointMissingValue) { std::string in = R"( SHADER compute my_compute GLSL # Compute Shader END PIPELINE compute my_pipeline ATTACH my_compute ENTRY_POINT END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("7: missing shader name in ATTACH ENTRY_POINT command", r.Error()); } TEST_F(AmberScriptParserTest, PipelineEntryPointExtraParameter) { std::string in = R"( SHADER compute my_compute GLSL # Compute Shader END PIPELINE compute my_pipeline ATTACH my_compute ENTRY_POINT green INVALID END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: unknown ATTACH parameter: INVALID", r.Error()); } TEST_F(AmberScriptParserTest, PiplineMultiShaderAttach) { std::string in = R"( SHADER multi my_shader GLSL # shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_entry_point END)"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()) << r.Error(); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(1U, shaders.size()); ASSERT_TRUE(shaders[0].GetShader() != nullptr); EXPECT_EQ(kShaderTypeMulti, shaders[0].GetShader()->GetType()); EXPECT_EQ(kShaderTypeCompute, shaders[0].GetShaderType()); EXPECT_EQ("my_entry_point", shaders[0].GetEntryPoint()); } TEST_F(AmberScriptParserTest, PipelineMultiShaderMismatchPipelineAndShaderType) { std::string in = R"( SHADER multi my_shader GLSL # shaders END PIPELINE graphics my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_entry_point END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: can not add a compute shader to a graphics pipeline", r.Error()); } TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingEntryPoint) { std::string in = R"( SHADER multi my_shader GLSL # shaders END PIPELINE graphics my_pipeline ATTACH my_shader TYPE fragment END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("7: ATTACH TYPE requires an ENTRY_POINT", r.Error()); } TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingType) { std::string in = R"( SHADER multi my_shader GLSL # shaders END PIPELINE graphics my_pipeline ATTACH my_shader END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("7: multi shader ATTACH requires TYPE", r.Error()); } TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingTypeWithEntryPoint) { std::string in = R"( SHADER multi my_shader GLSL # shaders END PIPELINE graphics my_pipeline ATTACH my_shader ENTRY_POINT my_ep END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: ATTACH missing TYPE for multi shader", r.Error()); } TEST_F(AmberScriptParserTest, PipelineSpecializationUint32) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 AS uint32 4 END)"; Parser parser; Result r = parser.Parse(in); EXPECT_EQ(r.Error(), ""); ASSERT_TRUE(r.IsSuccess()); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(1U, shaders.size()); EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1)); } TEST_F(AmberScriptParserTest, PipelineSpecializationInt32) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 2 AS int32 -1 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(1U, shaders.size()); EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); EXPECT_EQ(0xffffffffu, shaders[0].GetSpecialization().at(2)); } TEST_F(AmberScriptParserTest, PipelineSpecializationFloat) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 3 AS float 1.1 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(1U, shaders.size()); EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); EXPECT_EQ(0x3f8ccccdu, shaders[0].GetSpecialization().at(3)); } TEST_F(AmberScriptParserTest, PipelineSpecializationIDIsString) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE s3 AS float 1.1 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: specialization ID must be an integer", r.Error()); } TEST_F(AmberScriptParserTest, PipelineSpecializationNoAS) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 ASa float 1.1 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: expected AS as next token", r.Error()); } TEST_F(AmberScriptParserTest, PipelineSpecializationNotDataType) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 AS uint 1.1 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("6: invalid data type 'uint' provided", r.Error()); } TEST_F(AmberScriptParserTest, PipelineSpecializationBadDataType) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader ENTRY_POINT my_ep SPECIALIZE 1 AS uint8 1.1 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ( "6: only 32-bit types are currently accepted for specialization values", r.Error()); } TEST_F(AmberScriptParserTest, PipelineSpecializationMultipleSpecializations) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader TYPE compute ENTRY_POINT my_ep \ SPECIALIZE 1 AS uint32 4 \ SPECIALIZE 2 AS uint32 5 \ SPECIALIZE 5 AS uint32 1 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(1U, shaders.size()); EXPECT_EQ(3u, shaders[0].GetSpecialization().size()); EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1)); EXPECT_EQ(5u, shaders[0].GetSpecialization().at(2)); EXPECT_EQ(1u, shaders[0].GetSpecialization().at(5)); } TEST_F(AmberScriptParserTest, PipelineSpecializationNoType) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute my_pipeline ATTACH my_shader SPECIALIZE 1 AS uint32 4 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(1U, shaders.size()); EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1)); } } // namespace amberscript } // namespace amber