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 Annotation1 {}; 23 struct Annotation2 {}; 24 ''' 25 26 @pytest.mark.parametrize('XAnnot,MaybeConstXAnnot,XConstRefAnnot,YAnnot', [ 27 ('X', 'X', 'const X&', 'Y'), 28 ('X', 'const X', 'const X&', 'Y'), 29 ('X', 'X', 'const X&', 'fruit::Annotated<Annotation1, Y>'), 30 ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'), 31 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'), 32 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'), 33 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'), 34 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'), 35 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'), 36 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'), 37 ]) 38 def test_bind_interface(XAnnot, MaybeConstXAnnot, XConstRefAnnot, YAnnot): 39 source = ''' 40 struct X { 41 virtual void f() const = 0; 42 }; 43 44 struct Y : public X { 45 INJECT(Y()) = default; 46 47 void f() const override { 48 } 49 }; 50 51 fruit::Component<MaybeConstXAnnot> getComponent() { 52 return fruit::createComponent() 53 .bind<XAnnot, YAnnot>(); 54 } 55 56 int main() { 57 fruit::Injector<MaybeConstXAnnot> injector(getComponent); 58 const X& x = injector.get<XConstRefAnnot>(); 59 x.f(); 60 } 61 ''' 62 expect_success( 63 COMMON_DEFINITIONS, 64 source, 65 locals()) 66 67 @pytest.mark.parametrize('XAnnot,ConstXAnnot,XConstRefAnnot,YAnnot', [ 68 ('X', 'const X', 'const X&', 'Y'), 69 ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'), 70 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'), 71 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'), 72 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'), 73 ]) 74 def test_bind_interface_to_constant(XAnnot, ConstXAnnot, XConstRefAnnot, YAnnot): 75 source = ''' 76 struct X { 77 virtual void f() const = 0; 78 }; 79 80 struct Y : public X { 81 void f() const override { 82 } 83 }; 84 85 const Y y{}; 86 87 fruit::Component<ConstXAnnot> getComponent() { 88 return fruit::createComponent() 89 .bindInstance<YAnnot, Y>(y) 90 .bind<XAnnot, YAnnot>(); 91 } 92 93 int main() { 94 fruit::Injector<ConstXAnnot> injector(getComponent); 95 const X& x = injector.get<XConstRefAnnot>(); 96 x.f(); 97 } 98 ''' 99 expect_success( 100 COMMON_DEFINITIONS, 101 source, 102 locals()) 103 104 @pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [ 105 ('X', 'X&', 'Y'), 106 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'), 107 ]) 108 def test_bind_interface_target_bound_in_other_component(XAnnot, XRefAnnot, YAnnot): 109 source = ''' 110 struct X { 111 virtual void f() = 0; 112 }; 113 114 struct Y : public X { 115 void f() override { 116 } 117 }; 118 119 fruit::Component<fruit::Required<YAnnot>, XAnnot> getComponent() { 120 return fruit::createComponent() 121 .bind<XAnnot, YAnnot>(); 122 } 123 124 fruit::Component<XAnnot> getRootComponent() { 125 return fruit::createComponent() 126 .registerConstructor<YAnnot()>() 127 .install(getComponent); 128 } 129 130 int main() { 131 fruit::Injector<XAnnot> injector(getRootComponent); 132 X& x = injector.get<XRefAnnot>(); 133 x.f(); 134 } 135 ''' 136 expect_success( 137 COMMON_DEFINITIONS, 138 source, 139 locals()) 140 141 @pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [ 142 ('X', 'X&', 'Y'), 143 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'), 144 ]) 145 def test_bind_nonconst_interface_requires_nonconst_target(XAnnot, XRefAnnot, YAnnot): 146 source = ''' 147 struct X { 148 virtual void f() = 0; 149 }; 150 151 struct Y : public X { 152 void f() override { 153 } 154 }; 155 156 fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() { 157 return fruit::createComponent() 158 .bind<XAnnot, YAnnot>(); 159 } 160 ''' 161 expect_compile_error( 162 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>', 163 'The type T was declared as a const Required type in the returned Component, however a non-const binding', 164 COMMON_DEFINITIONS, 165 source, 166 locals()) 167 168 @pytest.mark.parametrize('XAnnot,YAnnot', [ 169 ('X', 'Y'), 170 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'), 171 ]) 172 def test_bind_interface_to_constant_nonconst_required_const_bound_error(XAnnot, YAnnot): 173 source = ''' 174 struct X { 175 virtual void f() const = 0; 176 }; 177 178 struct Y : public X { 179 void f() const override { 180 } 181 }; 182 183 const Y y{}; 184 185 fruit::Component<XAnnot> getComponent() { 186 return fruit::createComponent() 187 .bindInstance<YAnnot, Y>(y) 188 .bind<XAnnot, YAnnot>(); 189 } 190 ''' 191 expect_compile_error( 192 'NonConstBindingRequiredButConstBindingProvidedError<YAnnot>', 193 'The type T was provided as constant, however one of the constructors/providers/factories in this component', 194 COMMON_DEFINITIONS, 195 source, 196 locals()) 197 198 @pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [ 199 ('X', 'X&', 'Y'), 200 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'), 201 ]) 202 def test_bind_nonconst_interface_requires_nonconst_target_abstract(XAnnot, XRefAnnot, YAnnot): 203 source = ''' 204 struct X { 205 virtual void f() = 0; 206 }; 207 208 struct Y : public X {}; 209 210 fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() { 211 return fruit::createComponent() 212 .bind<XAnnot, YAnnot>(); 213 } 214 ''' 215 expect_compile_error( 216 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>', 217 'The type T was declared as a const Required type in the returned Component, however a non-const binding', 218 COMMON_DEFINITIONS, 219 source, 220 locals()) 221 222 @pytest.mark.parametrize('XAnnot,intAnnot', [ 223 ('X', 'int'), 224 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, int>'), 225 ]) 226 def test_error_not_base(XAnnot, intAnnot): 227 source = ''' 228 struct X {}; 229 230 fruit::Component<intAnnot> getComponent() { 231 return fruit::createComponent() 232 .bind<XAnnot, intAnnot>(); 233 } 234 ''' 235 expect_compile_error( 236 'NotABaseClassOfError<X,int>', 237 'I is not a base class of C.', 238 COMMON_DEFINITIONS, 239 source, 240 locals()) 241 242 # TODO: maybe the error should include the annotation here. 243 @pytest.mark.parametrize('XAnnot', [ 244 'X', 245 'fruit::Annotated<Annotation1, X>', 246 ]) 247 def test_error_bound_to_itself(XAnnot): 248 source = ''' 249 struct X {}; 250 251 fruit::Component<X> getComponent() { 252 return fruit::createComponent() 253 .bind<XAnnot, XAnnot>(); 254 } 255 ''' 256 expect_compile_error( 257 'InterfaceBindingToSelfError<X>', 258 'The type C was bound to itself.', 259 COMMON_DEFINITIONS, 260 source, 261 locals()) 262 263 def test_bound_to_itself_with_annotation_error(): 264 source = ''' 265 struct X {}; 266 267 fruit::Component<> getComponent() { 268 return fruit::createComponent() 269 .registerConstructor<X()>() 270 .bind<fruit::Annotated<Annotation1, X>, X>(); 271 } 272 ''' 273 expect_compile_error( 274 'InterfaceBindingToSelfError<X>', 275 'The type C was bound to itself.', 276 COMMON_DEFINITIONS, 277 source) 278 279 def test_bound_chain_ok(): 280 source = ''' 281 struct X { 282 virtual void f() = 0; 283 }; 284 285 struct Y : public X {}; 286 287 struct Z : public Y { 288 INJECT(Z()) = default; 289 void f() override { 290 } 291 }; 292 293 fruit::Component<X> getComponent() { 294 return fruit::createComponent() 295 .bind<X, Y>() 296 .bind<Y, Z>(); 297 } 298 299 int main() { 300 fruit::Injector<X> injector(getComponent); 301 X& x = injector.get<X&>(); 302 x.f(); 303 } 304 ''' 305 expect_success(COMMON_DEFINITIONS, source) 306 307 def test_bind_non_normalized_types_error(): 308 source = ''' 309 struct X {}; 310 311 struct Y : public std::shared_ptr<X> {}; 312 313 fruit::Component<> getComponent() { 314 return fruit::createComponent() 315 .bind<std::shared_ptr<X>, Y>(); 316 } 317 ''' 318 expect_compile_error( 319 'NonClassTypeError<std::shared_ptr<X>,X>', 320 'A non-class type T was specified. Use C instead', 321 COMMON_DEFINITIONS, 322 source) 323 324 if __name__== '__main__': 325 main(__file__) 326