Hello andreip,
I'd like you to do a code review. Please execute
g4 diff -c 9152568
or point your web browser to
http://mondrian/9152568
to review the following code:
Change 9152568 by [EMAIL PROTECTED] on 2008/11/25 15:17:36 *pending*
Adds maximumAge option to Geolocation API.
Also disallows empty string for gearsAddressLanguage and removes the
unused 'repeats' property fromTestParseGeolocationOptions().
R=andreip
[EMAIL PROTECTED]
DELTA=474 (359 added, 26 deleted, 89 changed)
OCL=9152568
Affected files ...
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation.cc#67
edit
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation.h#31
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc#36
edit
... //depot/googleclient/gears/opensource/gears/sdk/api_geolocation.html#12 edit
...
//depot/googleclient/gears/opensource/gears/test/testcases/geolocation_tests.js#5
edit
...
//depot/googleclient/gears/opensource/gears/test/testcases/internal_geolocation_tests.js#13
edit
474 delta lines: 359 added, 26 deleted, 89 changed
Also consider running:
g4 lint -c 9152568
which verifies that the changelist doesn't introduce new style violations.
If you can't do the review, please let me know as soon as possible. During
your review, please ensure that all new code has corresponding unit tests and
that existing unit tests are updated appropriately. Visit
http://www/eng/code_review.html for more information.
This is a semiautomated message from "g4 mail". Complaints or suggestions?
Mail [EMAIL PROTECTED]
Change 9152568 by [EMAIL PROTECTED] on 2008/11/25 15:17:36 *pending*
Adds maximumAge option to Geolocation API.
Also disallows empty string for gearsAddressLanguage and removes the
unused 'repeats' property fromTestParseGeolocationOptions().
Affected files ...
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation.cc#67
edit
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation.h#31
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc#36
edit
... //depot/googleclient/gears/opensource/gears/sdk/api_geolocation.html#12 edit
...
//depot/googleclient/gears/opensource/gears/test/testcases/geolocation_tests.js#5
edit
...
//depot/googleclient/gears/opensource/gears/test/testcases/internal_geolocation_tests.js#13
edit
==== //depot/googleclient/gears/opensource/gears/geolocation/geolocation.cc#67
-
c:\MyDocs\Gears4/googleclient/gears/opensource/gears/geolocation/geolocation.cc
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/geolocation.cc
2008-11-27 12:10:19.000000000 +0000
+++ googleclient/gears/opensource/gears/geolocation/geolocation.cc
2008-11-27 12:05:11.000000000 +0000
@@ -39,6 +39,7 @@
#include "gears/geolocation/geolocation.h"
+#include <limits>
#include <math.h>
#include "gears/base/common/js_runner.h"
#include "gears/base/common/permissions_manager.h"
@@ -53,6 +54,7 @@
// API options constants.
static const char16 *kEnableHighAccuracy = STRING16(L"enableHighAccuracy");
+static const char16 *kMaximumAge = STRING16(L"maximumAge");
static const char16 *kTimeout = STRING16(L"timeout");
static const char16 *kGearsRequestAddress = STRING16(L"gearsRequestAddress");
static const char16 *kGearsAddressLanguage = STRING16(L"gearsAddressLanguage");
@@ -448,7 +450,7 @@
reinterpret_cast<const FixRequestIdNotificationData*>(data);
TimeoutExpiredImpl(timeout_expired_data->fix_request_id);
} else if (topic_string == kCallbackRequiredObserverTopic) {
- // The timeout for a watch callback expired.
+ // The timeout for a callback expired.
const FixRequestIdNotificationData *callback_required_data =
reinterpret_cast<const FixRequestIdNotificationData*>(data);
CallbackRequiredImpl(callback_required_data->fix_request_id);
@@ -481,19 +483,30 @@
// Non-API methods
-void GearsGeolocation::GetPositionFix(JsCallContext *context, bool repeats) {
- ASSERT_SINGLE_THREAD();
-
- // Get the arguments.
- std::vector<std::string16> urls;
- scoped_ptr<FixRequestInfo> info(new FixRequestInfo());
- if (!ParseArguments(context, repeats, &urls, info.get())) {
- assert(context->is_exception_set());
- return;
- }
-
- // Add the providers. The lifetime of the providers is handled by the
location
- // provider pool, through Register and Unregister.
+bool GearsGeolocation::IsCachedPositionSuitable(int maximum_age) {
+ ASSERT_SINGLE_THREAD();
+ assert(maximum_age != 0);
+
+ if (!last_position_.IsGoodFix()) {
+ return false;
+ }
+ if (maximum_age == -1) { // Encoding for infinity.
+ return true;
+ }
+ assert(maximum_age > 0);
+ // maximum_age is limited to 2^31 milliseconds. The current (November 2008)
+ // timestamp is around 2^40, so there's no danger of underflow.
+ int64 oldest_timestamp = GetCurrentTimeMillis() - maximum_age;
+ assert(oldest_timestamp > 0);
+ return last_position_.timestamp > oldest_timestamp;
+}
+
+void GearsGeolocation::AddProvidersToRequest(std::vector<std::string16> &urls,
+ FixRequestInfo *info) {
+ assert(info);
+
+ // The lifetime of the providers is handled by the location provider pool,
+ // through Register and Unregister.
std::string16 host_name = EnvPageSecurityOrigin().host();
LocationProviderPool *pool = LocationProviderPool::GetInstance();
@@ -558,25 +571,97 @@
assert(network_provider);
info->providers.push_back(network_provider);
}
-
- // If this fix has no providers, throw an exception and quit.
- if (info->providers.empty()) {
- context->SetException(STRING16(L"Fix request has no location providers."));
+}
+
+void GearsGeolocation::GetPositionFix(JsCallContext *context, bool repeats) {
+ ASSERT_SINGLE_THREAD();
+
+ // The fact that Gears allows the caller to specify zero location providers
+ // makes the logic around the use of a cached position with maximumAge a
+ // little complex.
+ // - maximumAge is zero. The cached position is not used. We throw an
+ // exception if no location providers are specified, or use the providers
to
+ // obtain a new fix otherwise. (maximumAge defaults to zero, so this
+ // preserves the behaviour prior to the addition of maximumAge).
+ // - maximumAge is non-zero and location providers are specified.
+ // - One-shot request. We call the success callback immediately if the
+ // cached position is suitable, or use the providers to obtain a new fix
+ // otherwise.
+ // - Repeating request. We call the success callback immediately if the
+ // cached position is suitable. In any case we then use the providers to
+ // obtain a new fix.
+ // - maximumAge is non-zero and no location providers are specified. We call
+ // the success callback immediately if the cached position is suitable, or
+ // call the error callback immediately otherwise.
+
+ // Get the arguments.
+ std::vector<std::string16> urls;
+ scoped_ptr<FixRequestInfo> info(new FixRequestInfo());
+ if (!ParseArguments(context, &urls, info.get())) {
+ assert(context->is_exception_set());
return;
}
-
- // If this is a non-repeating request, hint to all providers that new data is
- // required ASAP.
- if (!info->repeats) {
- for (ProviderVector::iterator iter = info->providers.begin();
- iter != info->providers.end();
- ++iter) {
- (*iter)->UpdatePosition();
- }
- }
-
- // Record this fix. This updates the map of providers and fix requests. The
- // map takes ownership of the info structure on success.
+ info->repeats = repeats;
+
+ // This is purely an optimisation.
+ bool is_useful_to_add_location_providers = true;
+ // If the fix request is a one-shot request and will call back immediately,
+ // there's no point in adding any providers.
+ if (!info->repeats &&
+ info->maximum_age != 0 &&
+ IsCachedPositionSuitable(info->maximum_age)) {
+ is_useful_to_add_location_providers = false;
+ }
+
+ // Add the location providers.
+ if (is_useful_to_add_location_providers) {
+ AddProvidersToRequest(urls, info.get());
+
+ // If this is a non-repeating request, hint to all providers that new data
+ // is required ASAP.
+ if (!info->repeats) {
+ for (ProviderVector::iterator iter = info->providers.begin();
+ iter != info->providers.end();
+ ++iter) {
+ (*iter)->UpdatePosition();
+ }
+ }
+ }
+
+ // Check whether we need to call back immediately.
+ bool should_call_back_immediately = false;
+ Position position_to_call_back;
+ if (info->maximum_age == 0) {
+ // If this fix has no providers, throw an exception and quit.
+ if (info->providers.empty()) {
+ context->SetException(
+ STRING16(L"Fix request has no location providers."));
+ return;
+ }
+ } else {
+ // Non-zero maximumAge
+ if (info->providers.empty()) {
+ should_call_back_immediately = true;
+ if (IsCachedPositionSuitable(info->maximum_age)) {
+ position_to_call_back = last_position_;
+ } else {
+ position_to_call_back.error_code =
+ Position::ERROR_CODE_POSITION_UNAVAILABLE;
+ position_to_call_back.error_message =
+ STRING16(L"No suitable cached position available.");
+ }
+ } else {
+ if (IsCachedPositionSuitable(info->maximum_age)) {
+ should_call_back_immediately = true;
+ position_to_call_back = last_position_;
+ }
+ }
+ }
+ assert(is_useful_to_add_location_providers || should_call_back_immediately);
+
+ // Record this fix. This updates the map of providers for this fix requests
+ // and provides a fix request ID. The map takes ownership of the info
+ // structure on success.
int fix_request_id = 0;
if (!RecordNewFixRequest(info.get(), &fix_request_id)) {
context->SetException(STRING16(L"Exceeded maximum number of fix "
@@ -585,8 +670,22 @@
}
assert(fix_request_id != 0);
- // Start the timeout timer for this fix request.
- StartTimeoutTimer(fix_request_id);
+ // Invoke the callback. We use a message to do so asynchronously.
+ if (should_call_back_immediately) {
+ assert(position_to_call_back.IsInitialized());
+ assert(!info->success_callback_timer.get());
+
+ info->pending_position = position_to_call_back;
+ info->success_callback_timer.reset(new TimedMessage(
+ 0, // Call back immediately
+ kCallbackRequiredObserverTopic,
+ new FixRequestIdNotificationData(this, fix_request_id)));
+ }
+
+ // If we have providers, start the timeout timer for this fix request.
+ if (!info->providers.empty()) {
+ StartTimeoutTimer(fix_request_id);
+ }
// Set the return value if this fix repeats.
if (info->repeats) {
@@ -658,9 +757,9 @@
} else {
// Start an asynchronous timer which will post a message back to this
// thread once the minimum time period has elapsed.
- MakeFutureSuccessCallback(static_cast<int>(time_remaining),
- fix_request_id,
- position);
+ MakeFutureWatchSuccessCallback(static_cast<int>(time_remaining),
+ fix_request_id,
+ position);
}
}
}
@@ -803,6 +902,9 @@
// Update the last known position, which is the best position estimate we
// currently have.
+ // TODO(steveblock): Rather than updating when there is significant movement,
+ // we should probably update (though not neccessarily call back) if there is
+ // any movmement, provided the accuracy is not worse.
if (IsNewPositionMovement(last_position_, position) ||
IsNewPositionMoreAccurate(last_position_, position) ||
IsNewPositionMoreTimely(last_position_, position)) {
@@ -909,12 +1011,24 @@
void GearsGeolocation::CallbackRequiredImpl(int fix_request_id) {
ASSERT_SINGLE_THREAD();
- // Check that the fix request still exists
+ // Check that the fix request still exists.
FixRequestInfo *fix_info = MaybeGetFixRequest(fix_request_id);
if (fix_info) {
assert(fix_info->success_callback_timer.get());
fix_info->success_callback_timer.reset();
- MakeSuccessCallback(fix_info, fix_info->pending_position);
+
+ assert(fix_info->pending_position.IsInitialized());
+ if (fix_info->pending_position.IsGoodFix()) {
+ if (!MakeSuccessCallback(fix_info, fix_info->pending_position)) {
+ LOG(("GearsGeolocation::CallbackRequiredImpl() : JavaScript "
+ "success callback failed.\n"));
+ }
+ } else {
+ if (!MakeErrorCallback(fix_info, fix_info->pending_position)) {
+ LOG(("GearsGeolocation::CallbackRequiredImpl() : JavaScript "
+ "error callback failed.\n"));
+ }
+ }
}
}
@@ -993,14 +1107,12 @@
// static
bool GearsGeolocation::ParseArguments(JsCallContext *context,
- bool repeats,
std::vector<std::string16> *urls,
FixRequestInfo *info) {
assert(context);
assert(urls);
assert(info);
- info->repeats = repeats;
// Arguments are: function successCallback, optional function errorCallback,
// optional object options. errorCallback can be null.
//
@@ -1033,6 +1145,7 @@
// Set default values for options.
info->enable_high_accuracy = false;
+ info->maximum_age = 0;
info->timeout = -1; // No limit is applied
info->request_address = false;
urls->clear();
@@ -1067,6 +1180,25 @@
context->SetException(error);
return false;
}
+ if (options->GetPropertyType(kMaximumAge) != JSPARAM_UNDEFINED) {
+ double maximum_age_double;
+ int maximum_age_int;
+ if (options->GetPropertyAsDouble(kMaximumAge, &maximum_age_double) &&
+ maximum_age_double == std::numeric_limits<double>::infinity()) {
+ info->maximum_age = -1;
+ } else if (options->GetPropertyAsInt(kMaximumAge, &maximum_age_int) &&
+ maximum_age_int >= 0) {
+ info->maximum_age = maximum_age_int;
+ } else {
+ std::string16 error = STRING16(L"options.");
+ error += kMaximumAge;
+ error += STRING16(L" should be a non-negative 32 bit signed integer or "
+ L"Infinity.");
+ context->SetException(error);
+ return false;
+ }
+ assert(info->maximum_age >= -1);
+ }
if (options->GetPropertyType(kTimeout) != JSPARAM_UNDEFINED) {
int timeout = -1;
if (!options->GetPropertyAsInt(kTimeout, &timeout) ||
@@ -1089,14 +1221,19 @@
context->SetException(error);
return false;
}
- if (options->GetPropertyType(kGearsAddressLanguage) != JSPARAM_UNDEFINED &&
- !options->GetPropertyAsString(kGearsAddressLanguage,
- &(info->address_language))) {
- std::string16 error = STRING16(L"options.");
- error += kGearsAddressLanguage;
- error += STRING16(L" should be a string.");
- context->SetException(error);
- return false;
+ if (options->GetPropertyType(kGearsAddressLanguage) != JSPARAM_UNDEFINED) {
+ std::string16 address_language;
+ if (!options->GetPropertyAsString(kGearsAddressLanguage,
+ &address_language) ||
+ address_language.empty()) {
+ std::string16 error = STRING16(L"options.");
+ error += kGearsAddressLanguage;
+ error += STRING16(L" should be a non-empty string.");
+ context->SetException(error);
+ return false;
+ }
+ info->address_language = address_language;
+ assert(!info->address_language.empty());
}
if (options->GetPropertyType(kGearsLocationProviderUrls) !=
JSPARAM_UNDEFINED) {
@@ -1280,7 +1417,7 @@
// callback or cancel the request.
Ref();
- // Make sure we have an unload monitor in place to cancel pending request
+ // Make sure we have an unload monitor in place to cancel pending requests
// when the page unloads.
if (unload_monitor_ == NULL) {
unload_monitor_.reset(new JsEventMonitor(GetJsRunner(), JSEVENT_UNLOAD,
@@ -1393,14 +1530,16 @@
LocationProviderPool::GetInstance()->Unregister(provider, this);
}
-void GearsGeolocation::MakeFutureSuccessCallback(int timeout_milliseconds,
- int fix_request_id,
- const Position &position) {
+void GearsGeolocation::MakeFutureWatchSuccessCallback(
+ int timeout_milliseconds,
+ int fix_request_id,
+ const Position &position) {
ASSERT_SINGLE_THREAD();
// Check that there isn't already a timer running for this request.
FixRequestInfo *fix_info = GetFixRequest(fix_request_id);
assert(!fix_info->success_callback_timer.get());
+ assert(fix_info->repeats);
fix_info->pending_position = position;
fix_info->success_callback_timer.reset(new TimedMessage(
==== //depot/googleclient/gears/opensource/gears/geolocation/geolocation.h#31 -
c:\MyDocs\Gears4/googleclient/gears/opensource/gears/geolocation/geolocation.h
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/geolocation.h
2008-11-27 12:10:19.000000000 +0000
+++ googleclient/gears/opensource/gears/geolocation/geolocation.h
2008-11-27 12:02:18.000000000 +0000
@@ -219,6 +219,9 @@
FixRequestInfo() : last_success_callback_time(0) {}
ProviderVector providers;
bool enable_high_accuracy;
+ // The maximum age of a cached position which can be accepted for this fix.
+ // A value of -1 means no limit is applied. Must be non-negative otherwise.
+ int maximum_age;
// The maximum time period in which this request must obtain a fix. A value
// of -1 implies no limit is applied. Must be non-negative otherwise.
int timeout;
@@ -264,7 +267,13 @@
void TimeoutExpiredImpl(int fix_request_id);
void CallbackRequiredImpl(int fix_request_id);
+ // Determines if we have a cached position that meets the requirement of the
+ // specified maximumAge. maximumAge must be non-zero.
bool IsCachedPositionSuitable(int maximum_age);
+
+ // Adds all providers to a fix request.
+ void AddProvidersToRequest(std::vector<std::string16> &urls,
+ FixRequestInfo *info);
// Internal method used by GetCurrentPosition and WatchPosition to get a
// position fix.
@@ -299,7 +308,6 @@
// Parses the JavaScript arguments passed to the GetCurrentPosition and
// WatchPosition methods.
static bool ParseArguments(JsCallContext *context,
- bool repeats,
std::vector<std::string16> *urls,
GearsGeolocation::FixRequestInfo *info);
// Parses a JsObject representing the options parameter. The output is a
@@ -347,9 +355,9 @@
// Causes a callback to JavaScript to be made at the specified number of
// milliseconds in the future.
- void MakeFutureSuccessCallback(int timeout_milliseconds,
- int fix_request_id,
- const Position &position);
+ void MakeFutureWatchSuccessCallback(int timeout_milliseconds,
+ int fix_request_id,
+ const Position &position);
// TODO(steveblock): Refactor the logic used to maintain the fix request maps
// into a separate class.
====
//depot/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc#36
-
c:\MyDocs\Gears4/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/geolocation_test.cc
2008-11-27 12:10:19.000000000 +0000
+++ googleclient/gears/opensource/gears/geolocation/geolocation_test.cc
2008-11-25 18:07:29.000000000 +0000
@@ -252,7 +252,7 @@
JsRunnerInterface *js_runner) {
std::vector<std::string16> urls;
GearsGeolocation::FixRequestInfo info;
- if (!GearsGeolocation::ParseArguments(context, false, &urls, &info)) {
+ if (!GearsGeolocation::ParseArguments(context, &urls, &info)) {
if (!context->is_exception_set()) {
context->SetException(
STRING16(L"Internal error parsing geolocation options."));
@@ -272,9 +272,10 @@
}
// TODO(steveblock): Consider sharing these string constants with those in
// geolocation.cc.
- if (!return_object->SetPropertyBool(STRING16(L"repeats"), info.repeats) ||
- !return_object->SetPropertyBool(STRING16(L"enableHighAccuracy"),
+ if (!return_object->SetPropertyBool(STRING16(L"enableHighAccuracy"),
info.enable_high_accuracy) ||
+ !return_object->SetPropertyInt(STRING16(L"maximumAge"),
+ info.maximum_age) ||
!return_object->SetPropertyInt(STRING16(L"timeout"), info.timeout) ||
!return_object->SetPropertyBool(STRING16(L"gearsRequestAddress"),
info.request_address) ||
==== //depot/googleclient/gears/opensource/gears/sdk/api_geolocation.html#12 -
c:\MyDocs\Gears4/googleclient/gears/opensource/gears/sdk/api_geolocation.html
====
# action=edit type=text
--- googleclient/gears/opensource/gears/sdk/api_geolocation.html
2008-11-27 12:10:19.000000000 +0000
+++ googleclient/gears/opensource/gears/sdk/api_geolocation.html
2008-11-27 01:17:31.000000000 +0000
@@ -127,6 +127,7 @@
<pre><code><a href="#positionoptions">PositionOptions class</a>
readwrite attribute bool enableHighAccuracy
+ readwrite attribute int maximumAge
readwrite attribute int timeout
readwrite attribute bool gearsRequestAddress
readwrite attribute string gearsAddressLanguage
@@ -175,12 +176,21 @@
request, see <code><a
href="#positionoptions">PositionOptions</a></code>.</td>
</tr><tr class="odd">
<td>Description:</td>
- <td class="odd">Attempts to obtain a new position. If a good fix is
obtained
-within the time specified by <code>PositionOptions.timeout</code>, the success
-callback function is called exactly once with a good position. If the timeout
-expires or none of the location providers are able to provide a good fix, the
-error callback is called exactly, unless <code>null</code> was passed for
-<code>errorCallback</code>.
+ <td class="odd">Provides a previously cached position if available or
+ attempts to obtain a new position.<br><br>
+ If <code>PositionOptions.maximumAge</code> is zero, the cached position is
+ not used and a new position fix is always sought. The method will throw an
+ exception if no location providers are used.<br><br>
+ If <code>PositionOptions.maximumAge</code> is non-zero, Gears will call the
+ success callback if the cached position is more recent than
+ <code>PositionOptions.maximumAge</code>. If not, Gears will attempt to
+ obtain a new position fix. The error callabck is called exactly once as
soon
+ as a position fix is obtained, or the error callnback is called on failure.
+ If the cached position is not sufficiently recent and no location providers
+ are used, the error callback is called.<br><br>
+ Whenever Gears attempts to obtain a new position fix, this is subject to
+ <code>PositionOptions.timeout</code>. The error callback is called on
+ timeout.
<br />
<br />
The signature of the success callback is:<br />
@@ -210,14 +220,26 @@
</tr>
<tr class="odd">
<td>Description:</td>
- <td class="odd">Repeatedly obtains a new position. The success callback
-function is called as soon as a good position is available, or whenever the
-position changes significantly, subject to a maximum callback frequency. The
-error callback is called if a fatal error occurs which will prevent a position
-fix from ever being obtained by this watch, unless <code>null</code> was passed
-for <code>errorCallback</code>. The error callback, if supplied, is also called
-if Gears detects that the device has moved, but is unable to obtain a new
-position fix within the time specifed by <code>PositionOptions.timeout</code>.
+ <td class="odd">Provides a previously cached position if available and
+ repeatedly obtains a new position.<br><br>
+ If <code>PositionOptions.maximumAge</code> is zero, the cached position is
+ not used and a new position fix is always sought. The method will throw an
+ exception if no location providers are used.<br><br>
+ If <code>PositionOptions.maximumAge</code> is non-zero, Gears will call the
+ success callback if the cached position is more recent than
+ <code>PositionOptions.maximumAge</code>. If any case, Gears will then
+ attempt to obtain a new position fix. The success callback function is
+ called as soon as a good position is available and whenever the position
+ changes significantly, subject to a maximum callback frequency. The error
+ callback is called if a fatal error occurs which will prevent a position
fix
+ from ever being obtained by this watch. If the cached position is not
+ sufficiently recent and no location providers are used, the error callback
+ is called.<br><br>
+ Whenever Gears attempts to obtain a new position fix, this is subject to
+ <code>PositionOptions.timeout</code>. The error callback is called on
+ timeout when obtaining the first position fix, or if Gears detects that the
+ device has moved, but is unable to obtain a new position fix within the
time
+ specifed by <code>PositionOptions.timeout</code>.
<br />
<br />
The signature of the success callback is:<br />
@@ -373,6 +395,18 @@
Also, there is no guarantee that the device will be able to provide more
accurate results than if this flag is not specified. The default value
is false.</td>
+</tr>
+<tr class="odd">
+ <td><strong>maximumAge</strong></td>
+ <td class="odd">readwrite int</td>
+ <td class="odd">Optional, specifies the maximum permissible age of a cached
+ position provided by the getCurrentPosition and watchPosition methods. When
+ either of these methods is called, if Gears has a cahced permission and it is
+ more recent than the specified maximum age, the success callback will be
+ called immediatley with this cached position. If not, Gears will attempt to
+ obtain a new position fix. If a value of Infinity is specified, no time limit
+ is applied. The default value is zero, meaning that a cached position is
never
+ used and Gears wil always attempt to get a new position fix.</td>
</tr>
<tr class="odd">
<td><strong>timeout</strong></td>
====
//depot/googleclient/gears/opensource/gears/test/testcases/geolocation_tests.js#5
-
c:\MyDocs\Gears4/googleclient/gears/opensource/gears/test/testcases/geolocation_tests.js
====
# action=edit type=text
--- googleclient/gears/opensource/gears/test/testcases/geolocation_tests.js
2008-11-27 12:10:19.000000000 +0000
+++ googleclient/gears/opensource/gears/test/testcases/geolocation_tests.js
2008-11-27 00:24:25.000000000 +0000
@@ -30,6 +30,7 @@
// All good.
var goodOptions = {
enableHighAccuracy: false,
+ maximumAge: 0,
timeout: 0,
gearsRequestAddress: false,
gearsAddressLanguage: 'test',
@@ -66,68 +67,102 @@
dummyFunction,
{enableHighAccuracy: 42});
}, 'options.enableHighAccuracy should be a boolean.');
+ // Wrong type for maximumAge.
+ var maximumAgeTypeError = 'options.maximumAge should be a non-negative 32 ' +
+ 'bit signed integer or Infinity.';
+ assertError(function() {
+ geolocation.getCurrentPosition(dummyFunction,
+ dummyFunction,
+ {maximumAge: 42.9});
+ }, maximumAgeTypeError);
+ assertError(function() {
+ geolocation.getCurrentPosition(dummyFunction,
+ dummyFunction,
+ {maximumAge: -1});
+ }, maximumAgeTypeError);
+ assertError(function() {
+ geolocation.getCurrentPosition(dummyFunction,
+ dummyFunction,
+ {maximumAge: 2147483648}); // 2^31
+ }, maximumAgeTypeError);
+ assertError(function() {
+ geolocation.getCurrentPosition(dummyFunction,
+ dummyFunction,
+ {maximumAge: -Infinity});
+ }, maximumAgeTypeError);
// Wrong type for timeout.
+ var timeoutTypeError = 'options.timeout should be a non-negative 32 bit ' +
+ 'signed integer.';
assertError(function() {
geolocation.getCurrentPosition(dummyFunction,
dummyFunction,
{timeout: 42.9});
- }, 'options.timeout should be a non-negative 32 bit signed integer.');
+ }, timeoutTypeError);
assertError(function() {
geolocation.getCurrentPosition(dummyFunction,
dummyFunction,
{timeout: -1});
- }, 'options.timeout should be a non-negative 32 bit signed integer.');
+ }, timeoutTypeError);
assertError(function() {
geolocation.getCurrentPosition(dummyFunction,
dummyFunction,
{timeout: 2147483648}); // 2^31
- }, 'options.timeout should be a non-negative 32 bit signed integer.');
+ }, timeoutTypeError);
// Wrong type for gearsRequestAddress.
assertError(
function() {
geolocation.getCurrentPosition(dummyFunction,
dummyFunction,
{gearsRequestAddress: 42});
- },
- 'options.gearsRequestAddress should be a boolean.');
+ }, 'options.gearsRequestAddress should be a boolean.');
// Wrong type for gearsAddressLanguage.
+ var gearsAddressLanguageTypeError = 'options.gearsAddressLanguage should ' +
+ 'be a non-empty string.';
assertError(
function() {
geolocation.getCurrentPosition(dummyFunction,
dummyFunction,
{gearsAddressLanguage: 42});
- },
- 'options.gearsAddressLanguage should be a string.');
+ }, gearsAddressLanguageTypeError);
+ assertError(
+ function() {
+ geolocation.getCurrentPosition(dummyFunction,
+ dummyFunction,
+ {gearsAddressLanguage: ''});
+ }, gearsAddressLanguageTypeError);
// Wrong type for gearsLocationProviderUrls.
+ var gearsLocationProviderUrlsTypeError =
+ 'options.gearsLocationProviderUrls should be null or an array of
strings';
assertError(
function() {
geolocation.getCurrentPosition(dummyFunction,
dummyFunction,
{gearsLocationProviderUrls: 42});
- },
- 'options.gearsLocationProviderUrls should be null or an array of ' +
- 'strings');
+ }, gearsLocationProviderUrlsTypeError);
assertError(
function() {
geolocation.getCurrentPosition(dummyFunction,
dummyFunction,
{gearsLocationProviderUrls: [42]});
- },
- 'options.gearsLocationProviderUrls should be null or an array of ' +
- 'strings');
+ }, gearsLocationProviderUrlsTypeError);
}
function testNoProviders() {
+ // A request with no providers should assert unless maximumAge is non-zero.
assertError(
function() {
geolocation.getCurrentPosition(
dummyFunction,
dummyFunction,
- {gearsLocationProviderUrls: [], enableHighAccuracy: false});
+ {gearsLocationProviderUrls: []});
},
'Fix request has no location providers.',
'Calling getCurrentPosition() should fail if no location providers are '
+
- 'specified.');
+ 'specified unless maximumAge is non-zero.');
+ geolocation.getCurrentPosition(
+ dummyFunction,
+ dummyFunction,
+ {maximumAge: 1, gearsLocationProviderUrls: []});
}
function testZeroTimeout() {
====
//depot/googleclient/gears/opensource/gears/test/testcases/internal_geolocation_tests.js#13
-
c:\MyDocs\Gears4/googleclient/gears/opensource/gears/test/testcases/internal_geolocation_tests.js
====
# action=edit type=text
---
googleclient/gears/opensource/gears/test/testcases/internal_geolocation_tests.js
2008-11-27 12:10:19.000000000 +0000
+++
googleclient/gears/opensource/gears/test/testcases/internal_geolocation_tests.js
2008-11-27 11:58:52.000000000 +0000
@@ -46,17 +46,17 @@
if (isUsingCCTests) {
var dummyFunction = function() {};
- // Test correct parsing.
+ // Intialise to default values.
+ var defaultLocationProviderUrl = 'http://www.google.com/loc/json'
var correctOptions = {
- repeats: false,
enableHighAccuracy: false,
+ maximumAge: 0,
timeout: -1,
gearsRequestAddress: false,
gearsAddressLanguage: '',
- gearsLocationProviderUrls: ['http://www.google.com/loc/json']
- };
-
- var urls = ['url1', 'url2'];
+ gearsLocationProviderUrls: [defaultLocationProviderUrl]
+ };
+
var parsedOptions;
// No options.
@@ -79,34 +79,32 @@
dummyFunction, dummyFunction, {gearsLocationProviderUrls: null});
assertObjectEqual(correctOptions, parsedOptions);
- // All properties false, provider URLs set.
+ // All properties explicitly set to default values where possible.
parsedOptions = internalTests.testParseGeolocationOptions(
dummyFunction,
dummyFunction,
- {
- enableHighAccuracy: false,
- gearsRequestAddress: false,
- gearsAddressLanguage: '',
- gearsLocationProviderUrls: urls
- });
- correctOptions.gearsLocationProviderUrls = urls;
+ {enableHighAccuracy: false, maximumAge: 0, gearsRequestAddress:
false});
+ correctOptions.gearsLocationProviderUrls = [defaultLocationProviderUrl];
assertObjectEqual(correctOptions, parsedOptions);
- // All properties true, provider URLs set.
+ // Test infinite maximum age.
parsedOptions = internalTests.testParseGeolocationOptions(
- dummyFunction,
- dummyFunction,
- {
- enableHighAccuracy: true,
- gearsRequestAddress: true,
- gearsAddressLanguage: 'test',
- gearsLocationProviderUrls: urls
- });
- correctOptions.enableHighAccuracy = true;
- correctOptions.gearsRequestAddress = true;
- correctOptions.gearsAddressLanguage = 'test';
- correctOptions.gearsLocationProviderUrls = urls;
+ dummyFunction, dummyFunction, {maximumAge: Infinity});
+ correctOptions.maximumAge = -1;
assertObjectEqual(correctOptions, parsedOptions);
+
+ // All properties set to non-default values.
+ var options = {
+ enableHighAccuracy: true,
+ maximumAge: 1,
+ timeout: 0,
+ gearsRequestAddress: true,
+ gearsAddressLanguage: 'test',
+ gearsLocationProviderUrls: ['url1', 'url2']
+ };
+ parsedOptions = internalTests.testParseGeolocationOptions(
+ dummyFunction, dummyFunction, options);
+ assertObjectEqual(options, parsedOptions);
}
}
@@ -628,3 +626,121 @@
options);
}
}
+
+// Helper function for PopulateCacheAndMakeRequest
+function PopulateCachedPosition(geolocation, successFunction) {
+ // Uses the mock location provider to populate the cached position with a set
+ // value.
+ //
+ // Note that we can never guarantee that the cached position will be updated
+ // by the position from by a given provider because logic in the arbitrator
+ // only updates the cached position if the new position has a better
accuracy.
+ // To make the Pulse unit tests run reliably, the position we use here is the
+ // most accurate used in the unit tests.
+ //
+ // To make sure that the cached position has the correct timestamp (as well
as
+ // the correct lat/lng), we force two updates.
+ var intermediatePositionToCache = {
+ latitude: -80.0,
+ longitude: 0.0,
+ accuracy: 1.0
+ };
+ function SetIntermediatePosition() {
+ internalTests.configureGeolocationMockLocationProviderForTest(
+ intermediatePositionToCache);
+ geolocation.getCurrentPosition(
+ function(position) { SetPosition(); },
+ null,
+ {gearsLocationProviderUrls: null});
+ }
+ function SetPosition() {
+ internalTests.configureGeolocationMockLocationProviderForTest(
+ positionToCache);
+ geolocation.getCurrentPosition(
+ function(position) {
+ assert(geolocation.lastPosition.timestamp >= Date.parse(startTime),
+ 'Last position too old in PopulateCachedPosition.');
+ var lastPosition = geolocation.lastPosition;
+ delete lastPosition.timestamp;
+ assertObjectEqual(
+ positionToCache, lastPosition,
+ 'Last position incorrect in PopulateCachedPosition.');
+ internalTests.removeGeolocationMockLocationProvider();
+ successFunction();
+ },
+ null,
+ {gearsLocationProviderUrls: null});
+ }
+ var startTime = Date();
+ SetIntermediatePosition();
+}
+
+// Needs to be global to be used in tests.
+var positionToCache = {
+ latitude: 51.0,
+ longitude: -0.1,
+ accuracy: 1.0
+};
+
+// Helper function for testCachedPositionXXX tests
+function PopulateCacheAndMakeRequest(waitTime, successCallback, errorCallback,
+ options) {
+ var geolocation = google.gears.factory.create('beta.geolocation');
+ PopulateCachedPosition(geolocation, function() {
+ var timer = google.gears.factory.create('beta.timer');
+ timer.setTimeout(function() {
+ geolocation.getCurrentPosition(successCallback, errorCallback, options);
+ }, waitTime);
+ });
+}
+
+function testCachedPositionWithinMaximumAge() {
+ // Test that a request with a non-zero maximumAge immediately calls the
+ // success callback with the cached position if we have a suitable one.
+ startAsync();
+ PopulateCacheAndMakeRequest(
+ 0, // Wait for less than maximumAge.
+ function(position) {
+ delete position.timestamp;
+ assertObjectEqual(positionToCache, position,
+ 'Position incorrect in TestMaximumAge.');
+ completeAsync();
+ },
+ null,
+ {maximumAge: 1000});
+}
+
+function testCachedPositionOutsideMaximumAgeNoProviders() {
+ // Test that a request with a non-zero maximumAge and no location providers
+ // immediately calls the error callback if we don't have a suitable cached
+ // position.
+ startAsync();
+ PopulateCacheAndMakeRequest(
+ 10, // Wait for longer than maximumAge.
+ function() {},
+ function(error) {
+ assertErrorEqual(error.POSITION_UNAVAILABLE,
+ 'No suitable cached position available.',
+ error);
+ completeAsync();
+ },
+ {maximumAge: 1, gearsLocationProviderUrls: null});
+}
+
+function testCachedPositionOutsideMaximumAge() {
+ // Test that a request with a non-zero maximumAge and location providers
+ // does not immediately call the error callback if we don't have a suitable
+ // cached position. We use a non-existant URL for the location provider to
+ // force an error.
+ startAsync();
+ PopulateCacheAndMakeRequest(
+ 10, // Wait for longer than maximumAge.
+ function() {},
+ function(error) {
+ assert(error.message.search('returned error code 404.') != -1);
+ error.message = null;
+ assertErrorEqual(error.POSITION_UNAVAILABLE, null, error);
+ completeAsync();
+ },
+ {maximumAge: 1, gearsLocationProviderUrls: ['non_existant_url']});
+}