1 #!/usr/bin/env python3 2 # Copyright 2016 Google Inc. All Rights Reserved. 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS-IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 import pytest 16 17 from fruit_test_common import * 18 19 COMMON_DEFINITIONS = ''' 20 #include "test_common.h" 21 22 struct X; 23 24 struct Annotation1 {}; 25 using XAnnot = fruit::Annotated<Annotation1, X>; 26 27 struct Annotation2 {}; 28 ''' 29 30 @pytest.mark.parametrize('XVariant,XVariantRegexp', [ 31 ('X*', 'X\*'), 32 ('const X*', 'const X\*'), 33 ('X&', 'X&'), 34 ('const X&', 'const X&'), 35 ('std::shared_ptr<X>', 'std::shared_ptr<X>'), 36 ]) 37 def test_error_non_class_type_parameter(XVariant, XVariantRegexp): 38 source = ''' 39 struct X {}; 40 41 fruit::Provider<XVariant> provider; 42 ''' 43 expect_compile_error( 44 'NonClassTypeError<XVariantRegexp,X>', 45 'A non-class type T was specified. Use C instead', 46 COMMON_DEFINITIONS, 47 source, 48 locals()) 49 50 def test_error_annotated_type_parameter(): 51 source = ''' 52 struct X {}; 53 54 fruit::Provider<XAnnot> provider; 55 ''' 56 expect_compile_error( 57 'AnnotatedTypeError<fruit::Annotated<Annotation1,X>,X>', 58 'An annotated type was specified where a non-annotated type was expected.', 59 COMMON_DEFINITIONS, 60 source) 61 62 @pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [ 63 ('X', 'fruit::Provider<X>', 'X', 'X'), 64 ('X', 'fruit::Provider<X>', 'X', 'const X&'), 65 ('X', 'fruit::Provider<X>', 'X', 'const X*'), 66 ('X', 'fruit::Provider<X>', 'X', 'X&'), 67 ('X', 'fruit::Provider<X>', 'X', 'X*'), 68 ('X', 'fruit::Provider<X>', 'X', 'std::shared_ptr<X>'), 69 ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<X>'), 70 ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<const X>'), 71 ('X', 'fruit::Provider<const X>', 'const X', 'const X&'), 72 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<X>>', 'X', 'const X&'), 73 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'), 74 ]) 75 def test_provider_get_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam): 76 source = ''' 77 struct X { 78 using Inject = X(); 79 }; 80 81 fruit::Component<XBindingInInjector> getComponent() { 82 return fruit::createComponent(); 83 } 84 85 int main() { 86 fruit::Injector<XBindingInInjector> injector(getComponent); 87 fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>(); 88 89 XProviderGetParam x = provider.get<XProviderGetParam>(); 90 (void)x; 91 } 92 ''' 93 expect_success( 94 COMMON_DEFINITIONS, 95 source, 96 locals()) 97 98 @pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [ 99 ('const X', 'fruit::Provider<const X>', 'const X', 'X'), 100 ('const X', 'fruit::Provider<const X>', 'const X', 'const X&'), 101 ('const X', 'fruit::Provider<const X>', 'const X', 'const X*'), 102 ('const X', 'fruit::Provider<const X>', 'const X', 'fruit::Provider<const X>'), 103 ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'), 104 ]) 105 def test_provider_get_const_binding_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam): 106 XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '') 107 source = ''' 108 struct X {}; 109 110 const X x{}; 111 112 fruit::Component<XBindingInInjector> getComponent() { 113 return fruit::createComponent() 114 .bindInstance<XBindingInInjectorWithoutConst, X>(x); 115 } 116 117 int main() { 118 fruit::Injector<XBindingInInjector> injector(getComponent); 119 fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>(); 120 121 XProviderGetParam x = provider.get<XProviderGetParam>(); 122 (void)x; 123 } 124 ''' 125 expect_success( 126 COMMON_DEFINITIONS, 127 source, 128 locals()) 129 130 def test_provider_get_during_injection_ok(): 131 source = ''' 132 struct X { 133 INJECT(X()) = default; 134 void foo() { 135 } 136 }; 137 138 struct Y { 139 X x; 140 INJECT(Y(fruit::Provider<X> xProvider)) 141 : x(xProvider.get<X>()) { 142 } 143 144 void foo() { 145 x.foo(); 146 } 147 }; 148 149 struct Z { 150 Y y; 151 INJECT(Z(fruit::Provider<Y> yProvider)) 152 : y(yProvider.get<Y>()) { 153 } 154 155 void foo() { 156 y.foo(); 157 } 158 }; 159 160 fruit::Component<Z> getZComponent() { 161 return fruit::createComponent(); 162 } 163 164 int main() { 165 fruit::Injector<Z> injector(getZComponent); 166 fruit::Provider<Z> provider(injector); 167 // During provider.get<Z>(), yProvider.get() is called, and during that xProvider.get() 168 // is called. 169 Z z = provider.get<Z>(); 170 z.foo(); 171 } 172 ''' 173 expect_success( 174 COMMON_DEFINITIONS, 175 source) 176 177 def test_provider_get_error_type_not_provided(): 178 source = ''' 179 struct X {}; 180 struct Y {}; 181 182 void f(fruit::Provider<X> provider) { 183 provider.get<Y>(); 184 } 185 ''' 186 expect_compile_error( 187 'TypeNotProvidedError<Y>', 188 'Trying to get an instance of T, but it is not provided by this Provider/Injector.', 189 COMMON_DEFINITIONS, 190 source) 191 192 @pytest.mark.parametrize('XVariant,XVariantRegex', [ 193 ('X**', r'X\*\*'), 194 ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'), 195 ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'), 196 ('X* const', r'X\* const'), 197 ('const X* const', r'const X\* const'), 198 ('std::nullptr_t', r'(std::)?nullptr(_t)?'), 199 ('X*&', r'X\*&'), 200 ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'), 201 ('void', r'void'), 202 ('fruit::Annotated<Annotation1, fruit::Annotated<Annotation1, X>>', r'fruit::Annotated<Annotation1, X>'), 203 ]) 204 def test_provider_get_error_type_not_injectable(XVariant,XVariantRegex): 205 source = ''' 206 struct X {}; 207 208 void f(fruit::Provider<X> provider) { 209 provider.get<XVariant>(); 210 } 211 ''' 212 expect_compile_error( 213 'NonInjectableTypeError<XVariantRegex>', 214 'The type T is not injectable', 215 COMMON_DEFINITIONS, 216 source, 217 locals()) 218 219 @pytest.mark.parametrize('XProviderGetParam,XProviderGetParamRegex', [ 220 ('X&', 'X&'), 221 ('X*', 'X\*'), 222 ('std::shared_ptr<X>', 'std::shared_ptr<X>'), 223 ('fruit::Provider<X>', 'fruit::Provider<X>'), 224 ]) 225 def test_const_provider_get_does_not_allow_injecting_nonconst_variants(XProviderGetParam, XProviderGetParamRegex): 226 source = ''' 227 void f(fruit::Provider<const X> provider) { 228 provider.get<XProviderGetParam>(); 229 } 230 ''' 231 expect_compile_error( 232 'TypeProvidedAsConstOnlyError<XProviderGetParamRegex>', 233 'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector', 234 COMMON_DEFINITIONS, 235 source, 236 locals()) 237 238 @pytest.mark.parametrize('Y_PROVIDER_ANNOT', [ 239 ('fruit::Provider<Y>'), 240 ('ANNOTATED(Annotation1, fruit::Provider<Y>)'), 241 ]) 242 def test_lazy_injection_with_annotations(Y_PROVIDER_ANNOT): 243 source = ''' 244 struct Y : public ConstructionTracker<Y> { 245 using Inject = Y(); 246 }; 247 248 struct X : public ConstructionTracker<X> { 249 INJECT(X(Y_PROVIDER_ANNOT provider)) : provider(provider) { 250 } 251 252 void run() { 253 Y* y(provider); 254 (void) y; 255 } 256 257 fruit::Provider<Y> provider; 258 }; 259 260 fruit::Component<X> getComponent() { 261 return fruit::createComponent(); 262 } 263 264 fruit::Component<> getEmptyComponent() { 265 return fruit::createComponent(); 266 } 267 268 int main() { 269 fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent); 270 fruit::Injector<X> injector(normalizedComponent, getComponent); 271 272 Assert(X::num_objects_constructed == 0); 273 Assert(Y::num_objects_constructed == 0); 274 275 X* x(injector); 276 277 Assert(X::num_objects_constructed == 1); 278 Assert(Y::num_objects_constructed == 0); 279 280 x->run(); 281 282 Assert(X::num_objects_constructed == 1); 283 Assert(Y::num_objects_constructed == 1); 284 } 285 ''' 286 expect_success( 287 COMMON_DEFINITIONS, 288 source, 289 locals()) 290 291 if __name__== '__main__': 292 main(__file__) 293