Skip to content

Commit

Permalink
Add custom argument generators for Presto decimal functions (#9715)
Browse files Browse the repository at this point in the history
Summary:
To generate argument types in the decimal fuzzer test correctly, customized
argument type generators are required. This PR adds arg generators for the
Presto functions plus/minus, multiply, divide, and floor/round.
Inspired by #9358.

Pull Request resolved: #9715

Reviewed By: Yuhta

Differential Revision: D57173196

Pulled By: kgpai

fbshipit-source-id: 0bb23aaa55be046b27e4edef2aef9ed4cdf0545e
  • Loading branch information
rui-mo authored and facebook-github-bot committed May 15, 2024
1 parent b470e85 commit 08ffe22
Show file tree
Hide file tree
Showing 12 changed files with 449 additions and 34 deletions.
50 changes: 50 additions & 0 deletions velox/expression/fuzzer/tests/ArgGeneratorTestUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 "velox/expression/fuzzer/tests/ArgGeneratorTestUtils.h"
#include <gtest/gtest.h>
#include "velox/expression/SignatureBinder.h"

namespace facebook::velox::fuzzer::test {

void assertReturnType(
const std::shared_ptr<ArgGenerator>& generator,
const exec::FunctionSignature& signature,
const TypePtr& returnType) {
std::mt19937 seed{0};
const auto argTypes = generator->generateArgs(signature, returnType, seed);

// Resolve return type from argument types for the given signature.
exec::SignatureBinder binder(signature, argTypes);
VELOX_CHECK(
binder.tryBind(),
"Failed to resolve {} from argument types.",
returnType->toString());
const auto actualType = binder.tryResolveReturnType();
EXPECT_TRUE(returnType->equivalent(*actualType))
<< "Expected type: " << returnType->toString()
<< ", actual type: " << actualType->toString();
}

void assertEmptyArgs(
std::shared_ptr<ArgGenerator> generator,
const exec::FunctionSignature& signature,
const TypePtr& returnType) {
std::mt19937 seed{0};
const auto argTypes = generator->generateArgs(signature, returnType, seed);
EXPECT_TRUE(argTypes.empty());
}

} // namespace facebook::velox::fuzzer::test
36 changes: 36 additions & 0 deletions velox/expression/fuzzer/tests/ArgGeneratorTestUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once

#include "velox/expression/fuzzer/ArgGenerator.h"
#include "velox/functions/FunctionRegistry.h"

namespace facebook::velox::fuzzer::test {

/// Assert the equivalence between the given return type and the actual type
/// resolved from generated argument types.
void assertReturnType(
const std::shared_ptr<ArgGenerator>& generator,
const exec::FunctionSignature& signature,
const TypePtr& returnType);

// Assert that no argument types can be generated for the given return type.
void assertEmptyArgs(
std::shared_ptr<ArgGenerator> generator,
const exec::FunctionSignature& signature,
const TypePtr& returnType);

} // namespace facebook::velox::fuzzer::test
10 changes: 9 additions & 1 deletion velox/expression/fuzzer/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.

add_executable(velox_expression_fuzzer_unit_test ArgumentTypeFuzzerTest.cpp DecimalArgGeneratorTest.cpp ExpressionFuzzerUnitTest.cpp)
add_library(velox_expression_fuzzer_test_utility ArgGeneratorTestUtils.cpp)
target_link_libraries(velox_expression_fuzzer_test_utility
velox_expression_functions velox_function_registry gtest)

add_executable(
velox_expression_fuzzer_unit_test
ArgumentTypeFuzzerTest.cpp DecimalArgGeneratorTest.cpp
ExpressionFuzzerUnitTest.cpp)

target_link_libraries(
velox_expression_fuzzer_unit_test
velox_expression_fuzzer
velox_functions_prestosql
velox_core
velox_expression
velox_expression_fuzzer_test_utility
gtest
gtest_main)
34 changes: 1 addition & 33 deletions velox/expression/fuzzer/tests/DecimalArgGeneratorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
*/

#include <gtest/gtest.h>
#include "velox/expression/SignatureBinder.h"
#include "velox/expression/fuzzer/DecimalArgGeneratorBase.h"
#include "velox/expression/fuzzer/tests/ArgGeneratorTestUtils.h"

namespace facebook::velox::fuzzer::test {

Expand Down Expand Up @@ -50,38 +50,6 @@ class DecimalArgGeneratorTest : public testing::Test {
return {{p, s}};
}
};

// Assert the equivalence between the given return type and the actual type
// resolved from generated argument types.
void assertReturnType(
const std::shared_ptr<DecimalArgGeneratorBase>& generator,
const exec::FunctionSignature& signature,
const TypePtr& returnType) {
std::mt19937 seed{0};
const auto argTypes = generator->generateArgs(signature, returnType, seed);

// Resolve return type from argument types for the given signature.
TypePtr actualType;
exec::SignatureBinder binder(signature, argTypes);
if (binder.tryBind()) {
actualType = binder.tryResolveReturnType();
} else {
VELOX_FAIL("Failed to resolve return type from argument types.");
}
EXPECT_TRUE(returnType->equivalent(*actualType))
<< "Expected type: " << returnType->toString()
<< ", actual type: " << actualType->toString();
}

// Assert that no argument types can be generated for the given return type.
void assertEmptyArgs(
std::shared_ptr<DecimalArgGeneratorBase> generator,
const exec::FunctionSignature& signature,
const TypePtr& returnType) {
std::mt19937 seed{0};
const auto argTypes = generator->generateArgs(signature, returnType, seed);
EXPECT_TRUE(argTypes.empty());
}
};

