// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include #include #include #include #include #include #include #include #include #include #include #include using namespace search::fef; using namespace search::fef::test; using namespace search::features; using vespalib::eval::ValueType; struct ProxyExecutor : FeatureExecutor { bool input_is_object; bool output_is_object; double number_value; vespalib::eval::Value::UP object_value; ProxyExecutor(bool input_is_object_in, bool output_is_object_in) : input_is_object(input_is_object_in), output_is_object(output_is_object_in), number_value(0.0), object_value() {} bool isPure() override { return true; } void execute(uint32_t) override { double was_object = 0.0; if (input_is_object) { was_object = 1.0; number_value = inputs().get_object(0).get().as_double(); object_value.reset(new vespalib::eval::DoubleValue(number_value)); } else { number_value = inputs().get_number(0); object_value.reset(new vespalib::eval::DoubleValue(number_value)); } if (output_is_object) { outputs().set_object(0, *object_value); } else { outputs().set_number(0, number_value); } outputs().set_number(1, was_object); } }; struct ProxyBlueprint : Blueprint { vespalib::string name; AcceptInput accept_input; bool object_input; bool object_output; ProxyBlueprint(const vespalib::string &name_in, AcceptInput accept_input_in, bool object_output_in) : Blueprint(name_in), name(name_in), accept_input(accept_input_in), object_input(false), object_output(object_output_in) {} void visitDumpFeatures(const IIndexEnvironment &, IDumpFeatureVisitor &) const override {} Blueprint::UP createInstance() const override { return Blueprint::UP(new ProxyBlueprint(name, accept_input, object_output)); } bool setup(const IIndexEnvironment &, const std::vector ¶ms) override { ASSERT_EQUAL(1u, params.size()); object_input = defineInput(params[0], accept_input); describeOutput("value", "the value", object_output ? FeatureType::object(ValueType::double_type()) : FeatureType::number()); describeOutput("was_object", "whether input was object", FeatureType::number()); return true; } FeatureExecutor &createExecutor(const IQueryEnvironment &, vespalib::Stash &stash) const override { return stash.create(object_input, object_output); } }; struct Fixture { BlueprintFactory factory; IndexEnvironment indexEnv; explicit Fixture() { factory.addPrototype(Blueprint::SP(new ValueBlueprint())); factory.addPrototype(Blueprint::SP(new ProxyBlueprint("box", Blueprint::AcceptInput::NUMBER, true))); factory.addPrototype(Blueprint::SP(new ProxyBlueprint("maybe_box", Blueprint::AcceptInput::ANY, true))); factory.addPrototype(Blueprint::SP(new ProxyBlueprint("unbox", Blueprint::AcceptInput::OBJECT, false))); factory.addPrototype(Blueprint::SP(new ProxyBlueprint("maybe_unbox", Blueprint::AcceptInput::ANY, false))); } double eval(const vespalib::string &feature) { BlueprintResolver::SP resolver(new BlueprintResolver(factory, indexEnv)); resolver->addSeed(feature); if (!resolver->compile()) { return vespalib::eval::error_value; } MatchDataLayout mdl; QueryEnvironment queryEnv(&indexEnv); Properties overrides; RankProgram program(resolver); program.setup(mdl, queryEnv, overrides); auto result = program.get_seeds(); EXPECT_EQUAL(1u, result.num_features()); EXPECT_TRUE(!result.is_object(0)); // verifies auto-unboxing return result.resolve(0).as_number(1); } bool verify(const vespalib::string &feature) { return verifyFeature(factory, indexEnv, feature, "unit test"); } }; TEST_F("require that values can be boxed and unboxed", Fixture()) { EXPECT_EQUAL(3.0, f1.eval("box(value(3))")); EXPECT_EQUAL(0.0, f1.eval("box(value(3)).was_object")); EXPECT_EQUAL(3.0, f1.eval("unbox(box(value(3)))")); EXPECT_EQUAL(1.0, f1.eval("unbox(box(value(3))).was_object")); EXPECT_EQUAL(3.0, f1.eval("box(unbox(box(value(3))))")); EXPECT_EQUAL(0.0, f1.eval("box(unbox(box(value(3)))).was_object")); } TEST_F("require that output features may be either objects or numbers", Fixture()) { EXPECT_TRUE(f1.verify("value(3)")); EXPECT_TRUE(f1.verify("box(value(3))")); } TEST_F("require that feature input/output types must be compatible", Fixture()) { EXPECT_TRUE(!f1.verify("unbox(value(3))")); EXPECT_TRUE(f1.verify("maybe_unbox(value(3))")); EXPECT_TRUE(f1.verify("unbox(box(value(3)))")); EXPECT_TRUE(!f1.verify("unbox(box(box(value(3))))")); EXPECT_TRUE(f1.verify("unbox(maybe_box(box(value(3))))")); EXPECT_TRUE(f1.verify("unbox(box(unbox(box(value(3)))))")); } TEST_MAIN() { TEST_RUN_ALL(); }