• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 // Tests PPB_URLRequestInfo interface.
6 
7 #include "ppapi/tests/test_url_request.h"
8 
9 #include <string.h>
10 #include <string>
11 
12 #include "ppapi/c/ppb_file_io.h"
13 #include "ppapi/cpp/completion_callback.h"
14 #include "ppapi/cpp/file_io.h"
15 #include "ppapi/cpp/file_ref.h"
16 #include "ppapi/cpp/file_system.h"
17 #include "ppapi/cpp/instance.h"
18 #include "ppapi/cpp/var.h"
19 #include "ppapi/tests/test_utils.h"
20 #include "ppapi/tests/testing_instance.h"
21 
22 REGISTER_TEST_CASE(URLRequest);
23 
24 namespace {
25 // TODO(polina): move these to test_case.h/cc since other NaCl tests use them?
26 
27 const PP_Resource kInvalidResource = 0;
28 const PP_Instance kInvalidInstance = 0;
29 
30 // These should not exist.
31 // The bottom 2 bits are used to differentiate between different id types.
32 // 00 - module, 01 - instance, 10 - resource, 11 - var.
33 const PP_Instance kNotAnInstance = 0xFFFFF0;
34 const PP_Resource kNotAResource = 0xAAAAA0;
35 }
36 
TestURLRequest(TestingInstance * instance)37 TestURLRequest::TestURLRequest(TestingInstance* instance)
38     : TestCase(instance),
39       ppb_url_request_interface_(NULL),
40       ppb_url_loader_interface_(NULL),
41       ppb_url_response_interface_(NULL),
42       ppb_core_interface_(NULL),
43       ppb_var_interface_(NULL) {
44 }
45 
Init()46 bool TestURLRequest::Init() {
47   ppb_url_request_interface_ = static_cast<const PPB_URLRequestInfo*>(
48       pp::Module::Get()->GetBrowserInterface(PPB_URLREQUESTINFO_INTERFACE));
49   ppb_url_loader_interface_ = static_cast<const PPB_URLLoader*>(
50       pp::Module::Get()->GetBrowserInterface(PPB_URLLOADER_INTERFACE));
51   ppb_url_response_interface_ = static_cast<const PPB_URLResponseInfo*>(
52       pp::Module::Get()->GetBrowserInterface(PPB_URLRESPONSEINFO_INTERFACE));
53   ppb_core_interface_ = static_cast<const PPB_Core*>(
54       pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
55   ppb_var_interface_ = static_cast<const PPB_Var*>(
56       pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE));
57   if (!ppb_url_request_interface_)
58     instance_->AppendError("PPB_URLRequestInfo interface not available");
59   if (!ppb_url_response_interface_)
60     instance_->AppendError("PPB_URLResponseInfo interface not available");
61   if (!ppb_core_interface_)
62     instance_->AppendError("PPB_Core interface not available");
63   if (!ppb_var_interface_)
64     instance_->AppendError("PPB_Var interface not available");
65   if (!ppb_url_loader_interface_) {
66     instance_->AppendError("PPB_URLLoader interface not available");
67   }
68   return EnsureRunningOverHTTP();
69 }
70 
RunTests(const std::string & filter)71 void TestURLRequest::RunTests(const std::string& filter) {
72   RUN_TEST(CreateAndIsURLRequestInfo, filter);
73   RUN_TEST(SetProperty, filter);
74   RUN_TEST(AppendDataToBody, filter);
75   RUN_TEST(AppendFileToBody, filter);
76   RUN_TEST(Stress, filter);
77 }
78 
PP_MakeString(const char * s)79 PP_Var TestURLRequest::PP_MakeString(const char* s) {
80   return ppb_var_interface_->VarFromUtf8(s, strlen(s));
81 }
82 
83 // Tests
84 //   PP_Resource Create(PP_Instance instance)
85 //   PP_Bool IsURLRequestInfo(PP_Resource resource)
TestCreateAndIsURLRequestInfo()86 std::string TestURLRequest::TestCreateAndIsURLRequestInfo() {
87   // Create: Invalid / non-existent instance -> invalid resource.
88   ASSERT_EQ(ppb_url_request_interface_->Create(kInvalidInstance),
89             kInvalidResource);
90   ASSERT_EQ(ppb_url_request_interface_->Create(kNotAnInstance),
91             kInvalidResource);
92 
93   // Create: Valid instance -> valid resource.
94   PP_Resource url_request = ppb_url_request_interface_->Create(
95       instance_->pp_instance());
96   ASSERT_NE(url_request, kInvalidResource);
97 
98   // IsURLRequestInfo:
99   // Invalid / non-existent / non-URLRequestInfo resource -> false.
100   ASSERT_NE(PP_TRUE,
101             ppb_url_request_interface_->IsURLRequestInfo(kInvalidResource));
102   ASSERT_NE(PP_TRUE,
103             ppb_url_request_interface_->IsURLRequestInfo(kNotAResource));
104 
105   PP_Resource url_loader =
106       ppb_url_loader_interface_->Create(instance_->pp_instance());
107   ASSERT_NE(kInvalidResource, url_loader);
108 
109   ASSERT_NE(PP_TRUE, ppb_url_request_interface_->IsURLRequestInfo(url_loader));
110   ppb_url_loader_interface_->Close(url_loader);
111   ppb_core_interface_->ReleaseResource(url_loader);
112 
113   // IsURLRequestInfo: Current URLRequestInfo resource -> true.
114   std::string error;
115   if (PP_FALSE == ppb_url_request_interface_->IsURLRequestInfo(url_request))
116     error = "IsURLRequestInfo() failed with a current URLRequestInfo resource";
117 
118   // IsURLRequestInfo: Released URLRequestInfo resource -> false.
119   ppb_core_interface_->ReleaseResource(url_request);
120   ASSERT_NE(PP_TRUE, ppb_url_request_interface_->IsURLRequestInfo(url_request));
121 
122   return error;  // == PASS() if empty.
123 }
124 
125 // Tests
126 //  PP_Bool SetProperty(PP_Resource request,
127 //                      PP_URLRequestProperty property,
128 //                      struct PP_Var value);
TestSetProperty()129 std::string TestURLRequest::TestSetProperty() {
130   struct PropertyTestData {
131     PropertyTestData(PP_URLRequestProperty prop,
132                      const std::string& name,
133                      PP_Var value, PP_Bool expected) :
134         property(prop), property_name(name),
135         var(value), expected_value(expected) {
136       // var has ref count of 1 on creation.
137     }
138     PP_URLRequestProperty property;
139     std::string property_name;
140     PP_Var var;  // Instance owner is responsible for releasing this var.
141     PP_Bool expected_value;
142   };
143 
144   // All bool properties should accept PP_TRUE and PP_FALSE, while rejecting
145   // all other variable types.
146 #define TEST_BOOL(_name)                                              \
147     PropertyTestData(ID_STR(_name), PP_MakeBool(PP_TRUE), PP_TRUE),   \
148     PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_TRUE),  \
149     PropertyTestData(ID_STR(_name), PP_MakeUndefined(), PP_FALSE),    \
150     PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE),         \
151     PropertyTestData(ID_STR(_name), PP_MakeInt32(0), PP_FALSE),       \
152     PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE)
153 
154   // These property types are always invalid for string properties.
155 #define TEST_STRING_INVALID(_name)                                    \
156     PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE),         \
157     PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_FALSE), \
158     PropertyTestData(ID_STR(_name), PP_MakeInt32(0), PP_FALSE),       \
159     PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE)
160 
161 #define TEST_INT_INVALID(_name)                                         \
162     PropertyTestData(ID_STR(_name), PP_MakeUndefined(), PP_FALSE),      \
163     PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE),           \
164     PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_FALSE),   \
165     PropertyTestData(ID_STR(_name), PP_MakeString("notint"), PP_FALSE), \
166     PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE)
167 
168   // SetProperty accepts plenty of invalid values (malformed urls, negative
169   // thresholds, etc). Error checking is delayed until request opening (aka url
170   // loading).
171 #define ID_STR(arg) arg, #arg
172     PropertyTestData test_data[] = {
173       TEST_BOOL(PP_URLREQUESTPROPERTY_STREAMTOFILE),
174       TEST_BOOL(PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS),
175       TEST_BOOL(PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS),
176       TEST_BOOL(PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS),
177       TEST_BOOL(PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS),
178       TEST_BOOL(PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS),
179       TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_URL),
180       TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_METHOD),
181       TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_HEADERS),
182       TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
183       TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
184       TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
185       TEST_INT_INVALID(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
186       TEST_INT_INVALID(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
187       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
188                        PP_MakeString("http://www.google.com"), PP_TRUE),
189       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
190                        PP_MakeString("foo.jpg"), PP_TRUE),
191       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
192                        PP_MakeString("GET"), PP_TRUE),
193       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
194                        PP_MakeString("POST"), PP_TRUE),
195       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
196                        PP_MakeString("Accept: text/plain"), PP_TRUE),
197       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
198                        PP_MakeString(""), PP_TRUE),
199       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
200                        PP_MakeString("http://www.google.com"), PP_TRUE),
201       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
202                        PP_MakeString(""), PP_TRUE),
203       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
204                        PP_MakeUndefined(), PP_TRUE),
205       PropertyTestData(
206           ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
207           PP_MakeString("base64"), PP_TRUE),
208       PropertyTestData(
209           ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
210           PP_MakeString(""), PP_TRUE),
211       PropertyTestData(
212           ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
213           PP_MakeUndefined(), PP_TRUE),
214       PropertyTestData(
215           ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
216           PP_MakeString("My Crazy Plugin"), PP_TRUE),
217       PropertyTestData(
218           ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
219           PP_MakeString(""), PP_TRUE),
220       PropertyTestData(
221           ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
222           PP_MakeUndefined(), PP_TRUE),
223       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
224                        PP_MakeUndefined(), PP_FALSE),
225       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
226                        PP_MakeUndefined(), PP_FALSE),
227       PropertyTestData(
228           ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
229           PP_MakeString("Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA=="),
230           PP_TRUE),
231       PropertyTestData(
232           ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
233           PP_MakeString("Accept-Encoding: *\n"
234                         "Accept-Charset: iso-8859-5, unicode-1-1;q=0.8"),
235           PP_TRUE),
236       PropertyTestData(
237           ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
238           PP_MakeInt32(0), PP_TRUE),
239       PropertyTestData(
240           ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
241           PP_MakeInt32(100), PP_TRUE),
242       PropertyTestData(
243           ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
244           PP_MakeInt32(0), PP_TRUE),
245       PropertyTestData(
246           ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
247           PP_MakeInt32(100), PP_TRUE),
248       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
249                        PP_MakeString("::::::::::::"), PP_TRUE),
250       PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
251           PP_MakeString("INVALID"), PP_TRUE),
252       PropertyTestData(
253           ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
254           PP_MakeString("invalid"), PP_TRUE),
255       PropertyTestData(
256           ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
257           PP_MakeInt32(-100), PP_TRUE),
258       PropertyTestData(
259           ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
260           PP_MakeInt32(-100), PP_TRUE),
261 
262     };
263   std::string error;
264 
265   PP_Resource url_request = ppb_url_request_interface_->Create(
266       instance_->pp_instance());
267   if (url_request == kInvalidResource)
268     error = "Failed to create a URLRequestInfo";
269 
270   // Loop over all test data even if we encountered an error to release vars.
271   for (size_t i = 0;
272        i < sizeof(test_data) / sizeof(test_data[0]);
273        ++i) {
274     if (error.empty() && test_data[i].expected_value !=
275         ppb_url_request_interface_->SetProperty(url_request,
276                                                 test_data[i].property,
277                                                 test_data[i].var)) {
278       pp::Var var(pp::Var::DontManage(), test_data[i].var);
279       error = std::string("Setting property ") +
280           test_data[i].property_name + " to " + var.DebugString() +
281           " did not return " + (test_data[i].expected_value ? "True" : "False");
282       error = test_data[i].property_name;
283     }
284     ppb_var_interface_->Release(test_data[i].var);
285   }
286 
287   ppb_core_interface_->ReleaseResource(url_request);
288   return error;  // == PASS() if empty.
289 }
290 
LoadAndCompareBody(PP_Resource url_request,const std::string & expected_body)291 std::string TestURLRequest::LoadAndCompareBody(
292     PP_Resource url_request, const std::string& expected_body) {
293   TestCompletionCallback callback(instance_->pp_instance(), PP_REQUIRED);
294 
295   PP_Resource url_loader =
296       ppb_url_loader_interface_->Create(instance_->pp_instance());
297   ASSERT_NE(kInvalidResource, url_loader);
298 
299   callback.WaitForResult(ppb_url_loader_interface_->Open(
300       url_loader, url_request,
301       callback.GetCallback().pp_completion_callback()));
302   CHECK_CALLBACK_BEHAVIOR(callback);
303   ASSERT_EQ(PP_OK, callback.result());
304 
305   std::string error;
306   PP_Resource url_response =
307       ppb_url_loader_interface_->GetResponseInfo(url_loader);
308   if (url_response == kInvalidResource) {
309     error = "PPB_URLLoader::GetResponseInfo() returned invalid resource";
310   } else {
311     PP_Var status = ppb_url_response_interface_->GetProperty(
312         url_response, PP_URLRESPONSEPROPERTY_STATUSCODE);
313     if (status.type != PP_VARTYPE_INT32 && status.value.as_int != 200)
314       error = ReportError("PPB_URLLoader::Open() status", status.value.as_int);
315 
316     std::string actual_body;
317     for (; error.empty();) {  // Read the entire body in this loop.
318       const size_t kBufferSize = 32;
319       char buf[kBufferSize];
320       callback.WaitForResult(ppb_url_loader_interface_->ReadResponseBody(
321           url_loader, buf, kBufferSize,
322           callback.GetCallback().pp_completion_callback()));
323       if (callback.failed())
324         error.assign(callback.errors());
325       else if (callback.result() < PP_OK)
326         error.assign(ReportError("PPB_URLLoader::ReadResponseBody()",
327                                  callback.result()));
328       if (callback.result() <= PP_OK || callback.failed())
329         break;
330       actual_body.append(buf, callback.result());
331     }
332     if (actual_body != expected_body)
333       error = "PPB_URLLoader::ReadResponseBody() read unexpected response.";
334   }
335   ppb_core_interface_->ReleaseResource(url_response);
336 
337   ppb_url_loader_interface_->Close(url_loader);
338   ppb_core_interface_->ReleaseResource(url_loader);
339   return error;
340 }
341 
342 // Tests
343 //   PP_Bool AppendDataToBody(
344 //       PP_Resource request, const void* data, uint32_t len);
TestAppendDataToBody()345 std::string TestURLRequest::TestAppendDataToBody() {
346   PP_Resource url_request = ppb_url_request_interface_->Create(
347       instance_->pp_instance());
348   ASSERT_NE(url_request, kInvalidResource);
349 
350   std::string postdata("sample postdata");
351   PP_Var post_string_var = PP_MakeString("POST");
352   PP_Var echo_string_var = PP_MakeString("/echo");
353 
354   // NULL pointer causes a crash. In general PPAPI implementation does not
355   // test for NULL because they are just a special case of bad pointers that
356   // are not detectable if set to point to an object that does not exist.
357 
358   // Invalid resource should fail.
359   ASSERT_EQ(PP_FALSE, ppb_url_request_interface_->AppendDataToBody(
360       kInvalidResource, postdata.data(), postdata.length()));
361 
362   // Append data and POST to echoing web server.
363   ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
364       url_request, PP_URLREQUESTPROPERTY_METHOD, post_string_var));
365   ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
366       url_request, PP_URLREQUESTPROPERTY_URL, echo_string_var));
367 
368   // Append data to body and verify the body is what we expect.
369   ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->AppendDataToBody(
370       url_request, postdata.data(), postdata.length()));
371   std::string error = LoadAndCompareBody(url_request, postdata);
372 
373   ppb_var_interface_->Release(post_string_var);
374   ppb_var_interface_->Release(echo_string_var);
375   ppb_core_interface_->ReleaseResource(url_request);
376   return error;  // == PASS() if empty.
377 }
378 
TestAppendFileToBody()379 std::string TestURLRequest::TestAppendFileToBody() {
380   PP_Resource url_request = ppb_url_request_interface_->Create(
381       instance_->pp_instance());
382   ASSERT_NE(url_request, kInvalidResource);
383 
384   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
385 
386   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
387   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
388   CHECK_CALLBACK_BEHAVIOR(callback);
389   ASSERT_EQ(PP_OK, callback.result());
390 
391   pp::FileRef ref(file_system, "/test_file");
392   pp::FileIO io(instance_);
393   callback.WaitForResult(io.Open(ref,
394                                  PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE,
395                                  callback.GetCallback()));
396   CHECK_CALLBACK_BEHAVIOR(callback);
397   ASSERT_EQ(PP_OK, callback.result());
398 
399   std::string append_data = "hello\n";
400   callback.WaitForResult(io.Write(0,
401                                   append_data.c_str(),
402                                   append_data.size(),
403                                   callback.GetCallback()));
404   CHECK_CALLBACK_BEHAVIOR(callback);
405   ASSERT_EQ(static_cast<int32_t>(append_data.size()), callback.result());
406 
407   PP_Var post_string_var = PP_MakeString("POST");
408   PP_Var echo_string_var = PP_MakeString("/echo");
409 
410   // NULL pointer causes a crash. In general PPAPI implementation does not
411   // test for NULL because they are just a special case of bad pointers that
412   // are not detectable if set to point to an object that does not exist.
413 
414   // Invalid resource should fail.
415   ASSERT_EQ(PP_FALSE, ppb_url_request_interface_->AppendFileToBody(
416       kInvalidResource, ref.pp_resource(), 0, -1, 0));
417 
418   // Append data and POST to echoing web server.
419   ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
420       url_request, PP_URLREQUESTPROPERTY_METHOD, post_string_var));
421   ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
422       url_request, PP_URLREQUESTPROPERTY_URL, echo_string_var));
423 
424   // Append file to body and verify the body is what we expect.
425   ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->AppendFileToBody(
426       url_request, ref.pp_resource(), 0, -1, 0));
427   std::string error = LoadAndCompareBody(url_request, append_data);
428 
429   ppb_var_interface_->Release(post_string_var);
430   ppb_var_interface_->Release(echo_string_var);
431   ppb_core_interface_->ReleaseResource(url_request);
432   return error;  // == PASS() if empty.
433 }
434 
435 // Allocates and manipulates a large number of resources.
TestStress()436 std::string TestURLRequest::TestStress() {
437   const int kManyResources = 500;
438   PP_Resource url_request_info[kManyResources];
439 
440   std::string error;
441   int num_created = kManyResources;
442   for (int i = 0; i < kManyResources; i++) {
443     url_request_info[i] = ppb_url_request_interface_->Create(
444         instance_->pp_instance());
445     if (url_request_info[i] == kInvalidResource) {
446       error = "Create() failed";
447     } else if (PP_FALSE == ppb_url_request_interface_->IsURLRequestInfo(
448         url_request_info[i])) {
449       error = "IsURLRequestInfo() failed";
450     } else if (PP_FALSE == ppb_url_request_interface_->SetProperty(
451         url_request_info[i],
452         PP_URLREQUESTPROPERTY_STREAMTOFILE,
453         PP_MakeBool(PP_FALSE))) {
454       error = "SetProperty() failed";
455     }
456     if (!error.empty()) {
457       num_created = i + 1;
458       break;
459     }
460   }
461   for (int i = 0; i < num_created; i++) {
462     ppb_core_interface_->ReleaseResource(url_request_info[i]);
463     if (PP_TRUE ==
464         ppb_url_request_interface_->IsURLRequestInfo(url_request_info[i]))
465       error = "IsURLREquestInfo() succeeded after release";
466   }
467   return error;  // == PASS() if empty.
468 }
469