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.
577 lines
18 KiB
577 lines
18 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 fruit_test_common import * |
|
|
|
COMMON_DEFINITIONS = ''' |
|
#include "test_common.h" |
|
|
|
struct Listener; |
|
|
|
struct X {}; |
|
|
|
struct Annotation {}; |
|
struct Annotation1 {}; |
|
using ListenerAnnot = fruit::Annotated<Annotation, Listener>; |
|
''' |
|
|
|
def test_get_none(): |
|
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(): |
|
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(): |
|
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(): |
|
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(): |
|
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) |
|
|
|
@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [ |
|
('const X', 'const X'), |
|
('X*', 'X\*'), |
|
('const X*', 'const X\*'), |
|
('std::shared_ptr<X>', 'std::shared_ptr<X>'), |
|
('fruit::Annotated<Annotation1, const X>', 'const X'), |
|
('fruit::Annotated<Annotation1, X*>', 'X\*'), |
|
('fruit::Annotated<Annotation1, const X*>', 'const X\*'), |
|
('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'), |
|
]) |
|
def test_multibindings_get_error_non_class_type(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()) |
|
|
|
@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [ |
|
('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(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__': |
|
main(__file__)
|
|
|