зеркало из https://github.com/mozilla/pjs.git
Improve handling of server and network errors (bug 335965) r=marria
This commit is contained in:
Родитель
9f827e24d8
Коммит
85d0b27116
|
@ -880,6 +880,7 @@ extensions/typeaheadfind/Makefile
|
|||
|
||||
MAKEFILES_metrics="
|
||||
extensions/metrics/Makefile
|
||||
extensions/metrics/build/Makefile
|
||||
extensions/metrics/public/Makefile
|
||||
extensions/metrics/src/Makefile
|
||||
extensions/metrics/test/Makefile
|
||||
|
|
|
@ -48,7 +48,7 @@ NO_JAR_AUTO_REG = 1
|
|||
INSTALL_EXTENSION_ID = metrics@mozilla.org
|
||||
XPI_PKGNAME = metrics-$(MOZ_APP_VERSION)
|
||||
|
||||
DIRS = public src
|
||||
DIRS = public src build
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
TOOL_DIRS += test
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# vim:set ts=8 sw=8 sts=8 noet:
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is the Metrics extension.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Google Inc.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Brian Ryner <bryner@brianryner.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = metrics
|
||||
LIBRARY_NAME = metrics
|
||||
SHORT_LIBNAME = metrics
|
||||
|
||||
XPI_NAME = metrics
|
||||
|
||||
REQUIRES = \
|
||||
xpcom \
|
||||
necko \
|
||||
content \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
nsMetricsModule.cpp \
|
||||
$(NULL)
|
||||
|
||||
SHARED_LIBRARY_LIBS = \
|
||||
../src/$(LIB_PREFIX)metrics_s.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)bz2.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DSO_LDOPTS = $(XPCOM_GLUE_LDOPTS) \
|
||||
$(NSPR_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../src \
|
||||
-I$(DIST)/public/nss \
|
||||
-I$(DIST)/private/nss \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,128 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Metrics extension.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Google Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@meer.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsMetricsModule.h"
|
||||
#include "nsMetricsService.h"
|
||||
#include "nsLoadCollector.h"
|
||||
#include "nsWindowCollector.h"
|
||||
#include "nsProfileCollector.h"
|
||||
#include "nsUICommandCollector.h"
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsXPCOMCID.h"
|
||||
#ifndef MOZILLA_1_8_BRANCH
|
||||
#include "nsIClassInfoImpl.h"
|
||||
#endif
|
||||
|
||||
NS_DECL_CLASSINFO(nsMetricsService)
|
||||
|
||||
#define COLLECTOR_CONTRACTID(type) \
|
||||
"@mozilla.org/metrics/collector;1?name=" type ":" NS_METRICS_NAMESPACE
|
||||
|
||||
static NS_METHOD
|
||||
nsMetricsServiceRegisterSelf(nsIComponentManager *compMgr,
|
||||
nsIFile *path,
|
||||
const char *loaderStr,
|
||||
const char *type,
|
||||
const nsModuleComponentInfo *info)
|
||||
{
|
||||
nsCOMPtr<nsICategoryManager> cat =
|
||||
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
|
||||
NS_ENSURE_STATE(cat);
|
||||
|
||||
cat->AddCategoryEntry("app-startup",
|
||||
NS_METRICSSERVICE_CLASSNAME,
|
||||
"service," NS_METRICSSERVICE_CONTRACTID,
|
||||
PR_TRUE, PR_TRUE, nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLoadCollector, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowCollector)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsProfileCollector)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUICommandCollector)
|
||||
|
||||
static const nsModuleComponentInfo components[] = {
|
||||
{
|
||||
NS_METRICSSERVICE_CLASSNAME,
|
||||
NS_METRICSSERVICE_CID,
|
||||
NS_METRICSSERVICE_CONTRACTID,
|
||||
nsMetricsService::Create,
|
||||
nsMetricsServiceRegisterSelf,
|
||||
NULL,
|
||||
NULL,
|
||||
NS_CI_INTERFACE_GETTER_NAME(nsMetricsService),
|
||||
NULL,
|
||||
&NS_CLASSINFO_NAME(nsMetricsService),
|
||||
nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::SINGLETON
|
||||
},
|
||||
{
|
||||
NS_METRICSSERVICE_CLASSNAME,
|
||||
NS_METRICSSERVICE_CID,
|
||||
NS_ABOUT_MODULE_CONTRACTID_PREFIX "metrics",
|
||||
nsMetricsService::Create
|
||||
},
|
||||
{
|
||||
NS_LOADCOLLECTOR_CLASSNAME,
|
||||
NS_LOADCOLLECTOR_CID,
|
||||
COLLECTOR_CONTRACTID("document"),
|
||||
nsLoadCollectorConstructor
|
||||
},
|
||||
{
|
||||
NS_WINDOWCOLLECTOR_CLASSNAME,
|
||||
NS_WINDOWCOLLECTOR_CID,
|
||||
COLLECTOR_CONTRACTID("window"),
|
||||
nsWindowCollectorConstructor
|
||||
},
|
||||
{
|
||||
NS_PROFILECOLLECTOR_CLASSNAME,
|
||||
NS_PROFILECOLLECTOR_CID,
|
||||
COLLECTOR_CONTRACTID("profile"),
|
||||
nsProfileCollectorConstructor
|
||||
},
|
||||
{
|
||||
NS_UICOMMANDCOLLECTOR_CLASSNAME,
|
||||
NS_UICOMMANDCOLLECTOR_CID,
|
||||
COLLECTOR_CONTRACTID("uielement"),
|
||||
nsUICommandCollectorConstructor
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_NSGETMODULE(metrics, components)
|
|
@ -43,9 +43,8 @@ VPATH = @srcdir@
|
|||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = metrics
|
||||
LIBRARY_NAME = metrics
|
||||
SHORT_LIBNAME = metrics
|
||||
IS_COMPONENT = 1
|
||||
LIBRARY_NAME = metrics_s
|
||||
SHORT_LIBNAME = metrics_s
|
||||
|
||||
XPI_NAME = metrics
|
||||
|
||||
|
@ -89,7 +88,6 @@ CPPSRCS = \
|
|||
nsLoadCollector.cpp \
|
||||
nsMetricsConfig.cpp \
|
||||
nsMetricsEventItem.cpp \
|
||||
nsMetricsModule.cpp \
|
||||
nsMetricsService.cpp \
|
||||
nsProfileCollector.cpp \
|
||||
nsWindowCollector.cpp \
|
||||
|
@ -97,11 +95,11 @@ CPPSRCS = \
|
|||
nsStringUtils.cpp \
|
||||
$(NULL)
|
||||
|
||||
SHARED_LIBRARY_LIBS += $(DIST)/lib/$(LIB_PREFIX)bz2.$(LIB_SUFFIX)
|
||||
# Create a static library to link into the component library and unit tests
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
||||
EXTRA_DSO_LDOPTS = $(XPCOM_GLUE_LDOPTS) \
|
||||
$(NSPR_LIBS) \
|
||||
$(NULL)
|
||||
# md5.c requires NSS headers
|
||||
LOCAL_INCLUDES = -I$(DIST)/public/nss -I$(DIST)/private/nss
|
||||
|
||||
# Link against the static CRT
|
||||
ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||
|
@ -115,5 +113,3 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
export:: $(topsrcdir)/security/nss/lib/freebl/md5.c
|
||||
$(INSTALL) $^ .
|
||||
|
||||
LOCAL_INCLUDES += -I$(srcdir) -I$(DIST)/public/nss -I$(DIST)/private/nss
|
||||
|
|
|
@ -43,11 +43,12 @@
|
|||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOM3Node.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "prprf.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIDOMSerializer.h"
|
||||
|
||||
#define NS_DEFAULT_UPLOAD_INTERVAL_SEC 60 * 5
|
||||
|
||||
|
@ -67,6 +68,19 @@ MakeKey(const nsAString &eventNS, const nsAString &eventName)
|
|||
return key;
|
||||
}
|
||||
|
||||
static void
|
||||
SplitKey(const nsString &key, nsString &eventNS, nsString &eventName)
|
||||
{
|
||||
PRInt32 colon = FindChar(key, ':');
|
||||
if (colon == -1) {
|
||||
NS_ERROR("keys from MakeKey should always have a colon");
|
||||
return;
|
||||
}
|
||||
|
||||
eventName = Substring(key, 0, colon);
|
||||
eventNS = Substring(key, colon + 1, key.Length() - colon - 1);
|
||||
}
|
||||
|
||||
// This method leaves the result value unchanged if a parsing error occurs.
|
||||
static void
|
||||
ReadIntegerAttr(nsIDOMElement *elem, const nsAString &attrName, PRInt32 *result)
|
||||
|
@ -87,7 +101,7 @@ nsMetricsConfig::nsMetricsConfig()
|
|||
PRBool
|
||||
nsMetricsConfig::Init()
|
||||
{
|
||||
if (!mEventSet.Init()) {
|
||||
if (!mEventSet.Init() || !mNSURIToPrefixMap.Init()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
Reset();
|
||||
|
@ -102,8 +116,10 @@ nsMetricsConfig::Reset()
|
|||
NS_ASSERTION(mEventSet.IsInitialized(), "nsMetricsConfig::Init not called");
|
||||
|
||||
mEventSet.Clear();
|
||||
mNSURIToPrefixMap.Clear();
|
||||
mEventLimit = PR_INT32_MAX;
|
||||
mUploadInterval = NS_DEFAULT_UPLOAD_INTERVAL_SEC;
|
||||
mHasConfig = PR_FALSE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -111,17 +127,18 @@ nsMetricsConfig::Load(nsIFile *file)
|
|||
{
|
||||
// The given file references a XML file with the following structure:
|
||||
//
|
||||
// <config xmlns="http://www.mozilla.org/metrics"
|
||||
// xmlns:foo="http://foo.com/metrics">
|
||||
// <collectors>
|
||||
// <collector type="ui"/>
|
||||
// <collector type="document"/>
|
||||
// <collector type="window"/>
|
||||
// <collector type="foo:mystat"/>
|
||||
// </collectors>
|
||||
// <limit events="200"/>
|
||||
// <upload interval="600"/>
|
||||
// </config>
|
||||
// <response xmlns="http://www.mozilla.org/metrics">
|
||||
// <config xmlns:foo="http://foo.com/metrics">
|
||||
// <collectors>
|
||||
// <collector type="ui"/>
|
||||
// <collector type="document"/>
|
||||
// <collector type="window"/>
|
||||
// <collector type="foo:mystat"/>
|
||||
// </collectors>
|
||||
// <limit events="200"/>
|
||||
// <upload interval="600"/>
|
||||
// </config>
|
||||
// </response>
|
||||
|
||||
NS_ASSERTION(mEventSet.IsInitialized(), "nsMetricsConfig::Init not called");
|
||||
|
||||
|
@ -144,20 +161,142 @@ nsMetricsConfig::Load(nsIFile *file)
|
|||
getter_AddRefs(doc));
|
||||
NS_ENSURE_STATE(doc);
|
||||
|
||||
// Now, walk the DOM. Most elements are optional, but we check the root
|
||||
// element to make sure it's a valid response document.
|
||||
nsCOMPtr<nsIDOMElement> elem;
|
||||
doc->GetDocumentElement(getter_AddRefs(elem));
|
||||
NS_ENSURE_STATE(elem);
|
||||
|
||||
nsString nameSpace;
|
||||
elem->GetNamespaceURI(nameSpace);
|
||||
if (!nameSpace.Equals(NS_LITERAL_STRING(NS_METRICS_NAMESPACE))) {
|
||||
// We have a root element, but it's the wrong namespace
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsString tag;
|
||||
elem->GetLocalName(tag);
|
||||
if (!tag.Equals(NS_LITERAL_STRING("response"))) {
|
||||
// The root tag isn't what we're expecting
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// At this point, we can clear our old configuration.
|
||||
Reset();
|
||||
|
||||
// Now, walk the DOM. All config elements are optional, and we ignore stuff
|
||||
// that we don't understand.
|
||||
nsCOMPtr<nsIDOMElement> elem;
|
||||
doc->GetDocumentElement(getter_AddRefs(elem));
|
||||
if (!elem)
|
||||
return NS_OK;
|
||||
|
||||
ForEachChildElement(elem, &nsMetricsConfig::ProcessToplevelElement);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMetricsConfig::Save(nsILocalFile *file)
|
||||
{
|
||||
nsCOMPtr<nsIDOMDocument> doc =
|
||||
do_CreateInstance("@mozilla.org/xml/xml-document;1");
|
||||
NS_ENSURE_STATE(doc);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> response;
|
||||
nsMetricsUtils::CreateElement(doc, NS_LITERAL_STRING("response"),
|
||||
getter_AddRefs(response));
|
||||
NS_ENSURE_STATE(response);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> config;
|
||||
nsMetricsUtils::CreateElement(doc, NS_LITERAL_STRING("config"),
|
||||
getter_AddRefs(config));
|
||||
NS_ENSURE_STATE(config);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> collectors;
|
||||
nsMetricsUtils::CreateElement(doc, NS_LITERAL_STRING("collectors"),
|
||||
getter_AddRefs(collectors));
|
||||
NS_ENSURE_STATE(collectors);
|
||||
|
||||
nsTArray<nsString> events;
|
||||
GetEvents(events);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> nodeOut;
|
||||
nsresult rv;
|
||||
|
||||
for (PRUint32 i = 0; i < events.Length(); ++i) {
|
||||
nsString eventNS, eventName;
|
||||
SplitKey(events[i], eventNS, eventName);
|
||||
|
||||
nsString prefix;
|
||||
if (!eventNS.Equals(NS_LITERAL_STRING(NS_METRICS_NAMESPACE))) {
|
||||
if (!mNSURIToPrefixMap.Get(eventNS, &prefix)) {
|
||||
MS_LOG(("uri %s not in prefix map",
|
||||
NS_ConvertUTF16toUTF8(eventNS).get()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Declare the namespace prefix on the root element
|
||||
nsString attrName(NS_LITERAL_STRING("xmlns:"));
|
||||
attrName.Append(prefix);
|
||||
response->SetAttribute(attrName, eventNS);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMElement> collector;
|
||||
nsMetricsUtils::CreateElement(doc, NS_LITERAL_STRING("collector"),
|
||||
getter_AddRefs(collector));
|
||||
NS_ENSURE_STATE(collector);
|
||||
|
||||
nsString shortName;
|
||||
if (!prefix.IsEmpty()) {
|
||||
shortName = prefix;
|
||||
shortName.Append(':');
|
||||
}
|
||||
shortName.Append(eventName);
|
||||
|
||||
collector->SetAttribute(NS_LITERAL_STRING("type"), eventName);
|
||||
collectors->AppendChild(collector, getter_AddRefs(nodeOut));
|
||||
}
|
||||
config->AppendChild(collectors, getter_AddRefs(nodeOut));
|
||||
|
||||
if (mEventLimit != PR_INT32_MAX) {
|
||||
nsCOMPtr<nsIDOMElement> limit;
|
||||
nsMetricsUtils::CreateElement(doc, NS_LITERAL_STRING("limit"),
|
||||
getter_AddRefs(limit));
|
||||
NS_ENSURE_STATE(limit);
|
||||
|
||||
nsString limitStr;
|
||||
AppendInt(limitStr, mEventLimit);
|
||||
limit->SetAttribute(NS_LITERAL_STRING("events"), limitStr);
|
||||
config->AppendChild(limit, getter_AddRefs(nodeOut));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMElement> upload;
|
||||
nsMetricsUtils::CreateElement(doc, NS_LITERAL_STRING("upload"),
|
||||
getter_AddRefs(upload));
|
||||
NS_ENSURE_STATE(upload);
|
||||
|
||||
nsString intervalStr;
|
||||
AppendInt(intervalStr, mUploadInterval);
|
||||
upload->SetAttribute(NS_LITERAL_STRING("interval"), intervalStr);
|
||||
config->AppendChild(upload, getter_AddRefs(nodeOut));
|
||||
|
||||
response->AppendChild(config, getter_AddRefs(nodeOut));
|
||||
|
||||
nsCOMPtr<nsIDOMSerializer> ds =
|
||||
do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID);
|
||||
NS_ENSURE_STATE(ds);
|
||||
|
||||
nsString docText;
|
||||
rv = ds->SerializeToString(response, docText);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ConvertUTF16toUTF8 utf8Doc(docText);
|
||||
PRInt32 num = utf8Doc.Length();
|
||||
|
||||
PRFileDesc *fd;
|
||||
rv = file->OpenNSPRFileDesc(
|
||||
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool success = (PR_Write(fd, utf8Doc.get(), num) == num);
|
||||
PR_Close(fd);
|
||||
|
||||
return success ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
nsMetricsConfig::ForEachChildElement(nsIDOMElement *elem,
|
||||
ForEachChildElementCallback callback)
|
||||
|
@ -183,6 +322,19 @@ nsMetricsConfig::ProcessToplevelElement(nsIDOMElement *elem)
|
|||
{
|
||||
// Process a top-level element
|
||||
|
||||
nsString name;
|
||||
elem->GetLocalName(name);
|
||||
if (name.Equals(NS_LITERAL_STRING("config"))) {
|
||||
mHasConfig = PR_TRUE;
|
||||
ForEachChildElement(elem, &nsMetricsConfig::ProcessConfigChild);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMetricsConfig::ProcessConfigChild(nsIDOMElement *elem)
|
||||
{
|
||||
// Process a config element child
|
||||
|
||||
nsString name;
|
||||
elem->GetLocalName(name);
|
||||
if (name.Equals(NS_LITERAL_STRING("collectors"))) {
|
||||
|
@ -223,8 +375,12 @@ nsMetricsConfig::ProcessCollectorElement(nsIDOMElement *elem)
|
|||
// value is the EventName
|
||||
} else {
|
||||
// value is NamespacePrefix + ":" + EventName
|
||||
node->LookupNamespaceURI(StringHead(type, colon), namespaceURI);
|
||||
nsString prefix(StringHead(type, colon));
|
||||
node->LookupNamespaceURI(prefix, namespaceURI);
|
||||
type.Cut(0, colon + 1);
|
||||
|
||||
// Add this namespace -> prefix mapping to our lookup table
|
||||
mNSURIToPrefixMap.Put(namespaceURI, prefix);
|
||||
}
|
||||
|
||||
mEventSet.PutEntry(MakeKey(namespaceURI, type));
|
||||
|
@ -238,6 +394,26 @@ nsMetricsConfig::IsEventEnabled(const nsAString &eventNS,
|
|||
return mEventSet.GetEntry(MakeKey(eventNS, eventName)) != nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsMetricsConfig::SetEventEnabled(const nsAString &eventNS,
|
||||
const nsAString &eventName, PRBool enabled)
|
||||
{
|
||||
NS_ASSERTION(mEventSet.IsInitialized(), "nsMetricsConfig::Init not called");
|
||||
nsString key = MakeKey(eventNS, eventName);
|
||||
if (enabled) {
|
||||
mEventSet.PutEntry(key);
|
||||
} else {
|
||||
mEventSet.RemoveEntry(key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMetricsConfig::ClearEvents()
|
||||
{
|
||||
NS_ASSERTION(mEventSet.IsInitialized(), "nsMetricsConfig::Init not called");
|
||||
mEventSet.Clear();
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator PR_CALLBACK
|
||||
nsMetricsConfig::CopyKey(nsStringHashKey *entry, void *userData)
|
||||
{
|
||||
|
|
|
@ -39,11 +39,12 @@
|
|||
#ifndef nsMetricsConfig_h__
|
||||
#define nsMetricsConfig_h__
|
||||
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
class nsIDOMElement;
|
||||
class nsIFile;
|
||||
class nsILocalFile;
|
||||
template<class E> class nsTArray;
|
||||
|
||||
class nsMetricsConfig
|
||||
|
@ -67,6 +68,11 @@ public:
|
|||
*/
|
||||
nsresult Load(nsIFile *file);
|
||||
|
||||
/**
|
||||
* Writes the current metrics configuration to disk.
|
||||
*/
|
||||
nsresult Save(nsILocalFile *file);
|
||||
|
||||
/**
|
||||
* Call this method to determine if the given event type is enabled for
|
||||
* collection.
|
||||
|
@ -74,12 +80,23 @@ public:
|
|||
PRBool IsEventEnabled(const nsAString &eventNS,
|
||||
const nsAString &eventName) const;
|
||||
|
||||
/**
|
||||
* Sets a particular event to be enabled or disabled.
|
||||
*/
|
||||
void SetEventEnabled(const nsAString &eventNS,
|
||||
const nsAString &eventName, PRBool enabled);
|
||||
|
||||
/**
|
||||
* Call this method to get a list of all events that are enabled.
|
||||
* The event names are prefixed with the namespace, separated by a colon.
|
||||
*/
|
||||
void GetEvents(nsTArray<nsString> &events);
|
||||
|
||||
/**
|
||||
* Clears the set of events in this config.
|
||||
*/
|
||||
void ClearEvents();
|
||||
|
||||
/**
|
||||
* Get the limit on the number of events that should be collected.
|
||||
*/
|
||||
|
@ -89,6 +106,15 @@ public:
|
|||
return mEventLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event limit.
|
||||
*/
|
||||
void SetEventLimit(PRInt32 limit) {
|
||||
NS_ASSERTION(mEventSet.IsInitialized(),
|
||||
"nsMetricsConfig::Init not called");
|
||||
mEventLimit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upload interval (measured in seconds).
|
||||
*/
|
||||
|
@ -107,6 +133,15 @@ public:
|
|||
mUploadInterval = uploadInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there was a <config> present in the response.
|
||||
*/
|
||||
PRBool HasConfig() const {
|
||||
NS_ASSERTION(mEventSet.IsInitialized(),
|
||||
"nsMetricsConfig::Init not called");
|
||||
return mHasConfig;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef void (nsMetricsConfig::*ForEachChildElementCallback)(nsIDOMElement *);
|
||||
|
||||
|
@ -114,14 +149,17 @@ private:
|
|||
void ForEachChildElement(nsIDOMElement *elem, ForEachChildElementCallback cb);
|
||||
|
||||
void ProcessToplevelElement(nsIDOMElement *elem);
|
||||
void ProcessConfigChild(nsIDOMElement *elem);
|
||||
void ProcessCollectorElement(nsIDOMElement *elem);
|
||||
|
||||
static PLDHashOperator PR_CALLBACK CopyKey(nsStringHashKey *key,
|
||||
void *userData);
|
||||
|
||||
nsTHashtable<nsStringHashKey> mEventSet;
|
||||
nsDataHashtable<nsStringHashKey,nsString> mNSURIToPrefixMap;
|
||||
PRInt32 mEventLimit;
|
||||
PRInt32 mUploadInterval;
|
||||
PRBool mHasConfig;
|
||||
};
|
||||
|
||||
#endif // nsMetricsConfig_h__
|
||||
|
|
|
@ -550,14 +550,8 @@ nsMetricsService::Upload()
|
|||
if (NS_SUCCEEDED(rv))
|
||||
mUploading = PR_TRUE;
|
||||
|
||||
// Since UploadData is uploading a copy of the data, we can delete the
|
||||
// original data file, and allow new events to be logged to a new file.
|
||||
nsCOMPtr<nsILocalFile> dataFile;
|
||||
GetDataFile(&dataFile);
|
||||
if (dataFile) {
|
||||
if (NS_FAILED(dataFile->Remove(PR_FALSE)))
|
||||
NS_WARNING("failed to remove data file");
|
||||
}
|
||||
// We keep the original data file until we know we've uploaded
|
||||
// successfully, or get a 4xx (bad request) response from the server.
|
||||
|
||||
// Reset event count and persist.
|
||||
mEventCount = 0;
|
||||
|
@ -633,7 +627,7 @@ nsMetricsService::OnStartRequest(nsIRequest *request, nsISupports *context)
|
|||
NS_ENSURE_STATE(!mConfigOutputStream);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
GetConfigFile(getter_AddRefs(file));
|
||||
GetConfigTempFile(getter_AddRefs(file));
|
||||
|
||||
nsCOMPtr<nsIFileOutputStream> out =
|
||||
do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID);
|
||||
|
@ -647,88 +641,171 @@ nsMetricsService::OnStartRequest(nsIRequest *request, nsISupports *context)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsMetricsService::LoadNewConfig(nsIFile *newConfig, nsIFile *oldConfig)
|
||||
{
|
||||
// Try to load the new config
|
||||
PRBool exists = PR_FALSE;
|
||||
newConfig->Exists(&exists);
|
||||
if (exists && NS_SUCCEEDED(mConfig.Load(newConfig))) {
|
||||
MS_LOG(("Successfully loaded new config"));
|
||||
|
||||
// Replace the old config file with the new one
|
||||
oldConfig->Remove(PR_FALSE);
|
||||
|
||||
nsString filename;
|
||||
oldConfig->GetLeafName(filename);
|
||||
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
oldConfig->GetParent(getter_AddRefs(directory));
|
||||
|
||||
newConfig->MoveTo(directory, filename);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
MS_LOG(("Couldn't load new config"));
|
||||
|
||||
// We want to disable collection until the next upload interval,
|
||||
// but we don't want to reset the upload interval to the default
|
||||
// if the server had supplied one. So, write out a new config
|
||||
// that just has the collectors disabled.
|
||||
mConfig.ClearEvents();
|
||||
|
||||
nsCOMPtr<nsILocalFile> lf = do_QueryInterface(oldConfig);
|
||||
nsresult rv = mConfig.Save(lf);
|
||||
if (NS_FAILED(rv)) {
|
||||
MS_LOG(("failed to save config: %d", rv));
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
nsMetricsService::RemoveDataFile()
|
||||
{
|
||||
nsCOMPtr<nsILocalFile> dataFile;
|
||||
GetDataFile(&dataFile);
|
||||
if (!dataFile) {
|
||||
MS_LOG(("Couldn't get data file to remove"));
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = dataFile->Remove(PR_FALSE);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MS_LOG(("Removed data file"));
|
||||
} else {
|
||||
MS_LOG(("Couldn't remove data file: %d", rv));
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMetricsService::OnStopRequest(nsIRequest *request, nsISupports *context,
|
||||
nsresult status)
|
||||
{
|
||||
MS_LOG(("OnStopRequest status = %x", status));
|
||||
|
||||
// Close the output stream for the download
|
||||
if (mConfigOutputStream) {
|
||||
mConfigOutputStream->Close();
|
||||
mConfigOutputStream = 0;
|
||||
}
|
||||
|
||||
// Load configuration file:
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
GetConfigFile(getter_AddRefs(file));
|
||||
|
||||
|
||||
// If the upload fails, we'll first retry at the last upload interval
|
||||
// we saw. This is useful in cases where the failure is transient.
|
||||
// If we fail kMaxRetries times, we'll defer trying again for a randomly
|
||||
// selected length of time 12-36 hours from the last attempt.
|
||||
// When the 12-36 hour deferred upload is attempted, we reset the state
|
||||
// and will again retry up to kMaxRetriesTimes at the default upload
|
||||
// interval.
|
||||
// There are several possible outcomes of our upload request:
|
||||
// 1. The server returns 200 OK
|
||||
// We consider the upload a success and delete the old data file.
|
||||
//
|
||||
// Any time an upload is successful, the failure count is reset to 0.
|
||||
// 2. The server returns a 4xx error
|
||||
// There was a problem with the uploaded data, so we delete the data file.
|
||||
//
|
||||
// 3. The server returns a 5xx error
|
||||
// There was a transient server-side problem. We keep the data file.
|
||||
//
|
||||
// In any of these cases, we parse the server response. If it contains
|
||||
// a <config>, then it replaces our current config file. If not, we reset
|
||||
// to the default configuration, but preserve the upload interval.
|
||||
// Currently we don't properly handle a 3xx response, it's treated like
|
||||
// a 4xx error (delete the data file).
|
||||
//
|
||||
// 4. A network error occurs (NS_FAILED(status) is true)
|
||||
// We keep the old data and the old config.
|
||||
//
|
||||
// In any of the error cases, we increment the retry count and schedule
|
||||
// a retry for the next upload interval. To start off, the retry is at
|
||||
// the upload interval specified by our config. If we fail kMaxRetries
|
||||
// times, we'll delete the data file and defer trying again until a randomly
|
||||
// selected time 12-36 hours from the last attempt. When the 12-36 hour
|
||||
// deferred upload is attempted, we reset the state and will again retry up
|
||||
// to kMaxRetriesTimes at the default upload interval.
|
||||
//
|
||||
// Any time an upload is successful, the retry count is reset to 0.
|
||||
|
||||
PRBool success = NS_SUCCEEDED(status);
|
||||
if (success) {
|
||||
nsCOMPtr<nsIFile> configTempFile; // the response we just downloaded
|
||||
GetConfigTempFile(getter_AddRefs(configTempFile));
|
||||
NS_ENSURE_STATE(configTempFile);
|
||||
|
||||
nsCOMPtr<nsIFile> configFile; // our old config
|
||||
GetConfigFile(getter_AddRefs(configFile));
|
||||
NS_ENSURE_STATE(configFile);
|
||||
|
||||
PRBool success = PR_FALSE, replacedConfig = PR_FALSE;
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
// If the request succeeded (200), we remove the old data file
|
||||
PRUint32 responseCode = 500;
|
||||
nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(request);
|
||||
NS_ENSURE_STATE(channel);
|
||||
channel->GetRequestSucceeded(&success);
|
||||
#ifdef PR_LOGGING
|
||||
PRUint32 responseCode;
|
||||
channel->GetResponseStatus(&responseCode);
|
||||
MS_LOG(("Server response code: %lu, success = %d", responseCode, success));
|
||||
#endif
|
||||
if (channel) {
|
||||
channel->GetResponseStatus(&responseCode);
|
||||
}
|
||||
MS_LOG(("Server response: %u", responseCode));
|
||||
|
||||
if (responseCode == 200) {
|
||||
success = PR_TRUE;
|
||||
RemoveDataFile();
|
||||
} else if (responseCode < 500) {
|
||||
// This was a request error, so delete the data file
|
||||
RemoveDataFile();
|
||||
}
|
||||
|
||||
replacedConfig = LoadNewConfig(configTempFile, configFile);
|
||||
} else {
|
||||
MS_LOG(("Request failed"));
|
||||
}
|
||||
|
||||
// Clean up the temp file if we didn't rename it
|
||||
if (!replacedConfig) {
|
||||
configTempFile->Remove(PR_FALSE);
|
||||
}
|
||||
|
||||
// Handle success or failure of the request
|
||||
if (success) {
|
||||
MS_LOG(("Successful upload"));
|
||||
success = NS_SUCCEEDED(mConfig.Load(file));
|
||||
if (success) {
|
||||
MS_LOG(("Read config file successfully, reset retry count to 0"));
|
||||
mRetryCount = 0;
|
||||
mRetryCount = 0;
|
||||
|
||||
// Clear the next-upload-time pref, in case it was set somehow.
|
||||
FlushClearPref(kUploadTimePref);
|
||||
MS_LOG(("Uploaded successfully and reset retry count"));
|
||||
} else if (++mRetryCount >= kMaxRetries) {
|
||||
RemoveDataFile();
|
||||
|
||||
static const int kSecondsPerHour = 60 * 60;
|
||||
mRetryCount = 0;
|
||||
|
||||
PRInt32 interval_sec = kSecondsPerHour * 12;
|
||||
PRUint32 random = 0;
|
||||
if (nsMetricsUtils::GetRandomNoise(&random, sizeof(random))) {
|
||||
interval_sec += (random % (24 * kSecondsPerHour));
|
||||
}
|
||||
}
|
||||
// If we couldn't get any random bytes, just use the default of
|
||||
// 12 hours.
|
||||
|
||||
if (!success) {
|
||||
PRInt32 interval = mConfig.UploadInterval();
|
||||
mConfig.Reset();
|
||||
mConfig.SetUploadInterval(interval);
|
||||
FlushIntPref(kUploadTimePref, (PR_Now() / PR_USEC_PER_SEC) + interval_sec);
|
||||
|
||||
MS_LOG(("Failed to upload"));
|
||||
if (++mRetryCount >= kMaxRetries) {
|
||||
static const int kSecondsPerHour = 60 * 60;
|
||||
mRetryCount = 0;
|
||||
|
||||
PRInt32 interval_sec = kSecondsPerHour * 12;
|
||||
PRUint32 random = 0;
|
||||
if (nsMetricsUtils::GetRandomNoise(&random, sizeof(random))) {
|
||||
interval_sec += (random % (24 * kSecondsPerHour));
|
||||
}
|
||||
// If we couldn't get any random bytes, just use the default of
|
||||
// 12 hours.
|
||||
|
||||
FlushIntPref(kUploadTimePref,
|
||||
(PR_Now() / PR_USEC_PER_SEC) + interval_sec);
|
||||
|
||||
MS_LOG(("Reached max retry count, deferring upload for %d seconds",
|
||||
interval_sec));
|
||||
// We'll initialize a timer for this interval below by calling
|
||||
// InitUploadTimer().
|
||||
}
|
||||
|
||||
if (file && NS_FAILED(file->Remove(PR_FALSE)))
|
||||
NS_WARNING("failed to remove config file");
|
||||
MS_LOG(("Reached max retry count, deferring upload for %d seconds",
|
||||
interval_sec));
|
||||
// We'll initialize a timer for this interval below by calling
|
||||
// InitUploadTimer().
|
||||
}
|
||||
|
||||
// Restart the upload timer for our next upload
|
||||
InitUploadTimer(PR_FALSE);
|
||||
|
||||
EnableCollectors();
|
||||
|
||||
mUploading = PR_FALSE;
|
||||
|
@ -1074,9 +1151,8 @@ nsMetricsService::CreateRoot()
|
|||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIDOMElement> root;
|
||||
rv = mDocument->CreateElementNS(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
|
||||
NS_LITERAL_STRING("log"),
|
||||
getter_AddRefs(root));
|
||||
rv = nsMetricsUtils::CreateElement(mDocument, NS_LITERAL_STRING("log"),
|
||||
getter_AddRefs(root));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mRoot = root;
|
||||
|
@ -1363,6 +1439,18 @@ nsMetricsService::GetConfigFile(nsIFile **result)
|
|||
file.swap(*result);
|
||||
}
|
||||
|
||||
void
|
||||
nsMetricsService::GetConfigTempFile(nsIFile **result)
|
||||
{
|
||||
nsCOMPtr<nsIFile> file;
|
||||
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
|
||||
if (file)
|
||||
file->AppendNative(NS_LITERAL_CSTRING("metrics-config.tmp"));
|
||||
|
||||
*result = nsnull;
|
||||
file.swap(*result);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMetricsService::GenerateClientID(nsCString &clientID)
|
||||
{
|
||||
|
@ -1551,3 +1639,11 @@ nsMetricsUtils::GetRandomNoise(void *buf, PRSize size)
|
|||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
nsMetricsUtils::CreateElement(nsIDOMDocument *ownerDoc,
|
||||
const nsAString &tag, nsIDOMElement **element)
|
||||
{
|
||||
return ownerDoc->CreateElementNS(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
|
||||
tag, element);
|
||||
}
|
||||
|
|
|
@ -177,6 +177,10 @@ private:
|
|||
// A reference to the local file containing our current configuration
|
||||
void GetConfigFile(nsIFile **result);
|
||||
|
||||
// A reference to the local file where we'll download the server response.
|
||||
// We don't replace the real config file until we know the new one is valid.
|
||||
void GetConfigTempFile(nsIFile **result);
|
||||
|
||||
// Generate a new random client id string
|
||||
nsresult GenerateClientID(nsCString &clientID);
|
||||
|
||||
|
@ -201,6 +205,16 @@ private:
|
|||
// Does the real work of GetWindowID().
|
||||
PRUint32 GetWindowIDInternal(nsIDOMWindow *window);
|
||||
|
||||
// Tries to load a new config. If successful, the old config file is
|
||||
// replaced with the new one. If the new config couldn't be loaded,
|
||||
// a config file is written which disables collection and preserves the
|
||||
// upload interval from the old config. Returns true if the new config
|
||||
// file was loaded successfully.
|
||||
PRBool LoadNewConfig(nsIFile *newConfig, nsIFile *oldConfig);
|
||||
|
||||
// Removes the existing data file (metrics.xml)
|
||||
void RemoveDataFile();
|
||||
|
||||
static PLDHashOperator PR_CALLBACK
|
||||
PruneDisabledCollectors(const nsAString &key,
|
||||
nsCOMPtr<nsIMetricsCollector> &value,
|
||||
|
@ -278,6 +292,11 @@ public:
|
|||
// from the OS. Returns true on success, or false if no random
|
||||
// bytes are available
|
||||
static PRBool GetRandomNoise(void *buf, PRSize size);
|
||||
|
||||
// Creates a new element in the metrics namespace, using the given
|
||||
// ownerDocument and tag.
|
||||
static nsresult CreateElement(nsIDOMDocument *ownerDoc,
|
||||
const nsAString &tag, nsIDOMElement **element);
|
||||
};
|
||||
|
||||
#endif // nsMetricsService_h__
|
||||
|
|
|
@ -43,13 +43,53 @@ include $(DEPTH)/config/autoconf.mk
|
|||
|
||||
MODULE = test_metrics
|
||||
|
||||
REQUIRES = \
|
||||
xpcom \
|
||||
metrics \
|
||||
necko \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
TestMetricsConfig.cpp \
|
||||
$(NULL)
|
||||
|
||||
SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX))
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../src \
|
||||
-I$(DIST)/public/nss \
|
||||
-I$(DIST)/private/nss \
|
||||
$(NULL)
|
||||
|
||||
LIBS = \
|
||||
../src/$(LIB_PREFIX)metrics_s.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)bz2.$(LIB_SUFFIX) \
|
||||
$(XPCOM_GLUE_LDOPTS) \
|
||||
$(NSPR_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_UNIT_FILES := $(wildcard $(srcdir)/unit/*.js)
|
||||
|
||||
# Give the unit tests absolute paths to the data and temp directories.
|
||||
# For cygwin, we need to convert the paths to native Windows paths.
|
||||
ifdef CYGWIN_WRAPPER
|
||||
TESTDATA_DIR := `cygpath -wa $(srcdir)/data`
|
||||
TEST_TMPDIR := `cygpath -wa .`
|
||||
else
|
||||
TESTDATA_DIR := `cd $(srcdir)/data; pwd`
|
||||
TEST_TMPDIR := `pwd`
|
||||
endif
|
||||
|
||||
libs:: $(_UNIT_FILES)
|
||||
$(INSTALL) $^ $(DIST)/bin/metrics_unit_tests
|
||||
|
||||
check::
|
||||
@echo Running tests...
|
||||
@for f in $(SIMPLE_PROGRAMS); do \
|
||||
echo $$f; $(RUN_TEST_PROGRAM) $(DIST)/bin/$$f \
|
||||
$(TESTDATA_DIR) $(TEST_TMPDIR); \
|
||||
done
|
||||
$(RUN_TEST_PROGRAM) $(DIST)/bin/test_all.sh \
|
||||
$(DIST)/bin/metrics_unit_tests
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Metrics extension.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Google Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Ryner <bryner@brianryner.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// This file defines common macros for C++ unit tests
|
||||
|
||||
#define ASSERT_TRUE_RET(cond, ret) \
|
||||
if (!cond) { \
|
||||
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define ASSERT_TRUE(cond) \
|
||||
if (!cond) { \
|
||||
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
|
||||
return ; \
|
||||
}
|
||||
|
||||
#define ASSERT_SUCCESS(res) ASSERT_TRUE(NS_SUCCEEDED(res))
|
||||
#define ASSERT_FALSE(cond) ASSERT_TRUE(! cond)
|
|
@ -0,0 +1,172 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Metrics extension.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Google Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Ryner <bryner@brianryner.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Unit test for nsMetricsConfig
|
||||
|
||||
#include "TestCommon.h"
|
||||
#include "nsMetricsConfig.h"
|
||||
#include "nsMetricsService.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsILocalFile.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// This singleton must exist in any code that links against libmetrics_s.
|
||||
// TODO: find a way to declare this in src/ while still allowing it to be
|
||||
// visible to nsMetricsModule.
|
||||
NS_DECL_CLASSINFO(nsMetricsService)
|
||||
|
||||
static int gTotalTests = 0;
|
||||
static int gPassedTests = 0;
|
||||
|
||||
void TestLoad(const char *testdata_path)
|
||||
{
|
||||
++gTotalTests;
|
||||
|
||||
nsMetricsConfig config;
|
||||
ASSERT_TRUE(config.Init());
|
||||
|
||||
nsCOMPtr<nsILocalFile> dataFile;
|
||||
NS_NewNativeLocalFile(nsDependentCString(testdata_path),
|
||||
PR_TRUE, getter_AddRefs(dataFile));
|
||||
ASSERT_TRUE(dataFile);
|
||||
|
||||
ASSERT_SUCCESS(dataFile->AppendNative(
|
||||
NS_LITERAL_CSTRING("test_config.xml")));
|
||||
ASSERT_SUCCESS(config.Load(dataFile));
|
||||
|
||||
ASSERT_TRUE(config.IsEventEnabled(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
|
||||
NS_LITERAL_STRING("foo")));
|
||||
ASSERT_TRUE(config.IsEventEnabled(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
|
||||
NS_LITERAL_STRING("bar")));
|
||||
ASSERT_FALSE(config.IsEventEnabled(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
|
||||
NS_LITERAL_STRING("baz")));
|
||||
|
||||
ASSERT_TRUE(config.EventLimit() == 200);
|
||||
ASSERT_TRUE(config.UploadInterval() == 1000);
|
||||
ASSERT_TRUE(config.HasConfig());
|
||||
++gPassedTests;
|
||||
}
|
||||
|
||||
// Returns true if the contents of |file| match |contents|.
|
||||
static PRBool CheckFileContents(nsILocalFile *file, const char *contents)
|
||||
{
|
||||
nsCString nativePath;
|
||||
file->GetNativePath(nativePath);
|
||||
|
||||
// Now read in the file contents and compare to the expected output
|
||||
PRFileInfo info;
|
||||
ASSERT_TRUE_RET(PR_GetFileInfo(nativePath.get(), &info) == PR_SUCCESS,
|
||||
PR_FALSE);
|
||||
|
||||
char *buf = new char[info.size + 1];
|
||||
ASSERT_TRUE_RET(buf, PR_FALSE);
|
||||
|
||||
PRFileDesc *fd = PR_Open(nativePath.get(), PR_RDONLY, 0);
|
||||
ASSERT_TRUE_RET(fd, PR_FALSE);
|
||||
|
||||
ASSERT_TRUE_RET(PR_Read(fd, buf, info.size) == info.size, PR_FALSE);
|
||||
PR_Close(fd);
|
||||
buf[info.size] = '\0';
|
||||
|
||||
// Leave the file in place if the test failed
|
||||
ASSERT_TRUE_RET(!strcmp(buf, contents), PR_FALSE);
|
||||
PR_Delete(nativePath.get());
|
||||
delete[] buf;
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void TestSave(const char *temp_data_path)
|
||||
{
|
||||
++gTotalTests;
|
||||
static const char kFilename[] = "test-save.xml";
|
||||
static const char kExpectedContents[] =
|
||||
"<response xmlns=\"http://www.mozilla.org/metrics\"><config>"
|
||||
"<collectors>"
|
||||
"<collector type=\"uielement\"/>"
|
||||
"</collectors>"
|
||||
"<limit events=\"300\"/>"
|
||||
"<upload interval=\"500\"/>"
|
||||
"</config></response>";
|
||||
|
||||
nsMetricsConfig config;
|
||||
ASSERT_TRUE(config.Init());
|
||||
|
||||
// The data file goes to the current directory
|
||||
config.SetEventEnabled(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
|
||||
NS_LITERAL_STRING("uielement"), PR_TRUE);
|
||||
config.SetUploadInterval(500);
|
||||
config.SetEventLimit(300);
|
||||
|
||||
nsCOMPtr<nsILocalFile> outFile;
|
||||
NS_NewNativeLocalFile(nsDependentCString(temp_data_path),
|
||||
PR_TRUE, getter_AddRefs(outFile));
|
||||
ASSERT_TRUE(outFile);
|
||||
ASSERT_SUCCESS(outFile->AppendNative(nsDependentCString(kFilename)));
|
||||
|
||||
ASSERT_SUCCESS(config.Save(outFile));
|
||||
ASSERT_TRUE(CheckFileContents(outFile, kExpectedContents));
|
||||
|
||||
// Now test with no collectors
|
||||
static const char kExpectedOutputNoEvents[] =
|
||||
"<response xmlns=\"http://www.mozilla.org/metrics\"><config>"
|
||||
"<collectors/>"
|
||||
"<limit events=\"300\"/>"
|
||||
"<upload interval=\"500\"/>"
|
||||
"</config></response>";
|
||||
|
||||
config.ClearEvents();
|
||||
ASSERT_SUCCESS(config.Save(outFile));
|
||||
ASSERT_TRUE(CheckFileContents(outFile, kExpectedOutputNoEvents));
|
||||
|
||||
++gPassedTests;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s test_data_path temp_data_path\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TestLoad(argv[1]);
|
||||
TestSave(argv[2]);
|
||||
|
||||
printf("%d/%d tests passed\n", gPassedTests, gTotalTests);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<response xmlns="http://www.mozilla.org/metrics"><config><collectors><collector type="foo"/><collector type="bar"/></collectors><event limit="200"/><upload interval="1000"/></config></response>
|
Загрузка…
Ссылка в новой задаче