1*f585d8a3SJacky Wang /* 2*f585d8a3SJacky Wang * Copyright (C) 2015 The Dagger Authors. 3*f585d8a3SJacky Wang * 4*f585d8a3SJacky Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5*f585d8a3SJacky Wang * you may not use this file except in compliance with the License. 6*f585d8a3SJacky Wang * You may obtain a copy of the License at 7*f585d8a3SJacky Wang * 8*f585d8a3SJacky Wang * http://www.apache.org/licenses/LICENSE-2.0 9*f585d8a3SJacky Wang * 10*f585d8a3SJacky Wang * Unless required by applicable law or agreed to in writing, software 11*f585d8a3SJacky Wang * distributed under the License is distributed on an "AS IS" BASIS, 12*f585d8a3SJacky Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*f585d8a3SJacky Wang * See the License for the specific language governing permissions and 14*f585d8a3SJacky Wang * limitations under the License. 15*f585d8a3SJacky Wang */ 16*f585d8a3SJacky Wang 17*f585d8a3SJacky Wang package dagger.functional.nullables; 18*f585d8a3SJacky Wang 19*f585d8a3SJacky Wang import static com.google.common.truth.Truth.assertThat; 20*f585d8a3SJacky Wang import static org.junit.Assert.fail; 21*f585d8a3SJacky Wang 22*f585d8a3SJacky Wang import dagger.Component; 23*f585d8a3SJacky Wang import dagger.Module; 24*f585d8a3SJacky Wang import dagger.Provides; 25*f585d8a3SJacky Wang import dagger.Reusable; 26*f585d8a3SJacky Wang import javax.inject.Inject; 27*f585d8a3SJacky Wang import javax.inject.Provider; 28*f585d8a3SJacky Wang import org.junit.Test; 29*f585d8a3SJacky Wang import org.junit.runner.RunWith; 30*f585d8a3SJacky Wang import org.junit.runners.JUnit4; 31*f585d8a3SJacky Wang 32*f585d8a3SJacky Wang @RunWith(JUnit4.class) 33*f585d8a3SJacky Wang public class NullabilityTest { 34*f585d8a3SJacky Wang @interface Nullable {} 35*f585d8a3SJacky Wang 36*f585d8a3SJacky Wang @Component(dependencies = NullComponent.class) 37*f585d8a3SJacky Wang interface NullComponentWithDependency { string()38*f585d8a3SJacky Wang @Nullable String string(); number()39*f585d8a3SJacky Wang Number number(); stringProvider()40*f585d8a3SJacky Wang Provider<String> stringProvider(); numberProvider()41*f585d8a3SJacky Wang Provider<Number> numberProvider(); 42*f585d8a3SJacky Wang } 43*f585d8a3SJacky Wang 44*f585d8a3SJacky Wang @Component(modules = NullModule.class) 45*f585d8a3SJacky Wang interface NullComponent { string()46*f585d8a3SJacky Wang @Nullable String string(); integer()47*f585d8a3SJacky Wang @Nullable Integer integer(); nullFoo()48*f585d8a3SJacky Wang NullFoo nullFoo(); number()49*f585d8a3SJacky Wang Number number(); stringProvider()50*f585d8a3SJacky Wang Provider<String> stringProvider(); numberProvider()51*f585d8a3SJacky Wang Provider<Number> numberProvider(); 52*f585d8a3SJacky Wang } 53*f585d8a3SJacky Wang 54*f585d8a3SJacky Wang @Module 55*f585d8a3SJacky Wang static class NullModule { 56*f585d8a3SJacky Wang Number numberValue = null; 57*f585d8a3SJacky Wang Integer integerCallCount = 0; 58*f585d8a3SJacky Wang 59*f585d8a3SJacky Wang @Nullable 60*f585d8a3SJacky Wang @Provides provideNullableString()61*f585d8a3SJacky Wang String provideNullableString() { 62*f585d8a3SJacky Wang return null; 63*f585d8a3SJacky Wang } 64*f585d8a3SJacky Wang 65*f585d8a3SJacky Wang @Provides provideNumber()66*f585d8a3SJacky Wang Number provideNumber() { 67*f585d8a3SJacky Wang return numberValue; 68*f585d8a3SJacky Wang } 69*f585d8a3SJacky Wang 70*f585d8a3SJacky Wang @Nullable 71*f585d8a3SJacky Wang @Provides 72*f585d8a3SJacky Wang @Reusable provideNullReusableInteger()73*f585d8a3SJacky Wang Integer provideNullReusableInteger() { 74*f585d8a3SJacky Wang integerCallCount++; 75*f585d8a3SJacky Wang return null; 76*f585d8a3SJacky Wang } 77*f585d8a3SJacky Wang } 78*f585d8a3SJacky Wang 79*f585d8a3SJacky Wang @SuppressWarnings("BadInject") // This is just for testing purposes. 80*f585d8a3SJacky Wang static class NullFoo { 81*f585d8a3SJacky Wang final String string; 82*f585d8a3SJacky Wang final Number number; 83*f585d8a3SJacky Wang final Provider<String> stringProvider; 84*f585d8a3SJacky Wang final Provider<Number> numberProvider; 85*f585d8a3SJacky Wang 86*f585d8a3SJacky Wang @Inject NullFoo( @ullable String string, Number number, Provider<String> stringProvider, Provider<Number> numberProvider)87*f585d8a3SJacky Wang NullFoo( 88*f585d8a3SJacky Wang @Nullable String string, 89*f585d8a3SJacky Wang Number number, 90*f585d8a3SJacky Wang Provider<String> stringProvider, 91*f585d8a3SJacky Wang Provider<Number> numberProvider) { 92*f585d8a3SJacky Wang this.string = string; 93*f585d8a3SJacky Wang this.number = number; 94*f585d8a3SJacky Wang this.stringProvider = stringProvider; 95*f585d8a3SJacky Wang this.numberProvider = numberProvider; 96*f585d8a3SJacky Wang } 97*f585d8a3SJacky Wang 98*f585d8a3SJacky Wang String methodInjectedString; 99*f585d8a3SJacky Wang Number methodInjectedNumber; 100*f585d8a3SJacky Wang Provider<String> methodInjectedStringProvider; 101*f585d8a3SJacky Wang Provider<Number> methodInjectedNumberProvider; inject( @ullable String string, Number number, Provider<String> stringProvider, Provider<Number> numberProvider)102*f585d8a3SJacky Wang @Inject void inject( 103*f585d8a3SJacky Wang @Nullable String string, 104*f585d8a3SJacky Wang Number number, 105*f585d8a3SJacky Wang Provider<String> stringProvider, 106*f585d8a3SJacky Wang Provider<Number> numberProvider) { 107*f585d8a3SJacky Wang this.methodInjectedString = string; 108*f585d8a3SJacky Wang this.methodInjectedNumber = number; 109*f585d8a3SJacky Wang this.methodInjectedStringProvider = stringProvider; 110*f585d8a3SJacky Wang this.methodInjectedNumberProvider = numberProvider; 111*f585d8a3SJacky Wang } 112*f585d8a3SJacky Wang 113*f585d8a3SJacky Wang @Nullable @Inject String fieldInjectedString; 114*f585d8a3SJacky Wang @Inject Number fieldInjectedNumber; 115*f585d8a3SJacky Wang @Inject Provider<String> fieldInjectedStringProvider; 116*f585d8a3SJacky Wang @Inject Provider<Number> fieldInjectedNumberProvider; 117*f585d8a3SJacky Wang } 118*f585d8a3SJacky Wang 119*f585d8a3SJacky Wang @Test testNullability_provides()120*f585d8a3SJacky Wang public void testNullability_provides() { 121*f585d8a3SJacky Wang NullModule module = new NullModule(); 122*f585d8a3SJacky Wang NullComponent component = 123*f585d8a3SJacky Wang DaggerNullabilityTest_NullComponent.builder().nullModule(module).build(); 124*f585d8a3SJacky Wang 125*f585d8a3SJacky Wang // Can't construct NullFoo because it depends on Number, and Number was null. 126*f585d8a3SJacky Wang try { 127*f585d8a3SJacky Wang component.nullFoo(); 128*f585d8a3SJacky Wang fail(); 129*f585d8a3SJacky Wang } catch (NullPointerException npe) { 130*f585d8a3SJacky Wang assertThat(npe) 131*f585d8a3SJacky Wang .hasMessageThat() 132*f585d8a3SJacky Wang .isEqualTo("Cannot return null from a non-@Nullable @Provides method"); 133*f585d8a3SJacky Wang } 134*f585d8a3SJacky Wang 135*f585d8a3SJacky Wang // set number to non-null so we can create 136*f585d8a3SJacky Wang module.numberValue = 1; 137*f585d8a3SJacky Wang NullFoo nullFoo = component.nullFoo(); 138*f585d8a3SJacky Wang 139*f585d8a3SJacky Wang // Then set it back to null so we can test its providers. 140*f585d8a3SJacky Wang module.numberValue = null; 141*f585d8a3SJacky Wang validate(true, nullFoo.string, nullFoo.stringProvider, nullFoo.numberProvider); 142*f585d8a3SJacky Wang validate(true, nullFoo.methodInjectedString, nullFoo.methodInjectedStringProvider, 143*f585d8a3SJacky Wang nullFoo.methodInjectedNumberProvider); 144*f585d8a3SJacky Wang validate(true, nullFoo.fieldInjectedString, nullFoo.fieldInjectedStringProvider, 145*f585d8a3SJacky Wang nullFoo.fieldInjectedNumberProvider); 146*f585d8a3SJacky Wang } 147*f585d8a3SJacky Wang 148*f585d8a3SJacky Wang @Test testNullability_reusuable()149*f585d8a3SJacky Wang public void testNullability_reusuable() { 150*f585d8a3SJacky Wang NullModule module = new NullModule(); 151*f585d8a3SJacky Wang NullComponent component = 152*f585d8a3SJacky Wang DaggerNullabilityTest_NullComponent.builder().nullModule(module).build(); 153*f585d8a3SJacky Wang 154*f585d8a3SJacky Wang // Test that the @Nullable @Reusuable binding is cached properly even when the value is null. 155*f585d8a3SJacky Wang assertThat(module.integerCallCount).isEqualTo(0); 156*f585d8a3SJacky Wang assertThat(component.integer()).isNull(); 157*f585d8a3SJacky Wang assertThat(module.integerCallCount).isEqualTo(1); 158*f585d8a3SJacky Wang assertThat(component.integer()).isNull(); 159*f585d8a3SJacky Wang assertThat(module.integerCallCount).isEqualTo(1); 160*f585d8a3SJacky Wang } 161*f585d8a3SJacky Wang 162*f585d8a3SJacky Wang @Test testNullability_components()163*f585d8a3SJacky Wang public void testNullability_components() { 164*f585d8a3SJacky Wang NullComponent nullComponent = new NullComponent() { 165*f585d8a3SJacky Wang @Override public Provider<String> stringProvider() { 166*f585d8a3SJacky Wang return new Provider<String>() { 167*f585d8a3SJacky Wang @Override public String get() { 168*f585d8a3SJacky Wang return null; 169*f585d8a3SJacky Wang } 170*f585d8a3SJacky Wang }; 171*f585d8a3SJacky Wang } 172*f585d8a3SJacky Wang 173*f585d8a3SJacky Wang @Override public String string() { 174*f585d8a3SJacky Wang return null; 175*f585d8a3SJacky Wang } 176*f585d8a3SJacky Wang 177*f585d8a3SJacky Wang @Override public Provider<Number> numberProvider() { 178*f585d8a3SJacky Wang return new Provider<Number>() { 179*f585d8a3SJacky Wang @Override public Number get() { 180*f585d8a3SJacky Wang return null; 181*f585d8a3SJacky Wang } 182*f585d8a3SJacky Wang }; 183*f585d8a3SJacky Wang } 184*f585d8a3SJacky Wang 185*f585d8a3SJacky Wang @Override public Number number() { 186*f585d8a3SJacky Wang return null; 187*f585d8a3SJacky Wang } 188*f585d8a3SJacky Wang 189*f585d8a3SJacky Wang @Override public NullFoo nullFoo() { 190*f585d8a3SJacky Wang return null; 191*f585d8a3SJacky Wang } 192*f585d8a3SJacky Wang 193*f585d8a3SJacky Wang @Override public Integer integer() { 194*f585d8a3SJacky Wang return null; 195*f585d8a3SJacky Wang } 196*f585d8a3SJacky Wang }; 197*f585d8a3SJacky Wang NullComponentWithDependency component = 198*f585d8a3SJacky Wang DaggerNullabilityTest_NullComponentWithDependency.builder() 199*f585d8a3SJacky Wang .nullComponent(nullComponent) 200*f585d8a3SJacky Wang .build(); 201*f585d8a3SJacky Wang validate(false, component.string(), component.stringProvider(), component.numberProvider()); 202*f585d8a3SJacky Wang 203*f585d8a3SJacky Wang // Also validate that the component's number() method fails 204*f585d8a3SJacky Wang try { 205*f585d8a3SJacky Wang component.number(); 206*f585d8a3SJacky Wang fail(); 207*f585d8a3SJacky Wang } catch (NullPointerException npe) { 208*f585d8a3SJacky Wang assertThat(npe) 209*f585d8a3SJacky Wang .hasMessageThat() 210*f585d8a3SJacky Wang .isEqualTo("Cannot return null from a non-@Nullable component method"); 211*f585d8a3SJacky Wang } 212*f585d8a3SJacky Wang } 213*f585d8a3SJacky Wang validate(boolean fromProvides, String string, Provider<String> stringProvider, Provider<Number> numberProvider)214*f585d8a3SJacky Wang private void validate(boolean fromProvides, 215*f585d8a3SJacky Wang String string, 216*f585d8a3SJacky Wang Provider<String> stringProvider, 217*f585d8a3SJacky Wang Provider<Number> numberProvider) { 218*f585d8a3SJacky Wang assertThat(string).isNull(); 219*f585d8a3SJacky Wang assertThat(numberProvider).isNotNull(); 220*f585d8a3SJacky Wang try { 221*f585d8a3SJacky Wang numberProvider.get(); 222*f585d8a3SJacky Wang fail(); 223*f585d8a3SJacky Wang } catch (NullPointerException npe) { 224*f585d8a3SJacky Wang assertThat(npe) 225*f585d8a3SJacky Wang .hasMessageThat() 226*f585d8a3SJacky Wang .isEqualTo( 227*f585d8a3SJacky Wang "Cannot return null from a non-@Nullable " 228*f585d8a3SJacky Wang + (fromProvides ? "@Provides" : "component") 229*f585d8a3SJacky Wang + " method"); 230*f585d8a3SJacky Wang } 231*f585d8a3SJacky Wang assertThat(stringProvider.get()).isNull(); 232*f585d8a3SJacky Wang } 233*f585d8a3SJacky Wang } 234