• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 The gRPC 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 io.grpc.testing.integration;
18 
19 import static junit.framework.TestCase.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 
22 import io.grpc.ManagedChannel;
23 import io.grpc.ManagedChannelBuilder;
24 import io.grpc.internal.MoreThrowables;
25 import io.grpc.okhttp.OkHttpChannelBuilder;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import java.io.StringWriter;
29 import java.lang.reflect.Method;
30 import java.text.SimpleDateFormat;
31 import java.util.ArrayList;
32 import java.util.Calendar;
33 import java.util.List;
34 import java.util.Queue;
35 import java.util.concurrent.ConcurrentLinkedQueue;
36 import java.util.logging.Handler;
37 import java.util.logging.LogRecord;
38 import java.util.logging.Logger;
39 import java.util.logging.SimpleFormatter;
40 import javax.servlet.http.HttpServlet;
41 import javax.servlet.http.HttpServletRequest;
42 import javax.servlet.http.HttpServletResponse;
43 import org.junit.After;
44 import org.junit.Before;
45 import org.junit.Ignore;
46 import org.junit.Test;
47 import org.junit.internal.AssumptionViolatedException;
48 
49 /**
50  * This servlet communicates with {@code grpc-test.sandbox.googleapis.com}, which is a server
51  * managed by the gRPC team. For more information, see
52  * <a href="https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md">
53  *   Interoperability Test Case Descriptions</a>.
54  */
55 @SuppressWarnings("serial")
56 public final class OkHttpClientInteropServlet extends HttpServlet {
57   private static final String INTEROP_TEST_ADDRESS = "grpc-test.sandbox.googleapis.com:443";
58 
59   private static final class LogEntryRecorder extends Handler {
60     private Queue<LogRecord> loggedMessages = new ConcurrentLinkedQueue<>();
61 
62     @Override
publish(LogRecord logRecord)63     public void publish(LogRecord logRecord) {
64       loggedMessages.add(logRecord);
65     }
66 
67     @Override
flush()68     public void flush() {}
69 
70     @Override
close()71     public void close() {}
72 
getLogOutput()73     public String getLogOutput() {
74       SimpleFormatter formatter = new SimpleFormatter();
75       StringBuilder sb = new StringBuilder();
76       for (LogRecord loggedMessage : loggedMessages) {
77         sb.append(formatter.format(loggedMessage));
78       }
79       return sb.toString();
80     }
81   }
82 
83   @Override
doGet(HttpServletRequest req, HttpServletResponse resp)84   public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
85     LogEntryRecorder handler = new LogEntryRecorder();
86     Logger.getLogger("").addHandler(handler);
87     try {
88       doGetHelper(req, resp);
89     } finally {
90       Logger.getLogger("").removeHandler(handler);
91     }
92     resp.getWriter().append("=======================================\n")
93         .append("Server side java.util.logging messages:\n")
94         .append(handler.getLogOutput());
95   }
96 
doGetHelper(HttpServletRequest req, HttpServletResponse resp)97   private void doGetHelper(HttpServletRequest req, HttpServletResponse resp) throws IOException {
98     resp.setContentType("text/plain");
99     PrintWriter writer = resp.getWriter();
100     writer.println("Test invoked at: ");
101     writer.println(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss Z")
102         .format(Calendar.getInstance().getTime()));
103 
104     // We can not use JUnit because it tries to spawn backgrounds threads.
105     // GAE+JDK7 does not allow arbitrary background threads.
106     // Let's use reflection to run test methods.
107     List<Method> befores = new ArrayList<>();
108     List<Method> afters = new ArrayList<>();
109     List<Method> testMethods = new ArrayList<>();
110     int ignored = 0;
111     for (Method method : Tester.class.getMethods()) {
112       if (method.getAnnotation(Test.class) != null) {
113         if (method.getAnnotation(Ignore.class) != null) {
114           ignored++;
115         } else {
116           testMethods.add(method);
117         }
118       } else if (method.getAnnotation(Before.class) != null) {
119         befores.add(method);
120       } else if (method.getAnnotation(After.class) != null) {
121         afters.add(method);
122       }
123     }
124 
125     StringBuilder sb = new StringBuilder();
126     int failures = 0;
127     for (Method method : testMethods) {
128       // JUnit creates a new instance per test method, we will emulate that behavior.
129       Tester tester = new Tester();
130       try {
131         for (Method before : befores) {
132           before.invoke(tester);
133         }
134         try (AutoCloseable unused = toCloseable(tester, afters)) {
135           method.invoke(tester);
136         }
137         sb.append("================\n");
138         sb.append("PASS: Test method: ").append(method).append("\n");
139       } catch (Throwable t) {
140         // The default JUnit4 test runner skips tests with failed assumptions.
141         // We will do the same here.
142         boolean assumptionViolated = false;
143         for (Throwable iter = t; iter != null; iter = iter.getCause()) {
144           if (iter instanceof AssumptionViolatedException) {
145             assumptionViolated = true;
146             break;
147           }
148         }
149         if (assumptionViolated) {
150           continue;
151         }
152 
153         sb.append("================\n");
154         sb.append("FAILED: Test method: ").append(method).append("\n");
155         failures++;
156         StringWriter stringWriter = new StringWriter();
157         PrintWriter printWriter = new PrintWriter(stringWriter);
158         t.printStackTrace(printWriter);
159         sb.append(stringWriter);
160       }
161     }
162     if (failures == 0) {
163       resp.setStatus(200);
164       writer.println(
165           String.format(
166               "PASS! Tests ran %d, tests ignored %d",
167               testMethods.size(),
168               ignored));
169     } else {
170       resp.setStatus(500);
171       writer.println(
172           String.format(
173               "FAILED! Tests ran %d, tests failed %d, tests ignored %d",
174               testMethods.size(),
175               failures,
176               ignored));
177     }
178     writer.println(sb);
179   }
180 
toCloseable(final Object o, final List<Method> methods)181   private static AutoCloseable toCloseable(final Object o, final List<Method> methods) {
182     return new AutoCloseable() {
183       @Override
184       public void close() throws Exception {
185         Throwable failure = null;
186         for (Method method : methods) {
187           try {
188             method.invoke(o);
189           } catch (Throwable t) {
190             if (failure == null) {
191               failure = t;
192             } else {
193               failure.addSuppressed(t);
194             }
195           }
196         }
197         if (failure != null) {
198           MoreThrowables.throwIfUnchecked(failure);
199           throw new Exception(failure);
200         }
201       }
202     };
203   }
204 
205   public static final class Tester extends AbstractInteropTest {
206     @Override
207     protected ManagedChannel createChannel() {
208       assertEquals(
209           "jdk7 required",
210           "1.7",
211           System.getProperty("java.specification.version"));
212       assertEquals(
213           "Can not run in dev servers because they lack org.conscrypt.OpenSSLProvider support",
214           "Production",
215           System.getProperty("com.google.appengine.runtime.environment"));
216       ManagedChannelBuilder<?> builder =
217           ManagedChannelBuilder.forTarget(INTEROP_TEST_ADDRESS)
218               .maxInboundMessageSize(AbstractInteropTest.MAX_MESSAGE_SIZE);
219       assertTrue(builder instanceof OkHttpChannelBuilder);
220       return builder.build();
221     }
222 
223     @Override
224     protected boolean metricsExpected() {
225       // Server-side metrics won't be found because the server is running remotely.
226       return false;
227     }
228 
229     // grpc-test.sandbox.googleapis.com does not support these tests
230     @Ignore
231     @Override
232     public void customMetadata() { }
233 
234     @Ignore
235     @Override
236     public void statusCodeAndMessage() { }
237 
238     @Ignore
239     @Override
240     public void exchangeMetadataUnaryCall() { }
241 
242     @Ignore
243     @Override
244     public void exchangeMetadataStreamingCall() { }
245 
246     @Ignore
247     @Override
248     public void specialStatusMessage() {}
249 
250     // grpc-java/issues/4626: this test has become flakey on GAE JDK7
251     @Ignore
252     @Override
253     public void timeoutOnSleepingServer() {}
254   }
255 }
256