#!/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__)