TEST_F(DecimalArgGeneratorTest, unary) {
Expand Down
42 changes: 42 additions & 0 deletions velox/functions/prestosql/fuzzer/DivideArgGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once

#include "velox/expression/fuzzer/DecimalArgGeneratorBase.h"

namespace facebook::velox::exec::test {

// An argument type generator for decimal divide Presto function.
class DivideArgGenerator : public fuzzer::DecimalArgGeneratorBase {
public:
DivideArgGenerator() {
initialize(2);
}

protected:
std::optional<std::pair<int, int>>
toReturnType(int p1, int s1, int p2, int s2) override {
if (s1 + s2 > 38) {
return std::nullopt;
}

auto p = std::min(38, p1 + s2 + std::max(0, s2 - s1));
auto s = std::max(s1, s2);
return {{p, s}};
}
};

} // namespace facebook::velox::exec::test
75 changes: 75 additions & 0 deletions velox/functions/prestosql/fuzzer/FloorAndRoundArgGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once

#include <boost/random/uniform_int_distribution.hpp>
#include "velox/expression/fuzzer/ArgGenerator.h"

namespace facebook::velox::exec::test {

// An argument type generator for decimal floor and round Presto functions.
class FloorAndRoundArgGenerator : public fuzzer::ArgGenerator {
public:
std::vector<TypePtr> generateArgs(
const exec::FunctionSignature& signature,
const TypePtr& returnType,
FuzzerGenerator& rng) override {
if (signature.argumentTypes().size() == 1) {
return generateSingleArg(returnType, rng);
}
VELOX_CHECK_EQ(2, signature.argumentTypes().size())
return generateTwoArgs(returnType);
}

private:
// Generates a decimal type following below formulas:
// p = p1 - s1 + min(s1, 1)
// s = 0
std::vector<TypePtr> generateSingleArg(
const TypePtr& returnType,
FuzzerGenerator& rng) {
const auto [p, s] = getDecimalPrecisionScale(*returnType);
if (s != 0) {
return {};
}

const auto s1 = rand32(38 - p + 1, rng);
if (s1 == 0) {
return {DECIMAL(p, 0)};
}

return {DECIMAL(p - 1 + s1, s1)};
}

// Generates a decimal type and an integer type. Decimal type is generated
// following below formulas:
// p = p1 + 1
// s = s1
std::vector<TypePtr> generateTwoArgs(const TypePtr& returnType) {
auto [p, s] = getDecimalPrecisionScale(*returnType);
if (p == 1 || p == s) {
return {};
}

return {DECIMAL(p - 1, s), INTEGER()};
}

static uint32_t rand32(uint32_t max, FuzzerGenerator& rng) {
return boost::random::uniform_int_distribution<uint32_t>()(rng) % max;
}
};

} // namespace facebook::velox::exec::test
42 changes: 42 additions & 0 deletions velox/functions/prestosql/fuzzer/MultiplyArgGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once

#include "velox/expression/fuzzer/DecimalArgGeneratorBase.h"

namespace facebook::velox::exec::test {

// An argument type generator for decimal multiply Presto function.
class MultiplyArgGenerator : public fuzzer::DecimalArgGeneratorBase {
public:
MultiplyArgGenerator() {
initialize(2);
}

protected:
std::optional<std::pair<int, int>>
toReturnType(int p1, int s1, int p2, int s2) override {
if (s1 + s2 > 38) {
return std::nullopt;
}

auto p = std::min(38, p1 + p2);
auto s = s1 + s2;
return {{p, s}};
}
};

} // namespace facebook::velox::exec::test
38 changes: 38 additions & 0 deletions velox/functions/prestosql/fuzzer/PlusMinusArgGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once

#include "velox/expression/fuzzer/DecimalArgGeneratorBase.h"

namespace facebook::velox::exec::test {

// An argument type generator for decimal plus and minus Presto functions.
class PlusMinusArgGenerator : public fuzzer::DecimalArgGeneratorBase {
public:
PlusMinusArgGenerator() {
initialize(2);
}

protected:
std::optional<std::pair<int, int>>
toReturnType(int p1, int s1, int p2, int s2) override {
auto s = std::max(s1, s2);
auto p = std::min(38, std::max(p1 - s1, p2 - s2) + 1 + s);
return {{p, s}};
}
};

} // namespace facebook::velox::exec::test
Loading

0 comments on commit 08ffe22

Please sign in to comment.