You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
19 KiB
560 lines
19 KiB
#!/usr/bin/env python3
|
|
# Copyright 2016 Google Inc. 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.
|
|
|
|
from absl.testing import parameterized
|
|
from fruit_test_common import *
|
|
|
|
COMMON_DEFINITIONS = '''
|
|
#include "test_common.h"
|
|
|
|
struct Annotation1 {};
|
|
struct Annotation2 {};
|
|
|
|
template <typename T>
|
|
using WithNoAnnot = T;
|
|
|
|
template <typename T>
|
|
using WithAnnot1 = fruit::Annotated<Annotation1, T>;
|
|
'''
|
|
|
|
class TestMultibindingsBindProvider(parameterized.TestCase):
|
|
@parameterized.parameters([
|
|
'X()',
|
|
'new X()',
|
|
])
|
|
def test_bind_multibinding_provider_success(self, ConstructX):
|
|
source = '''
|
|
struct X : public ConstructionTracker<X> {
|
|
INJECT(X()) = default;
|
|
};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider([](){return ConstructX;});
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
|
|
Assert(X::num_objects_constructed == 0);
|
|
Assert(injector.getMultibindings<X>().size() == 1);
|
|
Assert(X::num_objects_constructed == 1);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@parameterized.parameters([
|
|
'WithNoAnnot',
|
|
'WithAnnot1',
|
|
])
|
|
def test_bind_multibinding_provider_abstract_class_success(self, WithAnnot):
|
|
source = '''
|
|
struct I {
|
|
virtual int foo() = 0;
|
|
virtual ~I() = default;
|
|
};
|
|
|
|
struct X : public I {
|
|
int foo() override {
|
|
return 5;
|
|
}
|
|
};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
|
|
Assert(injector.getMultibindings<WithAnnot<I>>().size() == 1);
|
|
Assert(injector.getMultibindings<WithAnnot<I>>()[0]->foo() == 5);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@parameterized.parameters([
|
|
'WithNoAnnot',
|
|
'WithAnnot1',
|
|
])
|
|
def test_bind_multibinding_provider_abstract_class_with_no_virtual_destructor_error(self, WithAnnot):
|
|
source = '''
|
|
struct I {
|
|
virtual int foo() = 0;
|
|
};
|
|
|
|
struct X : public I {
|
|
int foo() override {
|
|
return 5;
|
|
}
|
|
};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'MultibindingProviderReturningPointerToAbstractClassWithNoVirtualDestructorError<I>',
|
|
r'registerMultibindingProvider\(\) was called with a lambda that returns a pointer to T, but T is an abstract class with no virtual destructor',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@multiple_parameters([
|
|
('X()', 'X'),
|
|
('new X()', 'X*'),
|
|
], [
|
|
'WithNoAnnot',
|
|
'WithAnnot1',
|
|
], [
|
|
'Y',
|
|
'const Y',
|
|
'Y*',
|
|
'const Y*',
|
|
'Y&',
|
|
'const Y&',
|
|
'std::shared_ptr<Y>',
|
|
'fruit::Provider<Y>',
|
|
'fruit::Provider<const Y>',
|
|
])
|
|
def test_bind_multibinding_provider_with_param_success(self, ConstructX, XPtr, WithAnnot, YVariant):
|
|
source = '''
|
|
struct Y {};
|
|
|
|
struct X : public ConstructionTracker<X> {};
|
|
|
|
fruit::Component<WithAnnot<Y>> getYComponent() {
|
|
return fruit::createComponent()
|
|
.registerConstructor<WithAnnot<Y>()>();
|
|
}
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.install(getYComponent)
|
|
.addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
|
|
Assert(X::num_objects_constructed == 0);
|
|
Assert(injector.getMultibindings<X>().size() == 1);
|
|
Assert(X::num_objects_constructed == 1);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@multiple_parameters([
|
|
('X()', 'X'),
|
|
('new X()', 'X*'),
|
|
], [
|
|
'WithNoAnnot',
|
|
'WithAnnot1',
|
|
], [
|
|
'Y',
|
|
'const Y',
|
|
'const Y*',
|
|
'const Y&',
|
|
'fruit::Provider<const Y>',
|
|
])
|
|
def test_bind_multibinding_provider_with_param_const_binding_success(self, ConstructX, XPtr, WithAnnot, YVariant):
|
|
source = '''
|
|
struct Y {};
|
|
|
|
struct X : public ConstructionTracker<X> {};
|
|
|
|
const Y y{};
|
|
|
|
fruit::Component<WithAnnot<const Y>> getYComponent() {
|
|
return fruit::createComponent()
|
|
.bindInstance<WithAnnot<Y>, Y>(y);
|
|
}
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.install(getYComponent)
|
|
.addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
|
|
Assert(X::num_objects_constructed == 0);
|
|
Assert(injector.getMultibindings<X>().size() == 1);
|
|
Assert(X::num_objects_constructed == 1);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@multiple_parameters([
|
|
('X()', 'X'),
|
|
('new X()', 'X*'),
|
|
], [
|
|
('WithNoAnnot', 'Y'),
|
|
('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
|
|
], [
|
|
'Y*',
|
|
'Y&',
|
|
'std::shared_ptr<Y>',
|
|
'fruit::Provider<Y>',
|
|
])
|
|
def test_bind_multibinding_provider_with_param_error_nonconst_param_required(self, ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
|
|
source = '''
|
|
struct Y {};
|
|
struct X {};
|
|
|
|
fruit::Component<WithAnnot<const Y>> getYComponent();
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.install(getYComponent)
|
|
.addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
|
|
'The type T was provided as constant, however one of the constructors/providers/factories in this component',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@multiple_parameters([
|
|
('X()', 'X'),
|
|
('new X()', 'X*'),
|
|
], [
|
|
('WithNoAnnot', 'Y'),
|
|
('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
|
|
], [
|
|
'Y*',
|
|
'Y&',
|
|
'std::shared_ptr<Y>',
|
|
'fruit::Provider<Y>',
|
|
])
|
|
def test_bind_multibinding_provider_with_param_error_nonconst_param_required_install_after(self, ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
|
|
source = '''
|
|
struct Y {};
|
|
struct X {};
|
|
|
|
fruit::Component<WithAnnot<const Y>> getYComponent();
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; })
|
|
.install(getYComponent);
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
|
|
'The type T was provided as constant, however one of the constructors/providers/factories in this component',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
def test_bind_multibinding_provider_requiring_nonconst_then_requiring_const_ok(self):
|
|
source = '''
|
|
struct X {};
|
|
struct Y {};
|
|
|
|
fruit::Component<> getRootComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider([](X&) { return Y(); })
|
|
.addMultibindingProvider([](const X&) { return Y(); })
|
|
.registerConstructor<X()>();
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getRootComponent);
|
|
injector.getMultibindings<Y>();
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
def test_bind_multibinding_provider_requiring_nonconst_then_requiring_const_declaring_const_requirement_error(self):
|
|
source = '''
|
|
struct X {};
|
|
struct Y {};
|
|
|
|
fruit::Component<fruit::Required<const X>> getRootComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider([](X&) { return Y(); })
|
|
.addMultibindingProvider([](const X&) { return Y(); });
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
|
|
'The type T was declared as a const Required type in the returned Component, however',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
def test_bind_multibinding_provider_requiring_const_then_requiring_nonconst_ok(self):
|
|
source = '''
|
|
struct X {};
|
|
struct Y {};
|
|
|
|
fruit::Component<> getRootComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider([](const X&) { return Y(); })
|
|
.addMultibindingProvider([](X&) { return Y(); })
|
|
.registerConstructor<X()>();
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getRootComponent);
|
|
injector.getMultibindings<Y>();
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
def test_bind_multibinding_provider_requiring_const_then_requiring_nonconst_declaring_const_requirement_error(self):
|
|
source = '''
|
|
struct X {};
|
|
struct Y {};
|
|
|
|
fruit::Component<fruit::Required<const X>> getRootComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider([](const X&) { return Y(); })
|
|
.addMultibindingProvider([](X&) { return Y(); });
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
|
|
'The type T was declared as a const Required type in the returned Component, however',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@multiple_parameters([
|
|
('X()', 'X'),
|
|
('new X()', 'X*'),
|
|
], [
|
|
('Y', 'Y', 'Y**', r'Y\*\*'),
|
|
('Y', 'Y', 'std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
|
|
('Y', 'const Y', 'Y**', r'Y\*\*'),
|
|
('Y', 'const Y', 'std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
|
|
('fruit::Annotated<Annotation1, Y>', 'fruit::Annotated<Annotation1, Y>', 'Y**', r'Y\*\*'),
|
|
('fruit::Annotated<Annotation1, Y>', 'fruit::Annotated<Annotation1, const Y>', 'Y**', r'Y\*\*'),
|
|
])
|
|
def test_bind_multibinding_provider_with_param_error_type_not_injectable(self, ConstructX, XPtr, YAnnot, ConstYAnnot, YVariant, YVariantRegex):
|
|
source = '''
|
|
struct Y {};
|
|
struct X {};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<XPtr(YVariant)>([](YVariant){ return ConstructX; });
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'NonInjectableTypeError<YVariantRegex>',
|
|
'The type T is not injectable.',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@parameterized.parameters([
|
|
('X()', 'X', 'X'),
|
|
('X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
|
|
('new X()', 'X', 'X*'),
|
|
('new X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'),
|
|
])
|
|
def test_bind_multibinding_provider_explicit_signature_success(self, ConstructX, XAnnot, XPtrAnnot):
|
|
source = '''
|
|
struct X : public ConstructionTracker<X> {
|
|
INJECT(X()) = default;
|
|
|
|
static bool constructed;
|
|
};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<XPtrAnnot()>([](){return ConstructX;});
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
|
|
Assert(X::num_objects_constructed == 0);
|
|
Assert(injector.getMultibindings<XAnnot>().size() == 1);
|
|
Assert(X::num_objects_constructed == 1);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@parameterized.parameters([
|
|
('X()', 'X', 'X'),
|
|
('X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
|
|
('new X()', 'X', 'X*'),
|
|
('new X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'),
|
|
])
|
|
def test_bind_multibinding_provider_explicit_signature_with_normalized_component_success(self, ConstructX, XAnnot, XPtrAnnot):
|
|
source = '''
|
|
struct X : public ConstructionTracker<X> {
|
|
INJECT(X()) = default;
|
|
|
|
static bool constructed;
|
|
};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<XPtrAnnot()>([](){return ConstructX;});
|
|
}
|
|
|
|
fruit::Component<> getEmptyComponent() {
|
|
return fruit::createComponent();
|
|
}
|
|
|
|
int main() {
|
|
fruit::NormalizedComponent<> normalizedComponent(getComponent);
|
|
fruit::Injector<> injector(normalizedComponent, getEmptyComponent);
|
|
|
|
Assert(X::num_objects_constructed == 0);
|
|
Assert(injector.getMultibindings<XAnnot>().size() == 1);
|
|
Assert(X::num_objects_constructed == 1);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@parameterized.parameters([
|
|
('X', 'X*', 'int'),
|
|
('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', 'fruit::Annotated<Annotation2, int>'),
|
|
])
|
|
def test_multiple_providers(self, XAnnot, XPtrAnnot, intAnnot):
|
|
source = '''
|
|
struct X {};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.registerProvider<intAnnot()>([](){return 42;})
|
|
.addMultibindingProvider<XAnnot(intAnnot)>([](int){return X();})
|
|
.addMultibindingProvider<XPtrAnnot(intAnnot)>([](int){return new X();});
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
|
|
std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
|
|
Assert(multibindings.size() == 2);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@multiple_parameters([
|
|
'X()',
|
|
'new X()',
|
|
], [
|
|
'X',
|
|
'fruit::Annotated<Annotation1, X>',
|
|
])
|
|
def test_bind_multibinding_provider_malformed_signature(self, ConstructX, XAnnot):
|
|
source = '''
|
|
struct X {};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<XAnnot>([](){return ConstructX;});
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'NotASignatureError<XAnnot>',
|
|
'CandidateSignature was specified as parameter, but it.s not a signature.',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@multiple_parameters([
|
|
'X(n)',
|
|
'new X(n)',
|
|
], [
|
|
'X',
|
|
'fruit::Annotated<Annotation1, X>',
|
|
])
|
|
def test_bind_multibinding_provider_lambda_with_captures_error(self, ConstructX, XAnnot):
|
|
source = '''
|
|
struct X {
|
|
X(int) {}
|
|
};
|
|
|
|
fruit::Component<> getComponent() {
|
|
int n = 3;
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<XAnnot()>([=]{return ConstructX;});
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'FunctorUsedAsProviderError<.*>',
|
|
'A stateful lambda or a non-lambda functor was used as provider',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
# TODO: should XPtrAnnot be just XAnnot in the signature?
|
|
# Make sure the behavior here is consistent with registerProvider() and registerFactory().
|
|
@parameterized.parameters([
|
|
('X', 'X*', '(struct )?X'),
|
|
('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
|
|
])
|
|
def test_provider_returns_nullptr_error(self, XAnnot, XPtrAnnot, XAnnotRegex):
|
|
source = '''
|
|
struct X {};
|
|
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibindingProvider<XPtrAnnot()>([](){return (X*)nullptr;});
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
injector.getMultibindings<XAnnot>();
|
|
}
|
|
'''
|
|
expect_runtime_error(
|
|
'Fatal injection error: attempting to get an instance for the type XAnnotRegex but the provider returned nullptr',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
if __name__ == '__main__':
|
|
absltest.main()
|