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.
580 lines
20 KiB
580 lines
20 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 Listener;
|
|
|
|
struct X {};
|
|
|
|
struct Annotation {};
|
|
struct Annotation1 {};
|
|
using ListenerAnnot = fruit::Annotated<Annotation, Listener>;
|
|
'''
|
|
|
|
class TestMultibindingsMisc(parameterized.TestCase):
|
|
def test_get_none(self):
|
|
source = '''
|
|
fruit::Component<> getComponent() {
|
|
return fruit::createComponent();
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getComponent);
|
|
|
|
std::vector<X*> multibindings = injector.getMultibindings<X>();
|
|
(void) multibindings;
|
|
Assert(multibindings.empty());
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source)
|
|
|
|
def test_multiple_various_kinds(self):
|
|
source = '''
|
|
static int numNotificationsToListener1 = 0;
|
|
static int numNotificationsToListener2 = 0;
|
|
static int numNotificationsToListener3 = 0;
|
|
|
|
struct Listener {
|
|
public:
|
|
virtual ~Listener() = default;
|
|
|
|
virtual void notify() = 0;
|
|
};
|
|
|
|
struct Listener1 : public Listener {
|
|
public:
|
|
INJECT(Listener1()) = default;
|
|
|
|
virtual ~Listener1() = default;
|
|
|
|
void notify() override {
|
|
++numNotificationsToListener1;
|
|
}
|
|
};
|
|
|
|
struct Writer {
|
|
public:
|
|
virtual void write(std::string s) = 0;
|
|
};
|
|
|
|
struct StdoutWriter : public Writer {
|
|
public:
|
|
INJECT(StdoutWriter()) = default;
|
|
|
|
void write(std::string s) override {
|
|
std::cout << s << std::endl;
|
|
}
|
|
};
|
|
|
|
struct Listener2 : public Listener {
|
|
private:
|
|
Writer* writer;
|
|
|
|
public:
|
|
INJECT(Listener2(Writer* writer))
|
|
: writer(writer) {
|
|
}
|
|
|
|
virtual ~Listener2() = default;
|
|
|
|
void notify() override {
|
|
(void) writer;
|
|
++numNotificationsToListener2;
|
|
}
|
|
};
|
|
|
|
struct Listener3 : public Listener {
|
|
private:
|
|
Writer* writer;
|
|
|
|
public:
|
|
INJECT(Listener3(Writer* writer))
|
|
: writer(writer) {
|
|
}
|
|
|
|
virtual ~Listener3() = default;
|
|
|
|
void notify() override {
|
|
(void) writer;
|
|
++numNotificationsToListener3;
|
|
}
|
|
};
|
|
|
|
fruit::Component<> getListenersComponent() {
|
|
return fruit::createComponent()
|
|
.bind<Writer, StdoutWriter>()
|
|
// Note: this is just to exercise the other method, but in real code you should split this in
|
|
// an addMultibinding<Listener, Listener1> and a registerProvider with the lambda.
|
|
.addMultibindingProvider([]() {
|
|
Listener1* listener1 = new Listener1();
|
|
return static_cast<Listener*>(listener1);
|
|
})
|
|
.addMultibinding<Listener, Listener2>()
|
|
.addMultibinding<ListenerAnnot, Listener3>();
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getListenersComponent);
|
|
std::vector<Listener*> listeners = injector.getMultibindings<Listener>();
|
|
for (Listener* listener : listeners) {
|
|
listener->notify();
|
|
}
|
|
|
|
std::vector<Listener*> listeners2 = injector.getMultibindings<Listener>();
|
|
Assert(listeners == listeners2);
|
|
|
|
if (numNotificationsToListener1 != 1 || numNotificationsToListener2 != 1
|
|
|| numNotificationsToListener3 != 0) {
|
|
abort();
|
|
}
|
|
|
|
std::vector<Listener*> listenersWithAnnotation = injector.getMultibindings<ListenerAnnot>();
|
|
for (Listener* listener : listenersWithAnnotation) {
|
|
listener->notify();
|
|
}
|
|
|
|
if (numNotificationsToListener1 != 1 || numNotificationsToListener2 != 1
|
|
|| numNotificationsToListener3 != 1) {
|
|
abort();
|
|
}
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source)
|
|
|
|
def test_order(self):
|
|
source = '''
|
|
std::vector<int> numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
|
|
// *
|
|
// |-- 0
|
|
// |-- A
|
|
// | |-- 1
|
|
// | |-- B
|
|
// | | |-- 2
|
|
// | | `-- 3
|
|
// | |-- 4
|
|
// | |-- C
|
|
// | | |-- 5
|
|
// | | |-- 6
|
|
// | | |-- D
|
|
// | | | |-- 7
|
|
// | | | |-- E
|
|
// | | | | |-- 8
|
|
// | | | | `-- 9
|
|
// | | | `-- 10
|
|
// | | |-- 11
|
|
// | | |-- F
|
|
// | | | |-- 12
|
|
// | | | `-- 13
|
|
// | | `-- 14
|
|
// | |-- 15
|
|
// | |-- C (won't be expanded)
|
|
// | `-- 16
|
|
// |-- 17
|
|
// |-- C (won't be expanded)
|
|
// `-- 18
|
|
|
|
fruit::Component<> getRootComponent();
|
|
fruit::Component<> getComponentA();
|
|
fruit::Component<> getComponentB();
|
|
fruit::Component<> getComponentC();
|
|
fruit::Component<> getComponentD();
|
|
fruit::Component<> getComponentE();
|
|
fruit::Component<> getComponentF();
|
|
|
|
fruit::Component<> getRootComponent() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[0])
|
|
.install(getComponentA)
|
|
.addInstanceMultibinding(numbers[17])
|
|
.install(getComponentC)
|
|
.addInstanceMultibinding(numbers[18]);
|
|
}
|
|
|
|
fruit::Component<> getComponentA() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[1])
|
|
.install(getComponentB)
|
|
.addInstanceMultibinding(numbers[4])
|
|
.install(getComponentC)
|
|
.addInstanceMultibinding(numbers[15])
|
|
.install(getComponentC)
|
|
.addInstanceMultibinding(numbers[16]);
|
|
}
|
|
|
|
fruit::Component<> getComponentB() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[2])
|
|
.addInstanceMultibinding(numbers[3]);
|
|
}
|
|
|
|
fruit::Component<> getComponentC() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[5])
|
|
.addInstanceMultibinding(numbers[6])
|
|
.install(getComponentD)
|
|
.addInstanceMultibinding(numbers[11])
|
|
.install(getComponentF)
|
|
.addInstanceMultibinding(numbers[14]);
|
|
}
|
|
|
|
fruit::Component<> getComponentD() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[7])
|
|
.install(getComponentE)
|
|
.addInstanceMultibinding(numbers[10]);
|
|
}
|
|
|
|
fruit::Component<> getComponentE() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[8])
|
|
.addInstanceMultibinding(numbers[9]);
|
|
}
|
|
|
|
fruit::Component<> getComponentF() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[12])
|
|
.addInstanceMultibinding(numbers[13]);
|
|
}
|
|
|
|
int main() {
|
|
fruit::Injector<> injector(getRootComponent);
|
|
std::vector<int*> result_ptrs = injector.getMultibindings<int>();
|
|
std::vector<int> results;
|
|
std::cout << "Results: ";
|
|
for (int* result : result_ptrs) {
|
|
std::cout << *result << ", ";
|
|
results.push_back(*result);
|
|
}
|
|
std::cout << std::endl;
|
|
Assert(results == numbers);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source)
|
|
|
|
|
|
def test_order_with_normalized_component(self):
|
|
source = '''
|
|
std::vector<int> numbers = {
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
|
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37};
|
|
// root1
|
|
// |-- 0
|
|
// |-- A
|
|
// | |-- 1
|
|
// | |-- B
|
|
// | | |-- 2
|
|
// | | `-- 3
|
|
// | |-- 4
|
|
// | |-- C
|
|
// | | |-- 5
|
|
// | | |-- 6
|
|
// | | |-- D
|
|
// | | | |-- 7
|
|
// | | | |-- E
|
|
// | | | | |-- 8
|
|
// | | | | `-- 9
|
|
// | | | `-- 10
|
|
// | | |-- 11
|
|
// | | |-- F
|
|
// | | | |-- 12
|
|
// | | | `-- 13
|
|
// | | `-- 14
|
|
// | |-- 15
|
|
// | |-- C (won't be expanded)
|
|
// | `-- 16
|
|
// |-- 17
|
|
// |-- C (won't be expanded)
|
|
// `-- 18
|
|
|
|
// root2
|
|
// |-- 19
|
|
// |-- A2
|
|
// | |-- 20
|
|
// | |-- B2
|
|
// | | |-- 21
|
|
// | | `-- 22
|
|
// | |-- 23
|
|
// | |-- C2
|
|
// | | |-- 24
|
|
// | | |-- 25
|
|
// | | |-- D2
|
|
// | | | |-- 26
|
|
// | | | |-- E2
|
|
// | | | | |-- 27
|
|
// | | | | `-- 28
|
|
// | | | `-- 29
|
|
// | | |-- 30
|
|
// | | |-- F2
|
|
// | | | |-- 31
|
|
// | | | `-- 32
|
|
// | | `-- 33
|
|
// | |-- 34
|
|
// | |-- C2 (won't be expanded)
|
|
// | `-- 35
|
|
// |-- 36
|
|
// |-- C2 (won't be expanded)
|
|
// `-- 37
|
|
|
|
fruit::Component<> getRootComponent();
|
|
fruit::Component<> getComponentA();
|
|
fruit::Component<> getComponentB();
|
|
fruit::Component<> getComponentC();
|
|
fruit::Component<> getComponentD();
|
|
fruit::Component<> getComponentE();
|
|
fruit::Component<> getComponentF();
|
|
|
|
fruit::Component<> getRootComponent2();
|
|
fruit::Component<> getComponentA2();
|
|
fruit::Component<> getComponentB2();
|
|
fruit::Component<> getComponentC2();
|
|
fruit::Component<> getComponentD2();
|
|
fruit::Component<> getComponentE2();
|
|
fruit::Component<> getComponentF2();
|
|
|
|
fruit::Component<> getRootComponent() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[0])
|
|
.install(getComponentA)
|
|
.addInstanceMultibinding(numbers[17])
|
|
.install(getComponentC)
|
|
.addInstanceMultibinding(numbers[18]);
|
|
}
|
|
|
|
fruit::Component<> getComponentA() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[1])
|
|
.install(getComponentB)
|
|
.addInstanceMultibinding(numbers[4])
|
|
.install(getComponentC)
|
|
.addInstanceMultibinding(numbers[15])
|
|
.install(getComponentC)
|
|
.addInstanceMultibinding(numbers[16]);
|
|
}
|
|
|
|
fruit::Component<> getComponentB() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[2])
|
|
.addInstanceMultibinding(numbers[3]);
|
|
}
|
|
|
|
fruit::Component<> getComponentC() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[5])
|
|
.addInstanceMultibinding(numbers[6])
|
|
.install(getComponentD)
|
|
.addInstanceMultibinding(numbers[11])
|
|
.install(getComponentF)
|
|
.addInstanceMultibinding(numbers[14]);
|
|
}
|
|
|
|
fruit::Component<> getComponentD() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[7])
|
|
.install(getComponentE)
|
|
.addInstanceMultibinding(numbers[10]);
|
|
}
|
|
|
|
fruit::Component<> getComponentE() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[8])
|
|
.addInstanceMultibinding(numbers[9]);
|
|
}
|
|
|
|
fruit::Component<> getComponentF() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[12])
|
|
.addInstanceMultibinding(numbers[13]);
|
|
}
|
|
|
|
fruit::Component<> getRootComponent2() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[19])
|
|
.install(getComponentA2)
|
|
.addInstanceMultibinding(numbers[36])
|
|
.install(getComponentC2)
|
|
.addInstanceMultibinding(numbers[37]);
|
|
}
|
|
|
|
fruit::Component<> getComponentA2() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[20])
|
|
.install(getComponentB2)
|
|
.addInstanceMultibinding(numbers[23])
|
|
.install(getComponentC2)
|
|
.addInstanceMultibinding(numbers[34])
|
|
.install(getComponentC2)
|
|
.addInstanceMultibinding(numbers[35]);
|
|
}
|
|
|
|
fruit::Component<> getComponentB2() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[21])
|
|
.addInstanceMultibinding(numbers[22]);
|
|
}
|
|
|
|
fruit::Component<> getComponentC2() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[24])
|
|
.addInstanceMultibinding(numbers[25])
|
|
.install(getComponentD2)
|
|
.addInstanceMultibinding(numbers[30])
|
|
.install(getComponentF2)
|
|
.addInstanceMultibinding(numbers[33]);
|
|
}
|
|
|
|
fruit::Component<> getComponentD2() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[26])
|
|
.install(getComponentE2)
|
|
.addInstanceMultibinding(numbers[29]);
|
|
}
|
|
|
|
fruit::Component<> getComponentE2() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[27])
|
|
.addInstanceMultibinding(numbers[28]);
|
|
}
|
|
|
|
fruit::Component<> getComponentF2() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[31])
|
|
.addInstanceMultibinding(numbers[32]);
|
|
}
|
|
|
|
int main() {
|
|
fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
|
|
fruit::Injector<> injector(normalizedComponent, getRootComponent2);
|
|
std::vector<int*> result_ptrs = injector.getMultibindings<int>();
|
|
std::vector<int> results;
|
|
std::cout << "Results: ";
|
|
for (int* result : result_ptrs) {
|
|
std::cout << *result << ", ";
|
|
results.push_back(*result);
|
|
}
|
|
std::cout << std::endl;
|
|
Assert(results == numbers);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source)
|
|
|
|
def test_with_normalized_component_lazy_components_not_deduped_across(self):
|
|
source = '''
|
|
std::vector<int> numbers = {0, 1, 2, 3, 4};
|
|
|
|
// *
|
|
// |-- 0
|
|
// |-- A (lazy)
|
|
// | |-- 1
|
|
// | `-- 2
|
|
// |-- 3
|
|
// |-- A (lazy, won't be expanded)
|
|
// `-- 4
|
|
|
|
fruit::Component<> getRootComponent();
|
|
fruit::Component<> getComponentA();
|
|
|
|
fruit::Component<> getRootComponent() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[0])
|
|
.install(getComponentA)
|
|
.addInstanceMultibinding(numbers[3])
|
|
.install(getComponentA)
|
|
.addInstanceMultibinding(numbers[4]);
|
|
}
|
|
|
|
fruit::Component<> getComponentA() {
|
|
return fruit::createComponent()
|
|
.addInstanceMultibinding(numbers[1])
|
|
.addInstanceMultibinding(numbers[2]);
|
|
}
|
|
|
|
int main() {
|
|
fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
|
|
fruit::Injector<> injector(normalizedComponent, getRootComponent);
|
|
std::vector<int*> result_ptrs = injector.getMultibindings<int>();
|
|
std::vector<int> results;
|
|
std::cout << "Results: ";
|
|
for (int* result : result_ptrs) {
|
|
std::cout << *result << ", ";
|
|
results.push_back(*result);
|
|
}
|
|
std::cout << std::endl;
|
|
std::vector<int> expected_numbers = {0, 1, 2, 3, 4};
|
|
Assert(results == expected_numbers);
|
|
}
|
|
'''
|
|
expect_success(
|
|
COMMON_DEFINITIONS,
|
|
source)
|
|
|
|
@parameterized.parameters([
|
|
('const X', r'const X'),
|
|
('X*', r'X\*'),
|
|
('const X*', r'const X\*'),
|
|
('std::shared_ptr<X>', r'std::shared_ptr<X>'),
|
|
('fruit::Annotated<Annotation1, const X>', r'const X'),
|
|
('fruit::Annotated<Annotation1, X*>', r'X\*'),
|
|
('fruit::Annotated<Annotation1, const X*>', r'const X\*'),
|
|
('fruit::Annotated<Annotation1, std::shared_ptr<X>>', r'std::shared_ptr<X>'),
|
|
])
|
|
def test_multibindings_get_error_non_class_type(self, XVariantAnnot, XVariantRegexp):
|
|
source = '''
|
|
void f(fruit::Injector<> injector) {
|
|
injector.getMultibindings<XVariantAnnot>();
|
|
}
|
|
'''
|
|
expect_compile_error(
|
|
'NonClassTypeError<XVariantRegexp,X>',
|
|
'A non-class type T was specified. Use C instead.',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
@parameterized.parameters([
|
|
('X&', 'X&'),
|
|
('const X&', 'const X&'),
|
|
('fruit::Annotated<Annotation1, X&>', 'X&'),
|
|
('fruit::Annotated<Annotation1, const X&>', 'const X&'),
|
|
])
|
|
def test_multibindings_get_error_reference_type(self, XVariantAnnot, XVariantRegexp):
|
|
source = '''
|
|
void f(fruit::Injector<> injector) {
|
|
injector.getMultibindings<XVariantAnnot>();
|
|
}
|
|
'''
|
|
expect_generic_compile_error(
|
|
'declared as a pointer to a reference of type'
|
|
'|forming pointer to reference type'
|
|
'|fruit::Injector<.*>::getMultibindings.: no matching overloaded function found',
|
|
COMMON_DEFINITIONS,
|
|
source,
|
|
locals())
|
|
|
|
if __name__ == '__main__':
|
|
absltest.main()
|