Document graph service usage with docs and unit tests.

PiperOrigin-RevId: 513955877
This commit is contained in:
MediaPipe Team 2023-03-03 16:59:29 -08:00 committed by Copybara-Service
parent 3854d9fcd5
commit 9c3abcd06f
3 changed files with 204 additions and 0 deletions

View File

@ -1533,6 +1533,7 @@ cc_test(
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"//mediapipe/framework/port:status_matchers",
"//mediapipe/framework/tool:sink",
],
)

View File

@ -141,6 +141,12 @@ class CalculatorContract {
class GraphServiceRequest {
public:
// APIs that should be used by calculators.
//
// Indicates that requested service is optional and calculator can operate
// correctly without it.
//
// NOTE: `CalculatorGraph` will still try to create services which allow
// default initialization. (See `CalculatorGraph::UseService`)
GraphServiceRequest& Optional() {
optional_ = true;
return *this;
@ -158,6 +164,17 @@ class CalculatorContract {
bool optional_ = false;
};
// Indicates specific `service` is required for graph execution.
//
// For services which allow default initialization:
// - `CalculatorGraph` will try to create corresponding service object by
// default even if request is made optional
// (`GraphServiceRequest::Optional()`)
//
// For services which disallow default initialization:
// - `CalculatorGraph` requires client to set corresponding service object and
// otherwise fails, unles request is mad optional
// (`GraphServiceRequest::Optional()`)
GraphServiceRequest& UseService(const GraphServiceBase& service) {
auto it = service_requests_.emplace(service.key, service).first;
return it->second;

View File

@ -157,5 +157,191 @@ TEST_F(GraphServiceTest, CreateDefault) {
MP_EXPECT_OK(kNeedsCreateService.CreateDefaultObject());
}
struct TestServiceData {};
const GraphService<TestServiceData> kTestServiceAllowDefaultInitialization(
"kTestServiceAllowDefaultInitialization",
GraphServiceBase::kAllowDefaultInitialization);
// This is only for test purposes. Ideally, a calculator that fails when service
// is not available, should request the service as non-Optional.
class FailOnUnavailableOptionalServiceCalculator : public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
cc->UseService(kTestServiceAllowDefaultInitialization).Optional();
return absl::OkStatus();
}
absl::Status Open(CalculatorContext* cc) final {
RET_CHECK(cc->Service(kTestServiceAllowDefaultInitialization).IsAvailable())
<< "Service is unavailable.";
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) final { return absl::OkStatus(); }
};
REGISTER_CALCULATOR(FailOnUnavailableOptionalServiceCalculator);
// Documents and ensures current behavior for requesting optional
// "AllowDefaultInitialization" services:
// - Service object is created by default.
TEST(AllowDefaultInitializationGraphServiceTest,
ServiceIsAvailableWithOptionalUse) {
CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node { calculator: 'FailOnUnavailableOptionalServiceCalculator' }
)pb");
CalculatorGraph graph;
MP_ASSERT_OK(graph.Initialize(config));
MP_ASSERT_OK(graph.StartRun({}));
MP_EXPECT_OK(graph.WaitUntilIdle());
}
// Documents and ensures current behavior for setting `nullptr` service objects
// for "AllowDefaultInitialization" optional services.
// - It's allowed.
// - It disables creation of "AllowDefaultInitialization" service objects, hence
// results in optional service unavailability.
TEST(AllowDefaultInitializationGraphServiceTest,
NullServiceObjectIsAllowAndResultsInOptionalServiceUnavailability) {
CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node { calculator: 'FailOnUnavailableOptionalServiceCalculator' }
)pb");
CalculatorGraph graph;
std::shared_ptr<TestServiceData> object = nullptr;
MP_ASSERT_OK(
graph.SetServiceObject(kTestServiceAllowDefaultInitialization, object));
MP_ASSERT_OK(graph.Initialize(config));
MP_ASSERT_OK(graph.StartRun({}));
EXPECT_THAT(graph.WaitUntilIdle(),
StatusIs(absl::StatusCode::kInternal,
HasSubstr("Service is unavailable.")));
}
class FailOnUnavailableServiceCalculator : public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
cc->UseService(kTestServiceAllowDefaultInitialization);
return absl::OkStatus();
}
absl::Status Open(CalculatorContext* cc) final {
RET_CHECK(cc->Service(kTestServiceAllowDefaultInitialization).IsAvailable())
<< "Service is unavailable.";
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) final { return absl::OkStatus(); }
};
REGISTER_CALCULATOR(FailOnUnavailableServiceCalculator);
// Documents and ensures current behavior for requesting optional
// "AllowDefaultInitialization" services:
// - Service object is created by default.
TEST(AllowDefaultInitializationGraphServiceTest, ServiceIsAvailable) {
CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node { calculator: 'FailOnUnavailableServiceCalculator' }
)pb");
CalculatorGraph graph;
MP_ASSERT_OK(graph.Initialize(config));
MP_ASSERT_OK(graph.StartRun({}));
MP_EXPECT_OK(graph.WaitUntilIdle());
}
// Documents and ensures current behavior for setting `nullptr` service objects
// for "AllowDefaultInitialization" services.
// - It's allowed.
// - It disables creation of "AllowDefaultInitialization" service objects, hence
// in service unavaialbility.
TEST(AllowDefaultInitializationGraphServiceTest,
NullServiceObjectIsAllowAndResultsInServiceUnavailability) {
CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node { calculator: 'FailOnUnavailableServiceCalculator' }
)pb");
CalculatorGraph graph;
std::shared_ptr<TestServiceData> object = nullptr;
MP_ASSERT_OK(
graph.SetServiceObject(kTestServiceAllowDefaultInitialization, object));
MP_ASSERT_OK(graph.Initialize(config));
MP_ASSERT_OK(graph.StartRun({}));
EXPECT_THAT(graph.WaitUntilIdle(),
StatusIs(absl::StatusCode::kInternal,
HasSubstr("Service is unavailable.")));
}
const GraphService<TestServiceData> kTestServiceDisallowDefaultInitialization(
"kTestServiceDisallowDefaultInitialization",
GraphServiceBase::kDisallowDefaultInitialization);
class FailOnUnavailableOptionalDisallowDefaultInitServiceCalculator
: public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
cc->UseService(kTestServiceDisallowDefaultInitialization).Optional();
return absl::OkStatus();
}
absl::Status Open(CalculatorContext* cc) final {
RET_CHECK(
cc->Service(kTestServiceDisallowDefaultInitialization).IsAvailable())
<< "Service is unavailable.";
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) final { return absl::OkStatus(); }
};
REGISTER_CALCULATOR(
FailOnUnavailableOptionalDisallowDefaultInitServiceCalculator);
// Documents and ensures current behavior for requesting optional
// "DisallowDefaultInitialization" services:
// - Service object is not created by default.
TEST(DisallowDefaultInitializationGraphServiceTest,
ServiceIsUnavailableWithOptionalUse) {
CalculatorGraphConfig config = mediapipe::ParseTextProtoOrDie<
CalculatorGraphConfig>(R"pb(
node {
calculator: 'FailOnUnavailableOptionalDisallowDefaultInitServiceCalculator'
}
)pb");
CalculatorGraph graph;
MP_ASSERT_OK(graph.Initialize(config));
MP_ASSERT_OK(graph.StartRun({}));
EXPECT_THAT(graph.WaitUntilIdle(),
StatusIs(absl::StatusCode::kInternal,
HasSubstr("Service is unavailable.")));
}
class UseDisallowDefaultInitServiceCalculator : public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
cc->UseService(kTestServiceDisallowDefaultInitialization);
return absl::OkStatus();
}
absl::Status Open(CalculatorContext* cc) final { return absl::OkStatus(); }
absl::Status Process(CalculatorContext* cc) final { return absl::OkStatus(); }
};
REGISTER_CALCULATOR(UseDisallowDefaultInitServiceCalculator);
// Documents and ensures current behavior for requesting
// "DisallowDefaultInitialization" services:
// - Service object is not created by default.
// - Graph run fails.
TEST(DisallowDefaultInitializationGraphServiceTest,
StartRunFailsMissingService) {
CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node { calculator: 'UseDisallowDefaultInitServiceCalculator' }
)pb");
CalculatorGraph graph;
MP_ASSERT_OK(graph.Initialize(config));
EXPECT_THAT(graph.StartRun({}),
StatusIs(absl::StatusCode::kInternal,
HasSubstr("was not provided and cannot be created")));
}
} // namespace
} // namespace mediapipe