/* Copyright 2015 The TensorFlow 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.
|
==============================================================================*/
|
|
// Test that verifies that various changes to an OpDef are
|
// backwards-compatible.
|
|
#include "tensorflow/core/framework/fake_input.h"
|
#include "tensorflow/core/framework/node_def_builder.h"
|
#include "tensorflow/core/framework/node_def_util.h"
|
#include "tensorflow/core/framework/op.h"
|
#include "tensorflow/core/framework/types.h"
|
#include "tensorflow/core/kernels/ops_testutil.h"
|
#include "tensorflow/core/lib/strings/str_util.h"
|
#include "tensorflow/core/platform/test.h"
|
|
namespace tensorflow {
|
namespace {
|
|
class TestKernel : public OpKernel {
|
public:
|
explicit TestKernel(OpKernelConstruction* context) : OpKernel(context) {}
|
void Compute(OpKernelContext* context) override {
|
Tensor* out_tensor = nullptr;
|
OP_REQUIRES_OK(context, context->allocate_output("ndef", TensorShape({}),
|
&out_tensor));
|
out_tensor->scalar<string>()() = SummarizeNodeDef(def());
|
}
|
};
|
|
class OpCompatibilityTest : public OpsTestBase {
|
protected:
|
const OpDef* RegisteredOpDef() {
|
const OpDef* op_def;
|
TF_CHECK_OK(OpRegistry::Global()->LookUpOpDef(node_def()->op(), &op_def));
|
return op_def;
|
}
|
|
void ExpectSuccess(const OpDef& old_op_def) {
|
// Record the original signature before we change *node_def().
|
DataTypeVector old_in_types, old_out_types;
|
TF_ASSERT_OK(InOutTypesForNode(*node_def(), old_op_def, &old_in_types,
|
&old_out_types));
|
|
// This should be all that is needed to get compatibility.
|
const OpDef* new_op_def = RegisteredOpDef();
|
AddDefaultsToNodeDef(*new_op_def, node_def());
|
|
// Validate that it is indeed compatible.
|
TF_ASSERT_OK(ValidateNodeDef(*node_def(), *new_op_def));
|
DataTypeVector new_in_types, new_out_types;
|
TF_ASSERT_OK(InOutTypesForNode(*node_def(), *new_op_def, &new_in_types,
|
&new_out_types));
|
if (new_in_types.size() == old_in_types.size()) {
|
// Ref inputs are allowed to become non-ref inputs.
|
for (int i = 0; i < new_in_types.size(); ++i) {
|
if (IsRefType(old_in_types[i]) && !IsRefType(new_in_types[i])) {
|
old_in_types[i] = RemoveRefType(old_in_types[i]);
|
}
|
}
|
}
|
ASSERT_EQ(new_in_types, old_in_types);
|
if (new_out_types.size() == old_out_types.size()) {
|
// Non-ref outputs are allowed to become ref outputs.
|
for (int i = 0; i < new_out_types.size(); ++i) {
|
if (!IsRefType(old_out_types[i]) && IsRefType(new_out_types[i])) {
|
old_out_types[i] = MakeRefType(old_out_types[i]);
|
}
|
}
|
}
|
ASSERT_EQ(new_out_types, old_out_types);
|
TF_ASSERT_OK(OpDefCompatible(old_op_def, *new_op_def));
|
|
// Verify the Op actually runs. Result() will return the output.
|
TF_ASSERT_OK(InitOp());
|
TF_ASSERT_OK(RunOpKernel());
|
}
|
|
string Result() { return GetOutput(0)->scalar<string>()(); }
|
|
void ExpectIncompatible(const OpDef& old_op_def, const OpDef& new_op_def,
|
const string& error) {
|
// Test OpDefCompatible gives the same answer without the node_def.
|
Status status = OpDefCompatible(old_op_def, new_op_def);
|
if (status.ok()) {
|
ADD_FAILURE() << SummarizeOpDef(old_op_def) << " vs. "
|
<< SummarizeOpDef(new_op_def);
|
} else {
|
EXPECT_TRUE(str_util::StrContains(status.error_message(), error))
|
<< status << " does not contain " << error;
|
}
|
}
|
|
void ExpectInvalid(const OpDef& old_op_def, const string& validation_error,
|
const string& compatibility_error) {
|
// Record the original signature before we change *node_def().
|
DataTypeVector old_in_types, old_out_types;
|
TF_ASSERT_OK(InOutTypesForNode(*node_def(), old_op_def, &old_in_types,
|
&old_out_types));
|
|
// This should be all that is needed to get compatibility.
|
const OpDef* new_op_def = RegisteredOpDef();
|
AddDefaultsToNodeDef(*new_op_def, node_def());
|
|
// Validate that it does not pass validation.
|
Status status = ValidateNodeDef(*node_def(), *new_op_def);
|
if (status.ok()) {
|
ADD_FAILURE() << SummarizeNodeDef(*node_def());
|
} else {
|
EXPECT_TRUE(
|
str_util::StrContains(status.error_message(), validation_error))
|
<< status << " does not contain " << validation_error;
|
}
|
|
ExpectIncompatible(old_op_def, *new_op_def, compatibility_error);
|
}
|
|
void ExpectTypeMismatch(const OpDef& old_op_def,
|
const string& compatibility_error) {
|
// Record the original signature before we change *node_def().
|
DataTypeVector old_in_types, old_out_types;
|
TF_ASSERT_OK(InOutTypesForNode(*node_def(), old_op_def, &old_in_types,
|
&old_out_types));
|
|
// This should be all that is needed to get compatibility.
|
const OpDef* new_op_def = RegisteredOpDef();
|
AddDefaultsToNodeDef(*new_op_def, node_def());
|
|
// Validate that it is valid, but with incompatible types.
|
TF_ASSERT_OK(ValidateNodeDef(*node_def(), *new_op_def));
|
|
DataTypeVector new_in_types, new_out_types;
|
TF_ASSERT_OK(InOutTypesForNode(*node_def(), *new_op_def, &new_in_types,
|
&new_out_types));
|
if (new_in_types == old_in_types && new_out_types == old_out_types) {
|
ADD_FAILURE() << SummarizeNodeDef(*node_def()) << "\n"
|
<< DataTypeVectorString(new_in_types) << " -> "
|
<< DataTypeVectorString(new_out_types);
|
}
|
|
ExpectIncompatible(old_op_def, *new_op_def, compatibility_error);
|
}
|
|
void ExpectRenameFailure(const OpDef& old_op_def,
|
const string& compatibility_error) {
|
// This should be all that is needed to get compatibility.
|
const OpDef* new_op_def = RegisteredOpDef();
|
AddDefaultsToNodeDef(*new_op_def, node_def());
|
|
// Validate that the NodeDef is valid. This will ignore
|
// problems caused by output name changes for functions.
|
TF_ASSERT_OK(ValidateNodeDef(*node_def(), *new_op_def));
|
|
ExpectIncompatible(old_op_def, *new_op_def, compatibility_error);
|
}
|
|
void ExpectDefaultChangeFailure(const OpDef& old_op_def,
|
const string& compatibility_error) {
|
// This should be all that is needed to get compatibility.
|
const OpDef* new_op_def = RegisteredOpDef();
|
AddDefaultsToNodeDef(*new_op_def, node_def());
|
|
// Validate that the NodeDef is valid.
|
TF_ASSERT_OK(ValidateNodeDef(*node_def(), *new_op_def));
|
|
Status status = OpDefAttrDefaultsUnchanged(old_op_def, *new_op_def);
|
if (status.ok()) {
|
ADD_FAILURE() << SummarizeOpDef(old_op_def) << " vs. "
|
<< SummarizeOpDef(*new_op_def);
|
} else {
|
EXPECT_TRUE(
|
str_util::StrContains(status.error_message(), compatibility_error))
|
<< status << " does not contain " << compatibility_error;
|
}
|
}
|
};
|
|
// Should be compatible if the Op hasn't changed (sanity check).
|
REGISTER_OP("Same")
|
.Input("a: int32")
|
.Input("b: T")
|
.Input("c: N * int32")
|
.Input("d: N * T")
|
.Input("e: TList")
|
.Output("ndef: string")
|
.Attr("T: type")
|
.Attr("N: int")
|
.Attr("TList: list(type)");
|
REGISTER_KERNEL_BUILDER(Name("Same").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, Same) {
|
TF_ASSERT_OK(NodeDefBuilder("same", "Same")
|
.Input(FakeInput())
|
.Input(FakeInput(DT_FLOAT))
|
.Input(FakeInput(3))
|
.Input(FakeInput(3, DT_FLOAT))
|
.Input(FakeInput(2, DT_BOOL))
|
.Finalize(node_def()));
|
ExpectSuccess(*RegisteredOpDef());
|
EXPECT_EQ(
|
"{{node same}} = Same[N=3, T=DT_FLOAT, TList=[DT_BOOL, DT_BOOL]](a, b, "
|
"c, c:1, c:2, d, d:1, d:2, e, e:1)",
|
Result());
|
}
|
|
// Should be able to add an attr with a default.
|
REGISTER_OP("AddAttr").Output("ndef: string").Attr("a: int = 42");
|
REGISTER_KERNEL_BUILDER(Name("AddAttr").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AddAttr) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("AddAttr").Output("ndef: string").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("add_attr", &old_op.op_def).Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node add_attr}} = AddAttr[a=42]()", Result());
|
}
|
|
// Should be able to make an attr restriction less strict.
|
REGISTER_OP("LessStrict").Output("ndef: string").Attr("a: {'A', 'B', 'C'}");
|
REGISTER_KERNEL_BUILDER(Name("LessStrict").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, LessStrict) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("LessStrict")
|
.Output("ndef: string")
|
.Attr("a: {'A', 'B'}")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("less_strict", &old_op.op_def)
|
.Attr("a", "B")
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node less_strict}} = LessStrict[a=\"B\"]()", Result());
|
}
|
|
// Should be able to remove an attr restriction.
|
REGISTER_OP("RemoveRestriction").Output("ndef: string").Attr("a: type");
|
REGISTER_KERNEL_BUILDER(Name("RemoveRestriction").Device(DEVICE_CPU),
|
TestKernel);
|
|
TEST_F(OpCompatibilityTest, RemoveRestriction) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("RemoveRestriction")
|
.Output("ndef: string")
|
.Attr("a: {int32, bool}")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("remove_restriction", &old_op.op_def)
|
.Attr("a", DT_INT32)
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node remove_restriction}} = RemoveRestriction[a=DT_INT32]()",
|
Result());
|
}
|
|
// Should be able to change the order of attrs.
|
REGISTER_OP("AttrOrder").Output("ndef: string").Attr("a: int").Attr("b: bool");
|
REGISTER_KERNEL_BUILDER(Name("AttrOrder").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AttrOrder) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrOrder")
|
.Output("ndef: string")
|
.Attr("b: bool")
|
.Attr("a: int")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("attr_order", &old_op.op_def)
|
.Attr("b", true)
|
.Attr("a", 7)
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node attr_order}} = AttrOrder[a=7, b=true]()", Result());
|
}
|
|
// Should be able to make an input/output polymorphic.
|
// Changing from int32 -> T (where T: type = DT_INT32 by default).
|
REGISTER_OP("TypePolymorphic")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: type = DT_INT32");
|
REGISTER_KERNEL_BUILDER(Name("TypePolymorphic").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, TypePolymorphic) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("TypePolymorphic")
|
.Input("a: int32")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("type_polymorphic", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node type_polymorphic}} = TypePolymorphic[T=DT_INT32](a)",
|
Result());
|
}
|
|
// Should be able to make a single input/output into a list.
|
// Changing from int32 -> N * int32 (where N: int = 1 by default).
|
REGISTER_OP("MakeList")
|
.Input("a: N * int32")
|
.Output("ndef: string")
|
.Attr("N: int = 1");
|
REGISTER_KERNEL_BUILDER(Name("MakeList").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, MakeList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("MakeList")
|
.Input("a: int32")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("make_list", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node make_list}} = MakeList[N=1](a)", Result());
|
}
|
|
// Should be able to make a single input/output into a polymorphic list.
|
// Changing from int32 -> N * T (where N: int = 1 by default and
|
// T: type = DT_INT32 by default).
|
REGISTER_OP("MakePolyList")
|
.Input("a: N * T")
|
.Output("ndef: string")
|
.Attr("N: int = 1")
|
.Attr("T: type = DT_INT32");
|
REGISTER_KERNEL_BUILDER(Name("MakePolyList").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, MakePolyList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("MakePolyList")
|
.Input("a: int32")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("make_poly_list", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node make_poly_list}} = MakePolyList[N=1, T=DT_INT32](a)",
|
Result());
|
}
|
|
// Should be able to make a single input/output into an arbitrary list.
|
// Changing from int32 -> T (where T: list(type) = [DT_INT32] by default).
|
REGISTER_OP("MakeAnyList")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: list(type) = [DT_INT32]");
|
REGISTER_KERNEL_BUILDER(Name("MakeAnyList").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, MakeAnyList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("MakeAnyList")
|
.Input("a: int32")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("make_any_list", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node make_any_list}} = MakeAnyList[T=[DT_INT32]](a)", Result());
|
}
|
|
// Should be able to make a single polymorphic input/output into a list of
|
// the same type. Changing from T -> N * T (where N: int = 1 by default).
|
REGISTER_OP("PolyIntoList")
|
.Input("a: N * T")
|
.Output("ndef: string")
|
.Attr("N: int = 1")
|
.Attr("T: type");
|
REGISTER_KERNEL_BUILDER(Name("PolyIntoList").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, PolyIntoList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("PolyIntoList")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: type")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("poly_into_list", &old_op.op_def)
|
.Input(FakeInput(DT_INT32))
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node poly_into_list}} = PolyIntoList[N=1, T=DT_INT32](a)",
|
Result());
|
}
|
|
// Should be able to make a multiple inputs/outputs into a list with
|
// the default types matching the inputs/outputs being replaced.
|
|
// Changing from int32, int32 -> N * int32 (where N: int = 2 by default).
|
REGISTER_OP("MakeMultipleSameList")
|
.Input("a: N * int32")
|
.Output("ndef: string")
|
.Attr("N: int = 2");
|
REGISTER_KERNEL_BUILDER(Name("MakeMultipleSameList").Device(DEVICE_CPU),
|
TestKernel);
|
|
TEST_F(OpCompatibilityTest, MakeMultipleSameList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("MakeMultipleSameList")
|
.Input("a: int32")
|
.Input("b: int32")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("make_list", &old_op.op_def)
|
.Input(FakeInput())
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node make_list}} = MakeMultipleSameList[N=2](a, b)", Result());
|
}
|
|
// Changing from int32, float -> T
|
// (where T: list(type) = [int32, float] by default).
|
REGISTER_OP("MakeMultipleAnyList")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: list(type) = [DT_INT32, DT_FLOAT]");
|
REGISTER_KERNEL_BUILDER(Name("MakeMultipleAnyList").Device(DEVICE_CPU),
|
TestKernel);
|
|
TEST_F(OpCompatibilityTest, MakeMultipleAnyList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("MakeMultipleAnyList")
|
.Input("a: int32")
|
.Input("b: float")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("make_list", &old_op.op_def)
|
.Input(FakeInput())
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ(
|
"{{node make_list}} = MakeMultipleAnyList[T=[DT_INT32, DT_FLOAT]](a, b)",
|
Result());
|
}
|
|
// Should be able to change the name of an input/output.
|
REGISTER_OP("ChangeName").Input("y: int32").Output("ndef: string");
|
REGISTER_KERNEL_BUILDER(Name("ChangeName").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, ChangeName) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("ChangeName")
|
.Input("x: int32")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("change_name", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node change_name}} = ChangeName[](a)", Result());
|
}
|
|
// Should be able to add an input/output of type
|
// N * int32 (where N: int = 0 by default).
|
REGISTER_OP("AddNInts")
|
.Input("a: N * int32")
|
.Output("ndef: string")
|
.Attr("N: int >= 0 = 0");
|
REGISTER_KERNEL_BUILDER(Name("AddNInts").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AddNInts) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("AddNInts").Output("ndef: string").Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("add_n_ints", &old_op.op_def).Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node add_n_ints}} = AddNInts[N=0]()", Result());
|
}
|
|
// Should be able to add an input/output of type N * T
|
// (where N: int = 0 by default, and T: type = any valid default).
|
REGISTER_OP("AddNSame")
|
.Input("a: N * T")
|
.Output("ndef: string")
|
.Attr("N: int >= 0 = 0")
|
.Attr("T: type = DT_BOOL");
|
REGISTER_KERNEL_BUILDER(Name("AddNSame").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AddNSame) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("AddNSame").Output("ndef: string").Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("add_n_same", &old_op.op_def).Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node add_n_same}} = AddNSame[N=0, T=DT_BOOL]()", Result());
|
}
|
|
// Should be able to add an input/output of type N * T
|
// (where N: int >= 0 = 0 by default, and T an existing type attr).
|
REGISTER_OP("AddNSameAsExisting")
|
.Input("a: T")
|
.Input("b: N * T")
|
.Output("ndef: string")
|
.Attr("N: int >= 0 = 0")
|
.Attr("T: type");
|
REGISTER_KERNEL_BUILDER(Name("AddNSameAsExisting").Device(DEVICE_CPU),
|
TestKernel);
|
|
TEST_F(OpCompatibilityTest, AddNSameAsExisting) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AddNSameAsExisting")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: type")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("add_n_same_as_existing", &old_op.op_def)
|
.Input(FakeInput(DT_STRING))
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ(
|
"{{node add_n_same_as_existing}} = AddNSameAsExisting[N=0, "
|
"T=DT_STRING](a)",
|
Result());
|
}
|
|
// Should be able to add an input/output of type T
|
// (where T: list(type) >= 0 = [] by default).
|
REGISTER_OP("AddAnyList")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: list(type) >= 0 = []");
|
REGISTER_KERNEL_BUILDER(Name("AddAnyList").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AddAnyList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("AddAnyList").Output("ndef: string").Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("add_any_list", &old_op.op_def).Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node add_any_list}} = AddAnyList[T=[]]()", Result());
|
}
|
|
// Should be able to allow shorter lists.
|
REGISTER_OP("ShorterAnyList")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: list(type) >= 1");
|
REGISTER_KERNEL_BUILDER(Name("ShorterAnyList").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, ShorterAnyList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("ShorterAnyList")
|
.Input("a: T")
|
.Output("ndef: string")
|
.Attr("T: list(type) >= 2")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("shorter_any_list", &old_op.op_def)
|
.Input(FakeInput(2, DT_BOOL))
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ(
|
"{{node shorter_any_list}} = ShorterAnyList[T=[DT_BOOL, DT_BOOL]](a, "
|
"a:1)",
|
Result());
|
}
|
|
REGISTER_OP("ShorterSameList")
|
.Input("a: N * int32")
|
.Output("ndef: string")
|
.Attr("N: int >= 1");
|
REGISTER_KERNEL_BUILDER(Name("ShorterSameList").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, ShorterSameList) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("ShorterSameList")
|
.Input("a: N * int32")
|
.Output("ndef: string")
|
.Attr("N: int >= 2")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("shorter_same_list", &old_op.op_def)
|
.Input(FakeInput(2))
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node shorter_same_list}} = ShorterSameList[N=2](a, a:1)",
|
Result());
|
}
|
|
// Can remove a restriction to an attr
|
|
REGISTER_OP("AttrRemoveRestriction").Attr("t: type").Output("ndef: string");
|
REGISTER_KERNEL_BUILDER(Name("AttrRemoveRestriction").Device(DEVICE_CPU),
|
TestKernel);
|
|
TEST_F(OpCompatibilityTest, AttrRemoveRestriction) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrRemoveRestriction")
|
.Attr("t: {int32,int64}")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("remove_restriction", &old_op.op_def)
|
.Attr("t", DT_INT32)
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node remove_restriction}} = AttrRemoveRestriction[t=DT_INT32]()",
|
Result());
|
}
|
|
// Can make the restrictions on an attr less restrictive.
|
|
REGISTER_OP("AttrLessRestrictive")
|
.Attr("t: {int32, int64, bool}")
|
.Output("ndef: string");
|
REGISTER_KERNEL_BUILDER(Name("AttrLessRestrictive").Device(DEVICE_CPU),
|
TestKernel);
|
|
TEST_F(OpCompatibilityTest, AttrLessRestrictive) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrLessRestrictive")
|
.Attr("t: {int32, int64}")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("less_restrictive", &old_op.op_def)
|
.Attr("t", DT_INT32)
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node less_restrictive}} = AttrLessRestrictive[t=DT_INT32]()",
|
Result());
|
}
|
|
// Can remove a minimum from an attr.
|
|
REGISTER_OP("AttrRemoveMin").Attr("n: int").Output("ndef: string");
|
REGISTER_KERNEL_BUILDER(Name("AttrRemoveMin").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AttrRemoveMin) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrRemoveMin")
|
.Attr("n: int >= 3")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("remove_min", &old_op.op_def)
|
.Attr("n", 4)
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node remove_min}} = AttrRemoveMin[n=4]()", Result());
|
}
|
|
// Can lower the minimum on an attr.
|
|
REGISTER_OP("AttrLowerMin").Attr("n: int >= 1").Output("ndef: string");
|
REGISTER_KERNEL_BUILDER(Name("AttrLowerMin").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AttrLowerMin) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrLowerMin")
|
.Attr("n: int >= 3")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("lower_min", &old_op.op_def)
|
.Attr("n", 4)
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
EXPECT_EQ("{{node lower_min}} = AttrLowerMin[n=4]()", Result());
|
}
|
|
// Can make a ref input into a non-ref input.
|
|
REGISTER_OP("InputRemoveRef").Input("i: int32").Output("ndef: string");
|
REGISTER_KERNEL_BUILDER(Name("InputRemoveRef").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, InputRemoveRef) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("InputRemoveRef")
|
.Input("i: Ref(int32)")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("remove_input_ref", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
}
|
|
// Can make a non-ref output into a ref output.
|
|
REGISTER_OP("OutputAddRef").Output("o: Ref(int32)").Output("ndef: string");
|
REGISTER_KERNEL_BUILDER(Name("OutputAddRef").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, OutputAddRef) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("OutputAddRef")
|
.Output("o: int32")
|
.Output("ndef: string")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("add_output_ref", &old_op.op_def).Finalize(node_def()));
|
ExpectSuccess(old_op.op_def);
|
}
|
|
// Negative tests -------------------------------------------------------------
|
|
// Can't remove an attr.
|
REGISTER_OP("RemoveAttr");
|
|
TEST_F(OpCompatibilityTest, RemoveAttrFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("RemoveAttr").Attr("a: int").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Attr("a", 3)
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def, "NodeDef mentions attr 'a' not in",
|
"Attr 'a' removed");
|
}
|
|
// Can't add an attr without a default.
|
REGISTER_OP("AddAttrNoDefault").Attr("a: int");
|
|
TEST_F(OpCompatibilityTest, AddAttrNoDefaultFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AddAttrNoDefault").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def).Finalize(node_def()));
|
ExpectInvalid(old_op.op_def, "NodeDef missing attr 'a'",
|
"Attr 'a' added without default");
|
}
|
|
// Can't add a non-list input/output.
|
REGISTER_OP("AddSingleInput").Input("a: int32");
|
|
TEST_F(OpCompatibilityTest, AddSingleInputFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AddSingleInput").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def).Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"expected inputs 'int32' do not match 0 inputs specified",
|
"Input signature mismatch '' vs. 'int32'");
|
}
|
|
// Can't add a list input/output without an empty default.
|
|
REGISTER_OP("AddNIntsBigDefault").Input("a: N * int32").Attr("N: int = 1");
|
REGISTER_OP("AddNSameBigDefault")
|
.Input("a: N * T")
|
.Attr("N: int = 1")
|
.Attr("T: type = DT_INT32");
|
REGISTER_OP("AddListBigDefault")
|
.Input("a: T")
|
.Attr("T: list(type) = [DT_INT32]");
|
|
TEST_F(OpCompatibilityTest, AddNIntsBigDefaultFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AddNIntsBigDefault").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def).Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"expected inputs 'int32' do not match 0 inputs specified",
|
"Input signature mismatch '' vs. 'int32'");
|
}
|
|
TEST_F(OpCompatibilityTest, AddNSameBigDefaultFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AddNSameBigDefault").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def).Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"expected inputs 'int32' do not match 0 inputs specified",
|
"Input signature mismatch '' vs. 'int32'");
|
}
|
|
TEST_F(OpCompatibilityTest, AddListBigDefaultFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AddListBigDefault").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def).Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"expected inputs 'int32' do not match 0 inputs specified",
|
"Input signature mismatch '' vs. 'int32'");
|
}
|
|
// Can't change the type of an input/output.
|
|
REGISTER_OP("ChangeType").Input("a: float");
|
|
TEST_F(OpCompatibilityTest, ChangeTypeFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("ChangeType").Input("a: int32").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectTypeMismatch(old_op.op_def,
|
"Input signature mismatch 'int32' vs. 'float'");
|
}
|
|
// Can't change the order of inputs/outputs.
|
|
REGISTER_OP("ChangeOrder").Input("a: float").Input("b: int32");
|
|
TEST_F(OpCompatibilityTest, ChangeOrderFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("ChangeOrder")
|
.Input("b: int32")
|
.Input("a: float")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Input(FakeInput())
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectTypeMismatch(
|
old_op.op_def,
|
"Input signature mismatch 'int32, float' vs. 'float, int32'");
|
}
|
|
// Can't remove inputs/outputs.
|
|
REGISTER_OP("RemoveInput");
|
|
TEST_F(OpCompatibilityTest, RemoveInputFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("RemoveInput").Input("a: float").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"expected inputs '' do not match 1 inputs specified",
|
"Input signature mismatch 'float' vs. ''");
|
}
|
|
// Can't change the type of an attr.
|
|
REGISTER_OP("ChangeAttrType").Attr("a: int");
|
|
TEST_F(OpCompatibilityTest, ChangeAttrTypeFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("ChangeAttrType").Attr("a: bool").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Attr("a", true)
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def, "value with type 'bool' when 'int' expected",
|
"Attr 'a' changed type 'bool' -> 'int'");
|
}
|
|
// Can't change an attr from a list.
|
|
REGISTER_OP("AttrFromList").Attr("a: int");
|
|
TEST_F(OpCompatibilityTest, AttrFromListFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("AttrFromList").Attr("a: list(int)").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Attr("a", {5})
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"value with type 'list(int)' when 'int' expected",
|
"Attr 'a' changed type 'list(int)' -> 'int'");
|
}
|
|
// Can't change an attr to a list.
|
|
REGISTER_OP("AttrToList").Attr("a: list(int)");
|
|
TEST_F(OpCompatibilityTest, AttrToListFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrToList").Attr("a: int").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Attr("a", 5)
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"value with type 'int' when 'list(int)' expected",
|
"Attr 'a' changed type 'int' -> 'list(int)'");
|
}
|
|
// Can't change an input from polymorphic to a list of any type.
|
|
REGISTER_OP("PolymorphicToAnyList").Input("a: T").Attr("T: list(type)");
|
|
TEST_F(OpCompatibilityTest, PolymorphicToAnyListFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("PolymorphicToAnyList")
|
.Input("a: T")
|
.Attr("T: type")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Input(FakeInput(DT_INT32))
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"value with type 'type' when 'list(type)' expected",
|
"Attr 'T' changed type 'type' -> 'list(type)'");
|
}
|
|
// Can't change an input from a list of the same type to a list of any type.
|
|
REGISTER_OP("SameToAnyList")
|
.Input("a: T")
|
.Attr("T: list(type)")
|
.Attr("N: int = 1");
|
|
TEST_F(OpCompatibilityTest, SameToAnyListFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("SameToAnyList")
|
.Input("a: N * T")
|
.Attr("T: type")
|
.Attr("N: int")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("fails", &old_op.op_def)
|
.Input(FakeInput(1, DT_INT32))
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"value with type 'type' when 'list(type)' expected",
|
"Attr 'T' changed type 'type' -> 'list(type)'");
|
}
|
|
// Can't add a restriction to an attr
|
|
REGISTER_OP("AttrAddRestriction").Attr("t: {int32, int64}");
|
|
TEST_F(OpCompatibilityTest, AttrAddRestrictionFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("AttrAddRestriction").Attr("t: type").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("add_restriction", &old_op.op_def)
|
.Attr("t", DT_BOOL)
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"Value for attr 't' of bool is not in the list of allowed "
|
"values: int32, int64",
|
"Attr 't' has a stricter set of allowed values; from "
|
"no restriction to [DT_INT32, DT_INT64]");
|
}
|
|
// Can't make the restrictions on an attr more restrictive.
|
|
REGISTER_OP("AttrMoreRestrictive").Attr("t: {int32, int64}");
|
|
TEST_F(OpCompatibilityTest, AttrMoreRestrictiveFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrMoreRestrictive")
|
.Attr("t: {int32, int64, bool}")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("more_restrictive", &old_op.op_def)
|
.Attr("t", DT_BOOL)
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"Value for attr 't' of bool is not in the list of allowed "
|
"values: int32, int64",
|
"Attr 't' has a stricter set of allowed values; from "
|
"[DT_INT32, DT_INT64, DT_BOOL] to [DT_INT32, DT_INT64]");
|
}
|
|
// Can't add a minimum to an attr.
|
|
REGISTER_OP("AttrAddMin").Attr("n: int >= 3");
|
|
TEST_F(OpCompatibilityTest, AttrAddMinFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AttrAddMin").Attr("n: int").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("add_min", &old_op.op_def)
|
.Attr("n", 2)
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"Value for attr 'n' of 2 must be at least minimum 3",
|
"Attr 'n' has a higher minimum; from no minimum to 3");
|
}
|
|
// Can't raise the minimum on an attr.
|
|
REGISTER_OP("AttrRaiseMin").Attr("n: int >= 3");
|
|
TEST_F(OpCompatibilityTest, AttrRaiseMinFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("AttrRaiseMin").Attr("n: int >= 1").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("raise_min", &old_op.op_def)
|
.Attr("n", 2)
|
.Finalize(node_def()));
|
ExpectInvalid(old_op.op_def,
|
"Value for attr 'n' of 2 must be at least minimum 3",
|
"Attr 'n' has a higher minimum; from 1 to 3");
|
}
|
|
// Can't make a non-ref input into a ref input.
|
|
REGISTER_OP("InputAddRef").Input("i: Ref(int32)");
|
|
TEST_F(OpCompatibilityTest, InputAddRefFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("InputAddRef").Input("i: int32").Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("add_input_ref", &old_op.op_def)
|
.Input(FakeInput())
|
.Finalize(node_def()));
|
ExpectTypeMismatch(old_op.op_def, "Input 0 changed from non-ref to ref");
|
}
|
|
// Can't make a ref output into a non-ref output.
|
|
REGISTER_OP("OutputRemoveRef").Output("o: int32");
|
|
TEST_F(OpCompatibilityTest, OutputRemoveRefFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("OutputRemoveRef")
|
.Output("o: Ref(int32)")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("remove_output_ref", &old_op.op_def).Finalize(node_def()));
|
ExpectTypeMismatch(old_op.op_def, "Output 0 changed from ref to non-ref");
|
}
|
|
// Can't rename an output, to avoid problems in FunctionDefs.
|
|
REGISTER_OP("RenameOutput").Output("new: int32");
|
|
TEST_F(OpCompatibilityTest, RenameOutputFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(
|
OpDefBuilder("RenameOutput").Output("old: int32").Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("rename_output", &old_op.op_def).Finalize(node_def()));
|
ExpectRenameFailure(old_op.op_def,
|
"Output signature mismatch 'old:int32' vs. 'new:int32'");
|
}
|
|
REGISTER_OP("RenameNOutputs").Output("new: N*int32").Attr("N: int");
|
|
TEST_F(OpCompatibilityTest, RenameNOutputsFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("RenameNOutputs")
|
.Output("old: N*int32")
|
.Attr("N: int")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("rename_n_outputs", &old_op.op_def)
|
.Attr("N", 2)
|
.Finalize(node_def()));
|
ExpectRenameFailure(
|
old_op.op_def,
|
"Output signature mismatch 'old:N * int32' vs. 'new:N * int32'");
|
}
|
|
REGISTER_OP("RenameOutputList").Output("new: T").Attr("T: list(type)");
|
|
TEST_F(OpCompatibilityTest, RenameOutputListFails) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("RenameOutputList")
|
.Output("old: T")
|
.Attr("T: list(type)")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("rename_output_list", &old_op.op_def)
|
.Attr("T", {DT_INT32, DT_FLOAT})
|
.Finalize(node_def()));
|
ExpectRenameFailure(old_op.op_def,
|
"Output signature mismatch 'old:T' vs. 'new:T'");
|
}
|
|
// Should not be able to add a default to an attr.
|
REGISTER_OP("AddDefault").Output("ndef: string").Attr("a: int = 1234");
|
REGISTER_KERNEL_BUILDER(Name("AddDefault").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, AddDefault) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("AddDefault")
|
.Output("ndef: string")
|
.Attr("a: int")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(NodeDefBuilder("add_default", &old_op.op_def)
|
.Attr("a", 765)
|
.Finalize(node_def()));
|
ExpectDefaultChangeFailure(
|
old_op.op_def,
|
"Attr 'a' has added/removed it's default; from no default to 1234");
|
}
|
|
// Should not be able to remove a default from an attr.
|
REGISTER_OP("RemoveDefault").Output("ndef: string").Attr("a: int");
|
REGISTER_KERNEL_BUILDER(Name("RemoveDefault").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, RemoveDefault) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("RemoveDefault")
|
.Output("ndef: string")
|
.Attr("a: int = 91")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("remove_default", &old_op.op_def).Finalize(node_def()));
|
ExpectDefaultChangeFailure(
|
old_op.op_def,
|
"Attr 'a' has added/removed it's default; from 91 to no default");
|
}
|
|
// Should not be able to change a default for an attr.
|
REGISTER_OP("ChangeDefault").Output("ndef: string").Attr("a: int = 1");
|
REGISTER_KERNEL_BUILDER(Name("ChangeDefault").Device(DEVICE_CPU), TestKernel);
|
|
TEST_F(OpCompatibilityTest, ChangeDefault) {
|
OpRegistrationData old_op;
|
TF_ASSERT_OK(OpDefBuilder("ChangeDefault")
|
.Output("ndef: string")
|
.Attr("a: int = 2")
|
.Finalize(&old_op));
|
TF_ASSERT_OK(
|
NodeDefBuilder("change_default", &old_op.op_def).Finalize(node_def()));
|
ExpectDefaultChangeFailure(
|
old_op.op_def, "Attr 'a' has changed it's default value; from 2 to 1");
|
}
|
|
} // namespace
|
} // namespace tensorflow
|