diff options
13 files changed, 323 insertions, 51 deletions
diff --git a/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.cc b/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.cc index c7c9cca..53e41dc 100644 --- a/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.cc +++ b/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.cc @@ -5,10 +5,34 @@ #include "chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h" #include "base/sys_info.h" +#include "chrome/common/chrome_notification_types.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/notification_types.h" namespace extensions { using api::experimental_system_info_cpu::CpuInfo; +using api::experimental_system_info_cpu::CpuUpdateInfo; +using content::BrowserThread; + +// Default sampling interval is 1000ms. +const unsigned int kDefaultSamplingIntervalMs = 1000; + +CpuInfoProvider::CpuInfoProvider() + : is_sampling_started_(false), + sampling_timer_(NULL), + sampling_interval_(kDefaultSamplingIntervalMs) { + registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, + content::NotificationService::AllSources()); +} + +CpuInfoProvider::~CpuInfoProvider() { + DCHECK(sampling_timer_ == NULL); + registrar_.RemoveAll(); +} bool CpuInfoProvider::QueryInfo(CpuInfo* info) { if (info == NULL) @@ -20,14 +44,22 @@ bool CpuInfoProvider::QueryInfo(CpuInfo* info) { return true; } -bool CpuInfoProvider::StartSampling(const QueryCpuTimeCallback& callback) { - // TODO(hongbo): Implement sampling functionality for event router. - return false; +void CpuInfoProvider::StartSampling(const SamplingCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&CpuInfoProvider::StartSamplingOnFileThread, + base::Unretained(this), callback)); } -bool CpuInfoProvider::StopSampling() { - // TODO(hongbo): Implement sampling functionality for event router. - return false; +void CpuInfoProvider::StopSampling() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&CpuInfoProvider::StopSamplingOnFileThread, + base::Unretained(this))); } // static @@ -35,4 +67,79 @@ CpuInfoProvider* CpuInfoProvider::Get() { return CpuInfoProvider::GetInstance<CpuInfoProvider>(); } +void CpuInfoProvider::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_APP_TERMINATING: + StopSampling(); + break; + default: + NOTREACHED(); + break; + } +} + +void CpuInfoProvider::StartSamplingOnFileThread( + const SamplingCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + if (is_sampling_started_) + return; + + if (!QueryCpuTimePerProcessor(&baseline_cpu_time_)) + return; + + is_sampling_started_ = true; + callback_ = callback; + DCHECK(!sampling_timer_) << "The sampling timer has leaked. Did you forgot " + "to call StopSampling?"; + // Should be deleted on FILE thread. + sampling_timer_ = new base::RepeatingTimer<CpuInfoProvider>(); + sampling_timer_->Start(FROM_HERE, + base::TimeDelta::FromMilliseconds(sampling_interval_), + this, &CpuInfoProvider::DoSample); +} + +void CpuInfoProvider::StopSamplingOnFileThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + if (!is_sampling_started_) return; + delete sampling_timer_; + sampling_timer_ = NULL; + baseline_cpu_time_.clear(); + is_sampling_started_ = false; +} + +void CpuInfoProvider::DoSample() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + std::vector<CpuTime> next_cpu_time; + if (!QueryCpuTimePerProcessor(&next_cpu_time)) + return; + + // Calculate the CPU usage information from the next_cpu_time and + // |baseline_cpu_time_|. + double total_usage = 0; + scoped_ptr<CpuUpdateInfo> info(new CpuUpdateInfo()); + for (size_t i = 0; i < next_cpu_time.size(); ++i) { + double total_time = + (next_cpu_time[i].user - baseline_cpu_time_[i].user) + + (next_cpu_time[i].kernel - baseline_cpu_time_[i].kernel) + + (next_cpu_time[i].idle - baseline_cpu_time_[i].idle); + double idle_time = next_cpu_time[i].idle - baseline_cpu_time_[i].idle; + + double usage = 0; + if (total_time != 0) + usage = (100 - idle_time * 100 / total_time); + info->usage_per_processor.push_back(usage); + total_usage += usage; + } + + info->average_usage = total_usage / next_cpu_time.size(); + if (!callback_.is_null()) + callback_.Run(info.Pass()); + // Use next_cpu_time as baseline_cpu_time_ for the next sampling cycle. + baseline_cpu_time_.swap(next_cpu_time); +} + } // namespace extensions diff --git a/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h b/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h index a3c8b29..7622a55 100644 --- a/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h +++ b/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h @@ -8,16 +8,22 @@ #include <vector> +#include "base/timer.h" #include "chrome/common/extensions/api/experimental_system_info_cpu.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" namespace extensions { class CpuInfoProvider - : public SystemInfoProvider<api::experimental_system_info_cpu::CpuInfo> { + : public content::NotificationObserver, + public SystemInfoProvider<api::experimental_system_info_cpu::CpuInfo> { public: - typedef base::Callback<api::experimental_system_info_cpu::CpuUpdateInfo> - QueryCpuTimeCallback; - virtual ~CpuInfoProvider() {} + typedef base::Callback< + void(scoped_ptr<api::experimental_system_info_cpu::CpuUpdateInfo>)> + SamplingCallback; + + virtual ~CpuInfoProvider(); // Overriden from SystemInfoProvider<CpuInfo>. virtual bool QueryInfo( @@ -25,28 +31,64 @@ class CpuInfoProvider // Start sampling the CPU usage. The callback gets called when one sampling // cycle is completed periodically with the CPU updated usage info for each - // processors. Return true if it succeeds to start, otherwise, false is - // returned. - bool StartSampling(const QueryCpuTimeCallback& callback); + // processors. It gets called on UI thread, the |callback| gets called on the + // FILE thread. + void StartSampling(const SamplingCallback& callback); - // Stop the sampling cycle. Return true if it succeeds to stop. - bool StopSampling(); + // Stop the sampling cycle. Called on the FILE thread. + void StopSampling(); // Return the single shared instance of CpuInfoProvider. static CpuInfoProvider* Get(); private: + friend class SystemInfoProvider<api::experimental_system_info_cpu::CpuInfo>; + friend class MockCpuInfoProviderImpl; + // The amount of time that CPU spent on performing different kinds of work. // It is used to calculate the usage percent for processors. struct CpuTime { + CpuTime() : user(0), kernel(0), idle(0) {} int64 user; // user mode. int64 kernel; // kernel mode. int64 idle; // twiddling thumbs. }; + CpuInfoProvider(); + + // content::NotificationObserver implementation. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + // Platform specific implementation for querying the CPU time information // for each processor. - bool QueryCpuTimePerProcessor(std::vector<CpuTime>* times); + virtual bool QueryCpuTimePerProcessor(std::vector<CpuTime>* times); + + // Start and stop sampling on the FILE thread. + void StartSamplingOnFileThread(const SamplingCallback& callback); + void StopSamplingOnFileThread(); + + // Called when the sampling timer is triggered. + void DoSample(); + + content::NotificationRegistrar registrar_; + + // Indicates whether the CPU sampling is started. + bool is_sampling_started_; + + // The sampling value returned from the most recent QueryCpuTimePerProcessor + // call. + std::vector<CpuTime> baseline_cpu_time_; + + // The callback which will be called when one sampling cycle is completed. + SamplingCallback callback_; + + // The timer used for polling CPU time periodically. Lives on FILE thread. + base::RepeatingTimer<CpuInfoProvider>* sampling_timer_; + + // The time interval for sampling, in milliseconds. + int sampling_interval_; }; } // namespace extensions diff --git a/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider_android.cc b/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider_android.cc new file mode 100644 index 0000000..851df4d --- /dev/null +++ b/chrome/browser/extensions/api/system_info_cpu/cpu_info_provider_android.cc @@ -0,0 +1,14 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h" + +namespace extensions { + +bool CpuInfoProvider::QueryCpuTimePerProcessor(std::vector<CpuTime>* times) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/system_info_cpu/system_info_cpu_apitest.cc b/chrome/browser/extensions/api/system_info_cpu/system_info_cpu_apitest.cc index b516c97..21f4e17 100644 --- a/chrome/browser/extensions/api/system_info_cpu/system_info_cpu_apitest.cc +++ b/chrome/browser/extensions/api/system_info_cpu/system_info_cpu_apitest.cc @@ -5,7 +5,9 @@ #include "base/message_loop.h" #include "chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/common/chrome_switches.h" +#include "chrome/test/base/ui_test_utils.h" namespace extensions { @@ -13,18 +15,44 @@ using api::experimental_system_info_cpu::CpuInfo; class MockCpuInfoProviderImpl : public CpuInfoProvider { public: - MockCpuInfoProviderImpl() {} + MockCpuInfoProviderImpl() : num_of_processors_(4) { + // Set sampling interval to 200ms for testing. + sampling_interval_ = 200; + } ~MockCpuInfoProviderImpl() {} virtual bool QueryInfo(CpuInfo* info) OVERRIDE { if (!info) return false; - info->num_of_processors = 4; + info->num_of_processors = num_of_processors_; info->arch_name = "x86"; info->model_name = "unknown"; return true; } + + private: + virtual bool QueryCpuTimePerProcessor(std::vector<CpuTime>* times) OVERRIDE { + DCHECK(times); + + times->clear(); + static int user_step = 3; + static int kernel_step = 2; + static int idle_step = 1; + static int count = 0; + for (int i = 0; i < num_of_processors_; i++) { + CpuTime time; + time.user += user_step * count; + time.kernel += kernel_step * count; + time.idle += idle_step * count; + times->push_back(time); + + count++; + } + return true; + } + + int num_of_processors_; }; class SystemInfoCpuApiTest: public ExtensionApiTest { @@ -40,10 +68,6 @@ class SystemInfoCpuApiTest: public ExtensionApiTest { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { ExtensionApiTest::SetUpInProcessBrowserTestFixture(); message_loop_.reset(new MessageLoop(MessageLoop::TYPE_UI)); - - CpuInfoProvider* provider = new MockCpuInfoProviderImpl(); - // The provider is owned by the single CpuInfoProvider instance. - CpuInfoProvider::InitializeForTesting(provider); } private: @@ -51,6 +75,9 @@ class SystemInfoCpuApiTest: public ExtensionApiTest { }; IN_PROC_BROWSER_TEST_F(SystemInfoCpuApiTest, Cpu) { + CpuInfoProvider* provider = new MockCpuInfoProviderImpl(); + // The provider is owned by the single CpuInfoProvider instance. + CpuInfoProvider::InitializeForTesting(provider); ASSERT_TRUE(RunExtensionTest("systeminfo/cpu")) << message_; } diff --git a/chrome/browser/extensions/event_names.cc b/chrome/browser/extensions/event_names.cc index 0ae9498..4706282 100644 --- a/chrome/browser/extensions/event_names.cc +++ b/chrome/browser/extensions/event_names.cc @@ -72,6 +72,7 @@ const char kBluetoothOnPowerChanged[] = const char kOnPushMessage[] = "experimental.pushMessaging.onMessage"; +const char kOnCpuUpdated[] = "experimental.systemInfo.cpu.onUpdated"; const char kOnStorageAvailableCapacityChanged[] = "experimental.systemInfo.storage.onAvailableCapacityChanged"; const char kOnStorageAdded[] = "experimental.systemInfo.storage.onAdded"; diff --git a/chrome/browser/extensions/event_names.h b/chrome/browser/extensions/event_names.h index a196de8..c661d48 100644 --- a/chrome/browser/extensions/event_names.h +++ b/chrome/browser/extensions/event_names.h @@ -77,7 +77,8 @@ extern const char kBluetoothOnPowerChanged[]; // Push messaging. extern const char kOnPushMessage[]; -// SystemInfo storage +// systemInfo event names. +extern const char kOnCpuUpdated[]; extern const char kOnStorageAvailableCapacityChanged[]; extern const char kOnStorageAdded[]; extern const char kOnStorageRemoved[]; diff --git a/chrome/browser/extensions/system_info_event_router.cc b/chrome/browser/extensions/system_info_event_router.cc index d2c7933..f6dc20a 100644 --- a/chrome/browser/extensions/system_info_event_router.cc +++ b/chrome/browser/extensions/system_info_event_router.cc @@ -9,17 +9,34 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/event_router_forwarder.h" #include "chrome/browser/extensions/event_names.h" +#include "chrome/browser/extensions/api/system_info_cpu/cpu_info_provider.h" #include "chrome/browser/extensions/api/system_info_storage/storage_info_provider.h" +#include "chrome/common/extensions/api/experimental_system_info_cpu.h" #include "chrome/common/extensions/api/experimental_system_info_storage.h" namespace extensions { +using api::experimental_system_info_cpu::CpuUpdateInfo; using api::experimental_system_info_storage::StorageUnitInfo; using api::experimental_system_info_storage::StorageChangeInfo; using content::BrowserThread; +namespace { + const char kSystemInfoEventPrefix[] = "experimental.systemInfo"; const char kStorageEventPrefix[] = "experimental.systemInfo.storage"; +const char kCpuEventPrefix[] = "experimental.systemInfo.cpu"; + +static bool IsStorageEvent(const std::string& event_name) { + return event_name.compare(0, strlen(kStorageEventPrefix), + kStorageEventPrefix) == 0; +} + +static bool IsCpuEvent(const std::string& event_name) { + return event_name.compare(0, strlen(kCpuEventPrefix), kCpuEventPrefix) == 0; +} + +} // namespace // static SystemInfoEventRouter* SystemInfoEventRouter::GetInstance() { @@ -37,10 +54,19 @@ void SystemInfoEventRouter::AddEventListener(const std::string& event_name) { if (watching_event_set_.count(event_name) > 1) return; // Start watching the |event_name| event if the first event listener arrives. - std::string prefix(kStorageEventPrefix); - if (event_name.compare(0, prefix.size(), kStorageEventPrefix) == 0) { + // For systemInfo.storage event. + if (IsStorageEvent(event_name)) { // TODO (hongbo): Start watching storage device information via calling // SystemMonitor::StartWatchingStorage. + return; + } + + // For systemInfo.cpu event. + if (IsCpuEvent(event_name)) { + CpuInfoProvider::Get()->StartSampling( + base::Bind(&SystemInfoEventRouter::OnNextCpuSampling, + base::Unretained(this))); + return; } } @@ -52,13 +78,14 @@ void SystemInfoEventRouter::RemoveEventListener( // In case of the last event listener is removed, we need to stop watching // it to avoid unnecessary overhead. - std::string prefix(kStorageEventPrefix); - if (event_name.compare( - 0, prefix.size(), kStorageEventPrefix) == 0) { + if (IsStorageEvent(event_name)) { // TODO(hongbo): Stop watching storage device information via calling // SystemMonitor::StopWatchingStorage. return; } + if (IsCpuEvent(event_name)) { + CpuInfoProvider::Get()->StopSampling(); + } } // static @@ -102,4 +129,11 @@ void SystemInfoEventRouter::DispatchEvent(const std::string& event_name, BroadcastEventToRenderers(event_name, args.Pass(), GURL()); } +void SystemInfoEventRouter::OnNextCpuSampling(scoped_ptr<CpuUpdateInfo> info) { + scoped_ptr<base::ListValue> args(new base::ListValue()); + args->Append(info->ToValue().release()); + + DispatchEvent(event_names::kOnCpuUpdated, args.Pass()); +} + } // namespace extensions diff --git a/chrome/browser/extensions/system_info_event_router.h b/chrome/browser/extensions/system_info_event_router.h index a6e22a9..2fd8b4f 100644 --- a/chrome/browser/extensions/system_info_event_router.h +++ b/chrome/browser/extensions/system_info_event_router.h @@ -13,6 +13,16 @@ namespace extensions { +namespace api { + +namespace experimental_system_info_cpu { + +struct CpuUpdateInfo; + +} // namespace experimental_system_info_cpu + +} // namespace api + // Event router for systemInfo API. It is a singleton instance shared by // multiple profiles. // TODO(hongbo): It should derive from SystemMonitor::DevicesChangedObserver. @@ -46,11 +56,15 @@ class SystemInfoEventRouter { SystemInfoEventRouter(); virtual ~SystemInfoEventRouter(); - // Called on the UI thread to dispatch the systemInfo event to all extension + // Called from any thread to dispatch the systemInfo event to all extension // processes cross multiple profiles. void DispatchEvent(const std::string& event_name, scoped_ptr<base::ListValue> args); + // The callback for CPU sampling cycle. Called from FILE thread. + void OnNextCpuSampling( + scoped_ptr<api::experimental_system_info_cpu::CpuUpdateInfo> info); + // Used to record the event names being watched. std::multiset<std::string> watching_event_set_; diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 48d98b7..efa7581 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -270,6 +270,7 @@ 'browser/extensions/api/system_info_cpu/cpu_info_provider_linux.cc', 'browser/extensions/api/system_info_cpu/cpu_info_provider_mac.cc', 'browser/extensions/api/system_info_cpu/cpu_info_provider_win.cc', + 'browser/extensions/api/system_info_cpu/cpu_info_provider_android.cc', 'browser/extensions/api/system_info_cpu/system_info_cpu_api.cc', 'browser/extensions/api/system_info_cpu/system_info_cpu_api.h', 'browser/extensions/api/system_info_storage/storage_info_provider.cc', @@ -681,6 +682,8 @@ ['include', '^browser/extensions/api/proxy/proxy_api_constants.cc'], ['include', '^browser/extensions/api/push_messaging/push_messaging_api.cc'], ['include', '^browser/extensions/api/runtime/runtime_api.cc'], + ['include', '^browser/extensions/api/system_info_cpu/cpu_info_provider.cc'], + ['include', '^browser/extensions/api/system_info_cpu/cpu_info_provider_android.cc'], ['include', '^browser/extensions/api/tabs/tabs_constants.cc'], ['include', '^browser/extensions/api/web_navigation/frame_navigation_state.cc'], ['include', '^browser/extensions/api/web_navigation/web_navigation_api.cc'], diff --git a/chrome/common/extensions/api/experimental_system_info_cpu.idl b/chrome/common/extensions/api/experimental_system_info_cpu.idl index cc01114..5e2308b 100644 --- a/chrome/common/extensions/api/experimental_system_info_cpu.idl +++ b/chrome/common/extensions/api/experimental_system_info_cpu.idl @@ -28,4 +28,10 @@ namespace experimental.systemInfo.cpu { // Get CPU information. static void get(CpuInfoCallback callback); }; + + interface Events { + // Fired periodically to report CPU history usage information. The default + // period interval is 1 seconds. + static void onUpdated(CpuUpdateInfo info); + }; }; diff --git a/chrome/test/data/extensions/api_test/systeminfo/cpu/manifest.json b/chrome/test/data/extensions/api_test/systeminfo/cpu/manifest.json index d88baff..3e7c37d 100644 --- a/chrome/test/data/extensions/api_test/systeminfo/cpu/manifest.json +++ b/chrome/test/data/extensions/api_test/systeminfo/cpu/manifest.json @@ -5,7 +5,7 @@ "permissions": ["experimental"], "app": { "background": { - "scripts": ["test_cpuinfo_api.js"] + "scripts": ["test_cpu_api.js"] } } } diff --git a/chrome/test/data/extensions/api_test/systeminfo/cpu/test_cpu_api.js b/chrome/test/data/extensions/api_test/systeminfo/cpu/test_cpu_api.js new file mode 100644 index 0000000..497ab5c --- /dev/null +++ b/chrome/test/data/extensions/api_test/systeminfo/cpu/test_cpu_api.js @@ -0,0 +1,44 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// systeminfo.cpu api test +// browser_tests.exe --gtest_filter=SystemInfoCpuApiTest.* + +chrome.systemInfo = chrome.experimental.systemInfo; + +var userStep = 3; +var kernelStep = 2; +var idleStep = 1; +function calculateUsage(count) { + return (100 - idleStep * 100/(userStep + kernelStep + idleStep)); +} + +chrome.test.runTests([ + function testGet() { + for(var i = 0; i < 20; ++i) { + chrome.systemInfo.cpu.get(chrome.test.callbackPass(function(result) { + chrome.test.assertEq(4, result.numOfProcessors); + chrome.test.assertEq("x86", result.archName); + chrome.test.assertEq("unknown", result.modelName); + })); + } + }, + + function testUpdatedEvent() { + var numOfUpdatedEvent = 0; + var doneUpdatedEvent = chrome.test.listenForever( + chrome.systemInfo.cpu.onUpdated, + function listener(updateInfo) { + var expectedUsage = calculateUsage(numOfUpdatedEvent); + chrome.test.assertEq(updateInfo.averageUsage, expectedUsage); + + chrome.test.assertEq(updateInfo.usagePerProcessor.length, 4); + for (var i = 0; i < updateInfo.usagePerProcessor.length; ++i) + chrome.test.assertEq(updateInfo.usagePerProcessor[i], expectedUsage); + if (++numOfUpdatedEvent > 5) + doneUpdatedEvent(); + }); + } +]); + diff --git a/chrome/test/data/extensions/api_test/systeminfo/cpu/test_cpuinfo_api.js b/chrome/test/data/extensions/api_test/systeminfo/cpu/test_cpuinfo_api.js deleted file mode 100644 index cbeec68..0000000 --- a/chrome/test/data/extensions/api_test/systeminfo/cpu/test_cpuinfo_api.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// systeminfo.cpu api test -// browser_tests.exe --gtest_filter=SystemInfoCpuApiTest.* - -chrome.systemInfo = chrome.experimental.systemInfo; - -chrome.test.runTests([ - function testGet() { - for(var i = 0; i < 20; ++i) { - chrome.systemInfo.cpu.get(chrome.test.callbackPass(function(result) { - chrome.test.assertEq(4, result.numOfProcessors); - chrome.test.assertEq("x86", result.archName); - chrome.test.assertEq("unknown", result.modelName); - })); - } - }, -]); - |