1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.mojo.bindings; 6 7 import android.test.suitebuilder.annotation.SmallTest; 8 9 import org.chromium.base.test.util.UrlUtils; 10 import org.chromium.mojo.HandleMock; 11 import org.chromium.mojo.MojoTestCase; 12 import org.chromium.mojo.bindings.test.mojom.mojo.ConformanceTestInterface; 13 import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface; 14 import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterfaceTestHelper; 15 import org.chromium.mojo.system.Handle; 16 17 import java.io.File; 18 import java.io.FileFilter; 19 import java.io.FileNotFoundException; 20 import java.util.ArrayList; 21 import java.util.List; 22 import java.util.Scanner; 23 24 /** 25 * Testing validation upon deserialization using the interfaces defined in the 26 * mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom file. 27 * <p> 28 * One needs to pass '--test_data=bindings:{path to mojo/public/interfaces/bindings/tests/data}' to 29 * the test_runner script for this test to find the validation data it needs. 30 */ 31 public class ValidationTest extends MojoTestCase { 32 33 /** 34 * The path where validation test data is. 35 */ 36 private static final File VALIDATION_TEST_DATA_PATH = 37 new File(UrlUtils.getIsolatedTestFilePath( 38 "mojo/public/interfaces/bindings/tests/data/validation")); 39 40 /** 41 * The data needed for a validation test. 42 */ 43 private static class TestData { 44 public File dataFile; 45 public ValidationTestUtil.Data inputData; 46 public String expectedResult; 47 } 48 49 private static class DataFileFilter implements FileFilter { 50 private final String mPrefix; 51 DataFileFilter(String prefix)52 public DataFileFilter(String prefix) { 53 this.mPrefix = prefix; 54 } 55 56 @Override accept(File pathname)57 public boolean accept(File pathname) { 58 // TODO(yzshen, qsr): skip some interface versioning tests. 59 if (pathname.getName().startsWith("conformance_mthd13_good_2")) { 60 return false; 61 } 62 return pathname.isFile() && pathname.getName().startsWith(mPrefix) 63 && pathname.getName().endsWith(".data"); 64 } 65 } 66 getStringContent(File f)67 private static String getStringContent(File f) throws FileNotFoundException { 68 try (Scanner scanner = new Scanner(f)) { 69 scanner.useDelimiter("\\Z"); 70 StringBuilder result = new StringBuilder(); 71 while (scanner.hasNext()) { 72 result.append(scanner.next()); 73 } 74 return result.toString().trim(); 75 } 76 } 77 getTestData(String prefix)78 private static List<TestData> getTestData(String prefix) 79 throws FileNotFoundException { 80 List<TestData> results = new ArrayList<TestData>(); 81 82 // Fail if the test data is not present. 83 if (!VALIDATION_TEST_DATA_PATH.isDirectory()) { 84 fail("No test data directory found. " 85 + "Expected directory at: " + VALIDATION_TEST_DATA_PATH); 86 } 87 88 File[] files = VALIDATION_TEST_DATA_PATH.listFiles(new DataFileFilter(prefix)); 89 if (files != null) { 90 for (File dataFile : files) { 91 File resultFile = new File(dataFile.getParent(), 92 dataFile.getName().replaceFirst("\\.data$", ".expected")); 93 TestData testData = new TestData(); 94 testData.dataFile = dataFile; 95 testData.inputData = ValidationTestUtil.parseData(getStringContent(dataFile)); 96 testData.expectedResult = getStringContent(resultFile); 97 results.add(testData); 98 } 99 } 100 return results; 101 } 102 103 /** 104 * Runs all the test with the given prefix on the given {@link MessageReceiver}. 105 */ runTest(String prefix, MessageReceiver messageReceiver)106 private static void runTest(String prefix, MessageReceiver messageReceiver) 107 throws FileNotFoundException { 108 List<TestData> testData = getTestData(prefix); 109 for (TestData test : testData) { 110 assertNull("Unable to read: " + test.dataFile.getName() 111 + ": " + test.inputData.getErrorMessage(), 112 test.inputData.getErrorMessage()); 113 List<Handle> handles = new ArrayList<Handle>(); 114 for (int i = 0; i < test.inputData.getHandlesCount(); ++i) { 115 handles.add(new HandleMock()); 116 } 117 Message message = new Message(test.inputData.getData(), handles); 118 boolean passed = messageReceiver.accept(message); 119 if (passed && !test.expectedResult.equals("PASS")) { 120 fail("Input: " + test.dataFile.getName() 121 + ": The message should have been refused. Expected error: " 122 + test.expectedResult); 123 } 124 if (!passed && test.expectedResult.equals("PASS")) { 125 fail("Input: " + test.dataFile.getName() 126 + ": The message should have been accepted."); 127 } 128 } 129 } 130 131 private static class RoutingMessageReceiver implements MessageReceiver { 132 private final MessageReceiverWithResponder mRequest; 133 private final MessageReceiver mResponse; 134 RoutingMessageReceiver(MessageReceiverWithResponder request, MessageReceiver response)135 private RoutingMessageReceiver(MessageReceiverWithResponder request, 136 MessageReceiver response) { 137 this.mRequest = request; 138 this.mResponse = response; 139 } 140 141 /** 142 * @see MessageReceiver#accept(Message) 143 */ 144 @Override accept(Message message)145 public boolean accept(Message message) { 146 try { 147 MessageHeader header = message.asServiceMessage().getHeader(); 148 if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { 149 return mResponse.accept(message); 150 } else { 151 return mRequest.acceptWithResponder(message, new SinkMessageReceiver()); 152 } 153 } catch (DeserializationException e) { 154 return false; 155 } 156 } 157 158 /** 159 * @see MessageReceiver#close() 160 */ 161 @Override close()162 public void close() { 163 } 164 165 } 166 167 /** 168 * A trivial message receiver that refuses all messages it receives. 169 */ 170 private static class SinkMessageReceiver implements MessageReceiverWithResponder { 171 172 @Override accept(Message message)173 public boolean accept(Message message) { 174 return true; 175 } 176 177 @Override close()178 public void close() { 179 } 180 181 @Override acceptWithResponder(Message message, MessageReceiver responder)182 public boolean acceptWithResponder(Message message, MessageReceiver responder) { 183 return true; 184 } 185 } 186 187 /** 188 * Testing the conformance suite. 189 */ 190 @SmallTest testConformance()191 public void testConformance() throws FileNotFoundException { 192 runTest("conformance_", ConformanceTestInterface.MANAGER.buildStub(null, 193 ConformanceTestInterface.MANAGER.buildProxy(null, new SinkMessageReceiver()))); 194 } 195 196 /** 197 * Testing the integration suite for message headers. 198 */ 199 @SmallTest testIntegrationMessageHeader()200 public void testIntegrationMessageHeader() throws FileNotFoundException { 201 runTest("integration_msghdr_", 202 new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null, 203 IntegrationTestInterface.MANAGER.buildProxy(null, 204 new SinkMessageReceiver())), 205 IntegrationTestInterfaceTestHelper 206 .newIntegrationTestInterfaceMethodCallback())); 207 } 208 209 /** 210 * Testing the integration suite for request messages. 211 */ 212 @SmallTest testIntegrationRequestMessage()213 public void testIntegrationRequestMessage() throws FileNotFoundException { 214 runTest("integration_intf_rqst_", 215 new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null, 216 IntegrationTestInterface.MANAGER.buildProxy(null, 217 new SinkMessageReceiver())), 218 IntegrationTestInterfaceTestHelper 219 .newIntegrationTestInterfaceMethodCallback())); 220 } 221 222 /** 223 * Testing the integration suite for response messages. 224 */ 225 @SmallTest testIntegrationResponseMessage()226 public void testIntegrationResponseMessage() throws FileNotFoundException { 227 runTest("integration_intf_resp_", 228 new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null, 229 IntegrationTestInterface.MANAGER.buildProxy(null, 230 new SinkMessageReceiver())), 231 IntegrationTestInterfaceTestHelper 232 .newIntegrationTestInterfaceMethodCallback())); 233 } 234 } 235