aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/FallbackToGuiceInjectorTest.java
blob: 49d7516cbcb1fcb9b5d669ff19235fdc7bb64658 (plain) (blame)
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.di.componentgraph.core;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import com.yahoo.component.AbstractComponent;
import com.yahoo.component.ComponentId;
import com.yahoo.config.ConfigInstance;
import com.yahoo.vespa.config.ConfigKey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author Tony Vaagenes
 * @author gjoranv
 */
@SuppressWarnings("unused")
public class FallbackToGuiceInjectorTest {

    private ComponentGraph componentGraph;
    private Injector injector;
    private final Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs = new HashMap<>();


    @BeforeEach
    public void createGraph() {
        injector = Guice.createInjector();
        componentGraph = new ComponentGraph(0);
    }

    public static class MyComponent extends AbstractComponent {
        private final String url;
        private final Executor executor;

        @Inject
        public MyComponent(@Named("url") String url, Executor executor) {
            this.url = url;
            this.executor = executor;
        }

        public MyComponent() {
            throw new RuntimeException("Constructor annotated with @Inject is preferred.");
        }
    }

    public static class ComponentTakingDefaultString{
        private final String injectedString;

        public ComponentTakingDefaultString(String empty_string_created_by_guice) {
            this.injectedString = empty_string_created_by_guice;
        }
    }

    public static class ComponentThatCannotBeConstructed {
        public ComponentThatCannotBeConstructed(Integer cannot_be_injected_because_Integer_has_no_default_ctor) { }
    }

    @Test
    void guice_injector_is_used_when_no_global_component_exists() {
        setInjector(
                Guice.createInjector(new AbstractModule() {
                    @Override
                    protected void configure() {
                        bind(Executor.class).toInstance(Executors.newSingleThreadExecutor());
                        bind(String.class).annotatedWith(Names.named("url")).toInstance("http://yahoo.com");
                    }
                }));

        register(MyComponent.class);
        complete();

        MyComponent component = getInstance(MyComponent.class);
        assertEquals("http://yahoo.com", component.url);
        assertNotNull(component.executor);
    }

    @Test
    void guice_injector_creates_a_new_instance_with_default_ctor_when_no_explicit_binding_exists() {
        setInjector(emptyGuiceInjector());
        register(ComponentTakingDefaultString.class);
        complete();

        ComponentTakingDefaultString component = getInstance(ComponentTakingDefaultString.class);
        assertTrue(component.injectedString.isEmpty());
    }

    @Test
    void guice_injector_fails_when_no_explicit_binding_exists_and_class_has_no_default_ctor() {
        setInjector(emptyGuiceInjector());
        register(ComponentThatCannotBeConstructed.class);
        try {
            complete();
            fail();
        } catch (RuntimeException e) {
            assertEquals("When resolving dependencies of 'com.yahoo.container.di.componentgraph.core.FallbackToGuiceInjectorTest$ComponentThatCannotBeConstructed'", e.getMessage());
        }
    }

    public void register(Class<?> componentClass) {
        componentGraph.add(mockComponentNode(componentClass));
    }

    public ComponentId toId(Class<?> componentClass) {
        return ComponentId.fromString(componentClass.getName());
    }

    private Node mockComponentNode(Class<?> componentClass) {
        return new ComponentNode(toId(componentClass), toId(componentClass).toString(), componentClass, null);
    }

    public <T> T getInstance(Class<T> componentClass) {
        return componentGraph.getInstance(componentClass);
    }

    public void complete() {
        componentGraph.complete(injector);
        componentGraph.setAvailableConfigs(configs);
    }

    public void setInjector(Injector injector) {
        this.injector = injector;
    }

    private Injector emptyGuiceInjector() {
        return Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
            }
        });
    }
}