| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The LUCI Authors. All rights reserved. | 2 # Copyright 2014 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 import binascii | 6 import binascii |
| 7 import os |
| 8 import re |
| 9 import sys |
| 7 import time | 10 import time |
| 8 import unittest | 11 import unittest |
| 9 import sys | |
| 10 | 12 |
| 11 # Somehow this lets us find isolate_storage | 13 # Somehow this lets us find isolate_storage |
| 12 import net_utils | 14 import net_utils |
| 13 | 15 |
| 14 from depot_tools import auto_stub | 16 from depot_tools import auto_stub |
| 15 import isolate_storage | 17 import isolate_storage |
| 16 import test_utils | 18 import test_utils |
| 17 | 19 |
| 18 | 20 class ByteStreamStubMock(object): |
| 19 class FileServiceStubMock(object): | |
| 20 """Replacement for real gRPC stub | 21 """Replacement for real gRPC stub |
| 21 | 22 |
| 22 We can't mock *within* the real stub to replace individual functions, plus | 23 We can't mock *within* the real stub to replace individual functions, plus |
| 23 we'd have to mock __init__ every time anyway. So this class replaces the | 24 we'd have to mock __init__ every time anyway. So this class replaces the |
| 24 entire stub. As for the functions, they implement default happy path | 25 entire stub. As for the functions, they implement default happy path |
| 25 behaviour where possible, and are not implemented otherwise. | 26 behaviour where possible, and are not implemented otherwise. |
| 26 """ | 27 """ |
| 27 def __init__(self, _channel): | 28 def __init__(self, _channel): |
| 28 self._push_requests = [] | 29 self._push_requests = [] |
| 29 self._contains_requests = [] | 30 self._contains_requests = [] |
| 30 | 31 |
| 31 def FetchBlobs(self, request, timeout=None): | 32 def Read(self, request, timeout=None): |
| 33 del request, timeout |
| 32 raise NotImplementedError() | 34 raise NotImplementedError() |
| 33 | 35 |
| 34 def PushBlobs(self, requests): | 36 def Write(self, requests, timeout=None): |
| 37 del timeout |
| 38 nb = 0 |
| 35 for r in requests: | 39 for r in requests: |
| 40 nb += len(r.data) |
| 36 self._push_requests.append(r.__deepcopy__()) | 41 self._push_requests.append(r.__deepcopy__()) |
| 37 response = isolate_storage.isolate_bot_pb2.PushBlobsReply() | 42 resp = isolate_storage.bytestream_pb2.WriteResponse() |
| 38 response.status.succeeded = True | 43 resp.committed_size = nb |
| 39 return response | 44 return resp |
| 40 | |
| 41 def Contains(self, request, timeout=None): | |
| 42 del timeout | |
| 43 response = isolate_storage.isolate_bot_pb2.ContainsReply() | |
| 44 self._contains_requests.append(request.__deepcopy__()) | |
| 45 response.status.succeeded = True | |
| 46 return response | |
| 47 | 45 |
| 48 def popContainsRequests(self): | 46 def popContainsRequests(self): |
| 49 cr = self._contains_requests | 47 cr = self._contains_requests |
| 50 self._contains_requests = [] | 48 self._contains_requests = [] |
| 51 return cr | 49 return cr |
| 52 | 50 |
| 53 def popPushRequests(self): | 51 def popPushRequests(self): |
| 54 pr = self._push_requests | 52 pr = self._push_requests |
| 55 self._push_requests = [] | 53 self._push_requests = [] |
| 56 return pr | 54 return pr |
| 57 | 55 |
| 56 def raiseError(code): |
| 57 raise isolate_storage.grpc.RpcError( |
| 58 'cannot turn this into a real code yet: %s' % code) |
| 58 | 59 |
| 59 class IsolateStorageTest(auto_stub.TestCase): | 60 class IsolateStorageTest(auto_stub.TestCase): |
| 60 def get_server(self): | 61 def get_server(self): |
| 61 return isolate_storage.IsolateServerGrpc('grpc-proxy.luci.com', | 62 os.environ['ISOLATED_GRPC_PROXY'] = 'https://luci.com/client/bob' |
| 63 return isolate_storage.IsolateServerGrpc('https://luci.appspot.com', |
| 62 'default-gzip') | 64 'default-gzip') |
| 63 | 65 |
| 64 def testFetchHappySimple(self): | 66 def testFetchHappySimple(self): |
| 65 """Fetch: if we get a few chunks with the right offset, everything works""" | 67 """Fetch: if we get a few chunks with the right offset, everything works""" |
| 66 def FetchBlobs(self, request, timeout=None): | 68 def Read(self, request, timeout=None): |
| 67 del timeout | 69 del timeout |
| 68 self.request = request | 70 self.request = request |
| 69 response = isolate_storage.isolate_bot_pb2.FetchBlobsReply() | 71 response = isolate_storage.bytestream_pb2.ReadResponse() |
| 70 response.status.succeeded = True | |
| 71 for i in range(0, 3): | 72 for i in range(0, 3): |
| 72 response.data.data = str(i) | 73 response.data = str(i) |
| 73 response.data.offset = i | |
| 74 yield response | 74 yield response |
| 75 self.mock(FileServiceStubMock, 'FetchBlobs', FetchBlobs) | 75 self.mock(ByteStreamStubMock, 'Read', Read) |
| 76 | 76 |
| 77 s = self.get_server() | 77 s = self.get_server() |
| 78 replies = s.fetch('abc123') | 78 replies = s.fetch('abc123') |
| 79 response = replies.next() | 79 response = replies.next() |
| 80 self.assertEqual(binascii.unhexlify('abc123'), | |
| 81 s._stub.request.digest[0].digest) | |
| 82 self.assertEqual('0', response) | 80 self.assertEqual('0', response) |
| 83 response = replies.next() | 81 response = replies.next() |
| 84 self.assertEqual('1', response) | 82 self.assertEqual('1', response) |
| 85 response = replies.next() | 83 response = replies.next() |
| 86 self.assertEqual('2', response) | 84 self.assertEqual('2', response) |
| 87 | 85 |
| 88 def testFetchHappyZeroLengthBlob(self): | 86 def testFetchHappyZeroLengthBlob(self): |
| 89 """Fetch: if we get a zero-length blob, everything works""" | 87 """Fetch: if we get a zero-length blob, everything works""" |
| 90 def FetchBlobs(self, request, timeout=None): | 88 def Read(self, request, timeout=None): |
| 91 del timeout | 89 del timeout |
| 92 self.request = request | 90 self.request = request |
| 93 response = isolate_storage.isolate_bot_pb2.FetchBlobsReply() | 91 response = isolate_storage.bytestream_pb2.ReadResponse() |
| 94 response.status.succeeded = True | 92 response.data = '' |
| 95 response.data.data = '' | |
| 96 yield response | 93 yield response |
| 97 self.mock(FileServiceStubMock, 'FetchBlobs', FetchBlobs) | 94 self.mock(ByteStreamStubMock, 'Read', Read) |
| 98 | 95 |
| 99 s = self.get_server() | 96 s = self.get_server() |
| 100 replies = s.fetch('abc123') | 97 replies = s.fetch('abc123') |
| 101 response = replies.next() | 98 reply = replies.next() |
| 102 self.assertEqual(binascii.unhexlify('abc123'), | 99 self.assertEqual(0, len(reply)) |
| 103 s._stub.request.digest[0].digest) | |
| 104 self.assertEqual(0, len(response)) | |
| 105 | 100 |
| 106 def testFetchThrowsOnWrongOffset(self): | 101 def testFetchThrowsOnFailure(self): |
| 107 """Fetch: if we get a chunk with the wrong offset, we throw an exception""" | 102 """Fetch: if something goes wrong in Isolate, we throw an exception""" |
| 108 def FetchBlobs(self, request, timeout=None): | 103 def Read(self, request, timeout=None): |
| 109 del timeout | 104 del timeout |
| 110 self.request = request | 105 self.request = request |
| 111 response = isolate_storage.isolate_bot_pb2.FetchBlobsReply() | 106 raiseError(isolate_storage.grpc.StatusCode.INTERNAL) |
| 112 response.status.succeeded = True | 107 self.mock(ByteStreamStubMock, 'Read', Read) |
| 113 response.data.data = str(42) | |
| 114 response.data.offset = 1 | |
| 115 yield response | |
| 116 self.mock(FileServiceStubMock, 'FetchBlobs', FetchBlobs) | |
| 117 | 108 |
| 118 s = self.get_server() | 109 s = self.get_server() |
| 119 replies = s.fetch('abc123') | 110 replies = s.fetch('abc123') |
| 120 with self.assertRaises(IOError): | |
| 121 _response = replies.next() | |
| 122 | |
| 123 def testFetchThrowsOnFailure(self): | |
| 124 """Fetch: if something goes wrong in Isolate, we throw an exception""" | |
| 125 def FetchBlobs(self, request, timeout=None): | |
| 126 del timeout | |
| 127 self.request = request | |
| 128 response = isolate_storage.isolate_bot_pb2.FetchBlobsReply() | |
| 129 response.status.succeeded = False | |
| 130 yield response | |
| 131 self.mock(FileServiceStubMock, 'FetchBlobs', FetchBlobs) | |
| 132 | |
| 133 s = self.get_server() | |
| 134 replies = s.fetch('abc123') | |
| 135 with self.assertRaises(IOError): | 111 with self.assertRaises(IOError): |
| 136 _response = replies.next() | 112 _response = replies.next() |
| 137 | 113 |
| 138 def testFetchThrowsCorrectExceptionOnGrpcFailure(self): | 114 def testFetchThrowsCorrectExceptionOnGrpcFailure(self): |
| 139 """Fetch: if something goes wrong in gRPC, we throw an IOError""" | 115 """Fetch: if something goes wrong in gRPC, we throw an IOError""" |
| 140 def FetchBlobs(_self, _request, timeout=None): | 116 def Read(_self, _request, timeout=None): |
| 141 del timeout | 117 del timeout |
| 142 raise isolate_storage.grpc.RpcError('proxy died during initial fetch :(') | 118 raise isolate_storage.grpc.RpcError('proxy died during initial fetch :(') |
| 143 self.mock(FileServiceStubMock, 'FetchBlobs', FetchBlobs) | 119 self.mock(ByteStreamStubMock, 'Read', Read) |
| 144 | 120 |
| 145 s = self.get_server() | 121 s = self.get_server() |
| 146 replies = s.fetch('abc123') | 122 replies = s.fetch('abc123') |
| 147 with self.assertRaises(IOError): | 123 with self.assertRaises(IOError): |
| 148 _response = replies.next() | 124 _response = replies.next() |
| 149 | 125 |
| 150 def testFetchThrowsCorrectExceptionOnStreamingGrpcFailure(self): | 126 def testFetchThrowsCorrectExceptionOnStreamingGrpcFailure(self): |
| 151 """Fetch: if something goes wrong in gRPC, we throw an IOError""" | 127 """Fetch: if something goes wrong in gRPC, we throw an IOError""" |
| 152 def FetchBlobs(self, request, timeout=None): | 128 def Read(self, request, timeout=None): |
| 153 del timeout | 129 del timeout |
| 154 self.request = request | 130 self.request = request |
| 155 response = isolate_storage.isolate_bot_pb2.FetchBlobsReply() | 131 response = isolate_storage.bytestream_pb2.ReadResponse() |
| 156 response.status.succeeded = True | |
| 157 for i in range(0, 3): | 132 for i in range(0, 3): |
| 158 if i is 2: | 133 if i is 2: |
| 159 raise isolate_storage.grpc.RpcError( | 134 raise isolate_storage.grpc.RpcError( |
| 160 'proxy died during fetch stream :(') | 135 'proxy died during fetch stream :(') |
| 161 response.data.data = str(i) | 136 response.data = str(i) |
| 162 response.data.offset = i | |
| 163 yield response | 137 yield response |
| 164 self.mock(FileServiceStubMock, 'FetchBlobs', FetchBlobs) | 138 self.mock(ByteStreamStubMock, 'Read', Read) |
| 165 | 139 |
| 166 s = self.get_server() | 140 s = self.get_server() |
| 167 with self.assertRaises(IOError): | 141 with self.assertRaises(IOError): |
| 168 for _response in s.fetch('abc123'): | 142 for _response in s.fetch('abc123'): |
| 169 pass | 143 pass |
| 170 | 144 |
| 171 def testPushHappySingleSmall(self): | 145 def testPushHappySingleSmall(self): |
| 172 """Push: send one chunk of small data""" | 146 """Push: send one chunk of small data""" |
| 173 s = self.get_server() | 147 s = self.get_server() |
| 174 i = isolate_storage.Item(digest='abc123', size=4) | 148 i = isolate_storage.Item(digest='abc123', size=4) |
| 175 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') | 149 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') |
| 176 requests = s._stub.popPushRequests() | 150 requests = s._stub.popPushRequests() |
| 177 self.assertEqual(1, len(requests)) | 151 self.assertEqual(1, len(requests)) |
| 178 self.assertEqual(binascii.unhexlify('abc123'), | 152 m = re.search('client/bob/uploads/.*/blobs/abc123/4', |
| 179 requests[0].data.digest.digest) | 153 requests[0].resource_name) |
| 180 self.assertEqual(4, requests[0].data.digest.size_bytes) | 154 self.assertTrue(m) |
| 181 self.assertEqual('1234', requests[0].data.data) | 155 self.assertEqual('1234', requests[0].data) |
| 156 self.assertEqual(0, requests[0].write_offset) |
| 157 self.assertTrue(requests[0].finish_write) |
| 182 | 158 |
| 183 def testPushHappySingleBig(self): | 159 def testPushHappySingleBig(self): |
| 184 """Push: send one chunk of big data by splitting it into two""" | 160 """Push: send one chunk of big data by splitting it into two""" |
| 185 self.mock(isolate_storage, 'NET_IO_FILE_CHUNK', 3) | 161 self.mock(isolate_storage, 'NET_IO_FILE_CHUNK', 3) |
| 186 s = self.get_server() | 162 s = self.get_server() |
| 187 i = isolate_storage.Item(digest='abc123', size=4) | 163 i = isolate_storage.Item(digest='abc123', size=4) |
| 188 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') | 164 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') |
| 189 requests = s._stub.popPushRequests() | 165 requests = s._stub.popPushRequests() |
| 190 self.assertEqual(2, len(requests)) | 166 self.assertEqual(2, len(requests)) |
| 191 self.assertEqual(binascii.unhexlify('abc123'), | 167 m = re.search('client/bob/uploads/.*/blobs/abc123/4', |
| 192 requests[0].data.digest.digest) | 168 requests[0].resource_name) |
| 193 self.assertEqual(4, requests[0].data.digest.size_bytes) | 169 self.assertTrue(m) |
| 194 self.assertEqual('123', requests[0].data.data) | 170 self.assertEqual('123', requests[0].data) |
| 195 self.assertFalse(requests[1].data.HasField('digest')) | 171 self.assertEqual(0, requests[0].write_offset) |
| 196 self.assertEqual('4', requests[1].data.data) | 172 self.assertFalse(requests[0].finish_write) |
| 173 self.assertEqual('4', requests[1].data) |
| 174 self.assertEqual(3, requests[1].write_offset) |
| 175 self.assertTrue(requests[1].finish_write) |
| 197 | 176 |
| 198 def testPushHappyMultiSmall(self): | 177 def testPushHappyMultiSmall(self): |
| 199 """Push: sends multiple small chunks""" | 178 """Push: sends multiple small chunks""" |
| 200 s = self.get_server() | 179 s = self.get_server() |
| 201 i = isolate_storage.Item(digest='abc123', size=4) | 180 i = isolate_storage.Item(digest='abc123', size=4) |
| 202 s.push(i, isolate_storage._IsolateServerGrpcPushState(), ['12', '34']) | 181 s.push(i, isolate_storage._IsolateServerGrpcPushState(), ['12', '34']) |
| 203 requests = s._stub.popPushRequests() | 182 requests = s._stub.popPushRequests() |
| 204 self.assertEqual(2, len(requests)) | 183 self.assertEqual(2, len(requests)) |
| 205 self.assertEqual(binascii.unhexlify('abc123'), | 184 m = re.search('client/bob/uploads/.*/blobs/abc123/4', |
| 206 requests[0].data.digest.digest) | 185 requests[0].resource_name) |
| 207 self.assertEqual(4, requests[0].data.digest.size_bytes) | 186 self.assertTrue(m) |
| 208 self.assertEqual('12', requests[0].data.data) | 187 self.assertEqual('12', requests[0].data) |
| 209 self.assertFalse(requests[1].data.HasField('digest')) | 188 self.assertEqual(0, requests[0].write_offset) |
| 210 self.assertEqual('34', requests[1].data.data) | 189 self.assertFalse(requests[0].finish_write) |
| 190 self.assertEqual('34', requests[1].data) |
| 191 self.assertEqual(2, requests[1].write_offset) |
| 192 self.assertTrue(requests[1].finish_write) |
| 211 | 193 |
| 212 def testPushHappyMultiBig(self): | 194 def testPushHappyMultiBig(self): |
| 213 """Push: sends multiple chunks, each of which have to be split""" | 195 """Push: sends multiple chunks, each of which have to be split""" |
| 214 self.mock(isolate_storage, 'NET_IO_FILE_CHUNK', 2) | 196 self.mock(isolate_storage, 'NET_IO_FILE_CHUNK', 2) |
| 215 s = self.get_server() | 197 s = self.get_server() |
| 216 i = isolate_storage.Item(digest='abc123', size=6) | 198 i = isolate_storage.Item(digest='abc123', size=6) |
| 217 s.push(i, isolate_storage._IsolateServerGrpcPushState(), ['123', '456']) | 199 s.push(i, isolate_storage._IsolateServerGrpcPushState(), ['123', '456']) |
| 218 requests = s._stub.popPushRequests() | 200 requests = s._stub.popPushRequests() |
| 219 self.assertEqual(4, len(requests)) | 201 self.assertEqual(4, len(requests)) |
| 220 self.assertEqual(binascii.unhexlify('abc123'), | 202 m = re.search('client/bob/uploads/.*/blobs/abc123/6', |
| 221 requests[0].data.digest.digest) | 203 requests[0].resource_name) |
| 222 self.assertEqual(6, requests[0].data.digest.size_bytes) | 204 self.assertTrue(m) |
| 223 self.assertEqual('12', requests[0].data.data) | 205 self.assertEqual(0, requests[0].write_offset) |
| 224 self.assertFalse(requests[1].data.HasField('digest')) | 206 self.assertEqual('12', requests[0].data) |
| 225 self.assertEqual('3', requests[1].data.data) | 207 self.assertFalse(requests[0].finish_write) |
| 226 self.assertEqual('45', requests[2].data.data) | 208 self.assertEqual(2, requests[1].write_offset) |
| 227 self.assertEqual('6', requests[3].data.data) | 209 self.assertEqual('3', requests[1].data) |
| 210 self.assertFalse(requests[1].finish_write) |
| 211 self.assertEqual(3, requests[2].write_offset) |
| 212 self.assertEqual('45', requests[2].data) |
| 213 self.assertFalse(requests[2].finish_write) |
| 214 self.assertEqual(5, requests[3].write_offset) |
| 215 self.assertEqual('6', requests[3].data) |
| 216 self.assertTrue(requests[3].finish_write) |
| 228 | 217 |
| 229 def testPushHappyZeroLengthBlob(self): | 218 def testPushHappyZeroLengthBlob(self): |
| 230 """Push: send a zero-length blob""" | 219 """Push: send a zero-length blob""" |
| 231 s = self.get_server() | 220 s = self.get_server() |
| 232 i = isolate_storage.Item(digest='abc123', size=0) | 221 i = isolate_storage.Item(digest='abc123', size=0) |
| 233 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '') | 222 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '') |
| 234 requests = s._stub.popPushRequests() | 223 requests = s._stub.popPushRequests() |
| 235 self.assertEqual(1, len(requests)) | 224 self.assertEqual(1, len(requests)) |
| 236 self.assertEqual(binascii.unhexlify('abc123'), | 225 m = re.search('client/bob/uploads/.*/blobs/abc123/0', |
| 237 requests[0].data.digest.digest) | 226 requests[0].resource_name) |
| 238 self.assertEqual(0, requests[0].data.digest.size_bytes) | 227 self.assertTrue(m) |
| 239 self.assertEqual('', requests[0].data.data) | 228 self.assertEqual(0, requests[0].write_offset) |
| 229 self.assertEqual('', requests[0].data) |
| 230 self.assertTrue(requests[0].finish_write) |
| 240 | 231 |
| 241 def testPushThrowsOnFailure(self): | 232 def testPushThrowsOnFailure(self): |
| 242 """Push: if something goes wrong in Isolate, we throw an exception""" | 233 """Push: if something goes wrong in Isolate, we throw an exception""" |
| 243 def PushBlobs(self, request, timeout=None): | 234 def Write(self, request, timeout=None): |
| 244 del request, timeout, self | 235 del request, timeout, self |
| 245 response = isolate_storage.isolate_bot_pb2.PushBlobsReply() | 236 raiseError(isolate_storage.grpc.StatusCode.INTERNAL_ERROR) |
| 246 response.status.succeeded = False | 237 self.mock(ByteStreamStubMock, 'Write', Write) |
| 247 return response | |
| 248 self.mock(FileServiceStubMock, 'PushBlobs', PushBlobs) | |
| 249 | 238 |
| 250 s = self.get_server() | 239 s = self.get_server() |
| 251 i = isolate_storage.Item(digest='abc123', size=0) | 240 i = isolate_storage.Item(digest='abc123', size=0) |
| 252 with self.assertRaises(IOError): | 241 with self.assertRaises(IOError): |
| 253 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') | 242 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') |
| 254 | 243 |
| 255 def testPushThrowsCorrectExceptionOnGrpcFailure(self): | 244 def testPushThrowsCorrectExceptionOnGrpcFailure(self): |
| 256 """Push: if something goes wrong in Isolate, we throw an exception""" | 245 """Push: if something goes wrong in Isolate, we throw an exception""" |
| 257 def PushBlobs(_self, _request, timeout=None): | 246 def Write(_self, _request, timeout=None): |
| 258 del timeout | 247 del timeout |
| 259 raise isolate_storage.grpc.RpcError('proxy died during push :(') | 248 raise isolate_storage.grpc.RpcError('proxy died during push :(') |
| 260 self.mock(FileServiceStubMock, 'PushBlobs', PushBlobs) | 249 self.mock(ByteStreamStubMock, 'Write', Write) |
| 261 | 250 |
| 262 s = self.get_server() | 251 s = self.get_server() |
| 263 i = isolate_storage.Item(digest='abc123', size=0) | 252 i = isolate_storage.Item(digest='abc123', size=0) |
| 264 with self.assertRaises(IOError): | 253 with self.assertRaises(IOError): |
| 265 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') | 254 s.push(i, isolate_storage._IsolateServerGrpcPushState(), '1234') |
| 266 | 255 |
| 267 def testContainsHappySimple(self): | |
| 268 """Contains: basic sanity check""" | |
| 269 items = [] | |
| 270 for i in range(0, 3): | |
| 271 digest = ''.join(['a', str(i)]) | |
| 272 i = isolate_storage.Item(digest=digest, size=1) | |
| 273 items.append(i) | |
| 274 s = self.get_server() | |
| 275 response = s.contains(items) | |
| 276 self.assertEqual(0, len(response)) | |
| 277 requests = s._stub.popContainsRequests() | |
| 278 self.assertEqual(3, len(requests[0].digest)) | |
| 279 self.assertEqual('\xa0', requests[0].digest[0].digest) | |
| 280 self.assertEqual('\xa1', requests[0].digest[1].digest) | |
| 281 self.assertEqual('\xa2', requests[0].digest[2].digest) | |
| 282 | |
| 283 def testContainsMissingSimple(self): | |
| 284 """Contains: the digests are missing""" | |
| 285 def Contains(self, request, timeout=None): | |
| 286 del timeout, self | |
| 287 response = isolate_storage.isolate_bot_pb2.ContainsReply() | |
| 288 response.status.succeeded = False | |
| 289 response.status.error = ( | |
| 290 isolate_storage.isolate_bot_pb2.BlobStatus.MISSING_DIGEST) | |
| 291 for d in request.digest: | |
| 292 msg = response.status.missing_digest.add() | |
| 293 msg.CopyFrom(d) | |
| 294 return response | |
| 295 self.mock(FileServiceStubMock, 'Contains', Contains) | |
| 296 | |
| 297 items = [] | |
| 298 for i in range(0, 3): | |
| 299 digest = ''.join(['a', str(i)]) | |
| 300 i = isolate_storage.Item(digest=digest, size=1) | |
| 301 items.append(i) | |
| 302 s = self.get_server() | |
| 303 response = s.contains(items) | |
| 304 self.assertEqual(3, len(response)) | |
| 305 self.assertTrue(items[0] in response) | |
| 306 self.assertTrue(items[1] in response) | |
| 307 self.assertTrue(items[2] in response) | |
| 308 | |
| 309 def testContainsThrowsCorrectExceptionOnGrpcFailure(self): | |
| 310 """Contains: the digests are missing""" | |
| 311 def Contains(_self, _request, timeout=None): | |
| 312 del timeout | |
| 313 raise isolate_storage.grpc.RpcError('proxy died during contains :(') | |
| 314 self.mock(FileServiceStubMock, 'Contains', Contains) | |
| 315 | |
| 316 items = [] | |
| 317 for i in range(0, 3): | |
| 318 digest = ''.join(['a', str(i)]) | |
| 319 i = isolate_storage.Item(digest=digest, size=1) | |
| 320 items.append(i) | |
| 321 s = self.get_server() | |
| 322 with self.assertRaises(IOError): | |
| 323 _response = s.contains(items) | |
| 324 | |
| 325 | 256 |
| 326 if __name__ == '__main__': | 257 if __name__ == '__main__': |
| 327 if not isolate_storage.grpc: | 258 if not isolate_storage.grpc: |
| 328 # Don't print to stderr or return error code as this will | 259 # Don't print to stderr or return error code as this will |
| 329 # show up as a warning and fail in presubmit. | 260 # show up as a warning and fail in presubmit. |
| 330 print('gRPC could not be loaded; skipping tests') | 261 print('gRPC could not be loaded; skipping tests') |
| 331 sys.exit(0) | 262 sys.exit(0) |
| 332 isolate_storage.isolate_bot_pb2.FileServiceStub = FileServiceStubMock | 263 isolate_storage.bytestream_pb2.ByteStreamStub = ByteStreamStubMock |
| 333 test_utils.main() | 264 test_utils.main() |
| OLD | NEW |