1 /* 2 * Copyright (C) 2021 The Dagger Authors. 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 */ 16 17 package dagger.spi.model.testing; 18 19 import static com.google.common.collect.Iterables.getOnlyElement; 20 import static com.google.common.truth.Truth.assertAbout; 21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 22 23 import com.google.common.collect.ImmutableSet; 24 import com.google.common.truth.FailureMetadata; 25 import com.google.common.truth.Subject; 26 import dagger.spi.model.Binding; 27 import dagger.spi.model.BindingGraph; 28 import dagger.spi.model.DaggerType; 29 import javax.lang.model.type.TypeMirror; 30 import org.checkerframework.checker.nullness.compatqual.NullableDecl; 31 32 /** A Truth subject for making assertions on a {@link BindingGraph}. */ 33 public final class BindingGraphSubject extends Subject { 34 35 /** Starts a fluent assertion about a {@link BindingGraph}. */ assertThat(BindingGraph bindingGraph)36 public static BindingGraphSubject assertThat(BindingGraph bindingGraph) { 37 return assertAbout(BindingGraphSubject::new).that(bindingGraph); 38 } 39 40 private final BindingGraph actual; 41 BindingGraphSubject(FailureMetadata metadata, @NullableDecl BindingGraph actual)42 private BindingGraphSubject(FailureMetadata metadata, @NullableDecl BindingGraph actual) { 43 super(metadata, actual); 44 this.actual = actual; 45 } 46 47 /** 48 * Asserts that the graph has at least one binding with an unqualified key. 49 * 50 * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()} 51 */ hasBindingWithKey(String type)52 public void hasBindingWithKey(String type) { 53 bindingWithKey(type); 54 } 55 56 /** 57 * Asserts that the graph has at least one binding with a qualified key. 58 * 59 * @param qualifier the canonical string form of the qualifier, as returned by {@link 60 * javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()} 61 * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()} 62 */ hasBindingWithKey(String qualifier, String type)63 public void hasBindingWithKey(String qualifier, String type) { 64 bindingWithKey(qualifier, type); 65 } 66 67 /** 68 * Returns a subject for testing the binding for an unqualified key. 69 * 70 * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()} 71 */ bindingWithKey(String type)72 public BindingSubject bindingWithKey(String type) { 73 return bindingWithKeyString(type); 74 } 75 76 /** 77 * Returns a subject for testing the binding for a qualified key. 78 * 79 * @param qualifier the canonical string form of the qualifier, as returned by {@link 80 * javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()} 81 * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()} 82 */ bindingWithKey(String qualifier, String type)83 public BindingSubject bindingWithKey(String qualifier, String type) { 84 return bindingWithKeyString(keyString(qualifier, type)); 85 } 86 bindingWithKeyString(String keyString)87 private BindingSubject bindingWithKeyString(String keyString) { 88 ImmutableSet<Binding> bindings = getBindingNodes(keyString); 89 // TODO(dpb): Handle multiple bindings for the same key. 90 check("bindingsWithKey(%s)", keyString).that(bindings).hasSize(1); 91 return check("bindingWithKey(%s)", keyString) 92 .about(BindingSubject::new) 93 .that(getOnlyElement(bindings)); 94 } 95 getBindingNodes(String keyString)96 private ImmutableSet<Binding> getBindingNodes(String keyString) { 97 return actual.bindings().stream() 98 .filter(binding -> keyString(binding).equals(keyString)) 99 .collect(toImmutableSet()); 100 } 101 keyString(Binding binding)102 public static String keyString(Binding binding) { 103 return binding.key().qualifier().isPresent() 104 ? keyString(binding.key().qualifier().get().toString(), formattedType(binding.key().type())) 105 : formattedType(binding.key().type()); 106 } 107 keyString(String qualifier, String type)108 private static String keyString(String qualifier, String type) { 109 return String.format("%s %s", qualifier, type); 110 } 111 formattedType(DaggerType type)112 private static String formattedType(DaggerType type) { 113 switch (type.backend()) { 114 case JAVAC: 115 return type.javac().toString(); 116 case KSP: 117 return type.ksp().getDeclaration().getQualifiedName().asString(); 118 } 119 throw new AssertionError("Unsupported backend"); 120 } 121 122 /** A Truth subject for a {@link Binding}. */ 123 public final class BindingSubject extends Subject { 124 125 private final Binding actual; 126 BindingSubject(FailureMetadata metadata, @NullableDecl Binding actual)127 BindingSubject(FailureMetadata metadata, @NullableDecl Binding actual) { 128 super(metadata, actual); 129 this.actual = actual; 130 } 131 132 /** 133 * Asserts that the binding depends on a binding with an unqualified key. 134 * 135 * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()} 136 */ dependsOnBindingWithKey(String type)137 public void dependsOnBindingWithKey(String type) { 138 dependsOnBindingWithKeyString(type); 139 } 140 141 /** 142 * Asserts that the binding depends on a binding with a qualified key. 143 * 144 * @param qualifier the canonical string form of the qualifier, as returned by {@link 145 * javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()} 146 * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()} 147 */ dependsOnBindingWithKey(String qualifier, String type)148 public void dependsOnBindingWithKey(String qualifier, String type) { 149 dependsOnBindingWithKeyString(keyString(qualifier, type)); 150 } 151 dependsOnBindingWithKeyString(String keyString)152 private void dependsOnBindingWithKeyString(String keyString) { 153 if (actualBindingGraph().requestedBindings(actual).stream() 154 .noneMatch(binding -> keyString(binding).equals(keyString))) { 155 failWithActual("expected to depend on binding with key", keyString); 156 } 157 } 158 actualBindingGraph()159 private BindingGraph actualBindingGraph() { 160 return BindingGraphSubject.this.actual; 161 } 162 } 163 } 164