@ -34,6 +34,7 @@ class MetricsCollectorTest(unittest.TestCase):
# Keep track of the URL requests, file reads/writes and subprocess spawned.
self . urllib2 = mock . Mock ( )
self . print_notice = mock . Mock ( )
self . print_version_change = mock . Mock ( )
self . Popen = mock . Mock ( )
self . FileWrite = mock . Mock ( )
self . FileRead = mock . Mock ( )
@ -43,6 +44,9 @@ class MetricsCollectorTest(unittest.TestCase):
mock . patch ( ' metrics.gclient_utils.FileWrite ' , self . FileWrite ) . start ( )
mock . patch ( ' metrics.gclient_utils.FileRead ' , self . FileRead ) . start ( )
mock . patch ( ' metrics.metrics_utils.print_notice ' , self . print_notice ) . start ( )
mock . patch (
' metrics.metrics_utils.print_version_change ' ,
self . print_version_change ) . start ( )
# Patch the methods used to get the system information, so we have a known
# environment.
@ -90,7 +94,8 @@ class MetricsCollectorTest(unittest.TestCase):
self . assertEqual ( self . collector . config . countdown , 10 )
self . assert_writes_file (
self . config_file , { ' is-googler ' : True , ' countdown ' : 10 , ' opt-in ' : None } )
self . config_file ,
{ ' is-googler ' : True , ' countdown ' : 10 , ' opt-in ' : None , ' version ' : 0 } )
def test_writes_config_if_not_exists_non_googler ( self ) :
self . FileRead . side_effect = [ IOError ( 2 , " No such file or directory " ) ]
@ -104,7 +109,7 @@ class MetricsCollectorTest(unittest.TestCase):
self . assert_writes_file (
self . config_file ,
{ ' is-googler ' : False , ' countdown ' : 10 , ' opt-in ' : None })
{ ' is-googler ' : False , ' countdown ' : 10 , ' opt-in ' : None , ' version ' : 0 })
def test_disables_metrics_if_cant_write_config ( self ) :
self . FileRead . side_effect = [ IOError ( 2 , ' No such file or directory ' ) ]
@ -132,7 +137,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_collects_system_information ( self ) :
""" Tests that we collect information about the runtime environment. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : null , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -144,7 +149,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_collects_added_metrics ( self ) :
""" Tests that we can collect custom metrics. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : null , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -156,7 +161,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_collects_metrics_when_opted_in ( self ) :
""" Tests that metrics are collected when the user opts-in. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 1234, " opt-in " : true }'
' { " is-googler " : true, " countdown " : 1234, " opt-in " : true , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -183,7 +188,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_metrics_collection_disabled_not_googler ( self ) :
""" Tests that metrics collection is disabled for non googlers. """
self . FileRead . side_effect = [
' { " is-googler " : false, " countdown " : 0, " opt-in " : null }'
' { " is-googler " : false, " countdown " : 0, " opt-in " : null , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -201,7 +206,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_metrics_collection_disabled_opted_out ( self ) :
""" Tests that metrics collection is disabled if the user opts out. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : false }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : false , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -219,7 +224,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_metrics_collection_disabled_non_zero_countdown ( self ) :
""" Tests that metrics collection is disabled until the countdown expires. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 1, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 1, " opt-in " : null , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -237,7 +242,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_handles_exceptions ( self ) :
""" Tests that exception are caught and we exit with an appropriate code. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : true }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : true , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -252,7 +257,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_handles_system_exit ( self ) :
""" Tests that the sys.exit code is respected and metrics are collected. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : true }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : true , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -268,7 +273,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_handles_system_exit_non_zero ( self ) :
""" Tests that the sys.exit code is respected and metrics are collected. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : true }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : true , " version " : 0 }'
]
@self.collector.collect_metrics ( ' fun ' )
def fun ( ) :
@ -284,7 +289,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_prints_notice_non_zero_countdown ( self ) :
""" Tests that a notice is printed while the countdown is non-zero. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 1234, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 1234, " opt-in " : null , " version " : 0 }'
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
@ -295,7 +300,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_prints_notice_zero_countdown ( self ) :
""" Tests that a notice is printed when the countdown reaches 0. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : null , " version " : 0 }'
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
@ -306,7 +311,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_doesnt_print_notice_opted_in ( self ) :
""" Tests that a notice is not printed when the user opts-in. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : true }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : true , " version " : 0 }'
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
@ -317,7 +322,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_doesnt_print_notice_opted_out ( self ) :
""" Tests that a notice is not printed when the user opts-out. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : false }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : false , " version " : 0 }'
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
@ -338,7 +343,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_print_notice_handles_exceptions ( self ) :
""" Tests that exception are caught and we exit with an appropriate code. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : null , " version " : 0 }'
]
# print_notice should catch the exception, print it and invoke sys.exit()
with self . assertRaises ( SystemExit ) as cm :
@ -350,7 +355,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_print_notice_handles_system_exit ( self ) :
""" Tests that the sys.exit code is respected and a notice is displayed. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : null , " version " : 0 }'
]
# print_notice should catch the exception, print it and invoke sys.exit()
with self . assertRaises ( SystemExit ) as cm :
@ -362,7 +367,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_print_notice_handles_system_exit_non_zero ( self ) :
""" Tests that the sys.exit code is respected and a notice is displayed. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : null , " version " : 0 }'
]
# When an exception is raised, we should catch it, update exit-code,
# collect metrics, and re-raise it.
@ -375,7 +380,7 @@ class MetricsCollectorTest(unittest.TestCase):
def test_counts_down ( self ) :
""" Tests that the countdown works correctly. """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 10, " opt-in " : null }'
' { " is-googler " : true, " countdown " : 10, " opt-in " : null , " version " : 0 }'
]
# We define multiple functions to ensure it has no impact on countdown.
@ -403,13 +408,14 @@ class MetricsCollectorTest(unittest.TestCase):
self . print_notice . assert_called_once_with ( 10 )
self . assert_writes_file (
self . config_file , { ' is-googler ' : True , ' countdown ' : 9 , ' opt-in ' : None } )
self . config_file ,
{ ' is-googler ' : True , ' countdown ' : 9 , ' opt-in ' : None , ' version ' : 0 } )
def test_nested_functions ( self ) :
""" Tests that a function can call another function for which metrics are
collected . """
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : true }'
' { " is-googler " : true, " countdown " : 0, " opt-in " : true , " version " : 0 }'
]
@self.collector.collect_metrics ( ' barn ' )
@ -426,6 +432,148 @@ class MetricsCollectorTest(unittest.TestCase):
# Assert that we collected metrics for fun, but not for barn.
self . assert_collects_metrics ( { ' fun-metric ' : 1001 } )
@mock.patch ( ' metrics.metrics_utils.CURRENT_VERSION ' , 5 )
def test_version_change_from_hasnt_decided ( self ) :
# The user has not decided yet, and the countdown hasn't reached 0, so we're
# not collecting metrics.
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 9, " opt-in " : null, " version " : 0} '
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
self . collector . add ( ' foo-metric ' , 1 )
self . assertEqual ( cm . exception . code , 0 )
# We display the notice informing the user of the changes.
self . print_version_change . assert_called_once_with ( 0 )
# But the countdown is not reset.
self . assert_writes_file (
self . config_file ,
{ ' is-googler ' : True , ' countdown ' : 8 , ' opt-in ' : None , ' version ' : 0 } )
# And no metrics are uploaded.
self . assertFalse ( self . Popen . called )
@mock.patch ( ' metrics.metrics_utils.CURRENT_VERSION ' , 5 )
def test_version_change_from_opted_in_by_default ( self ) :
# The user has not decided yet, but the countdown has reached 0, and we're
# collecting metrics.
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : null, " version " : 0} '
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
self . collector . add ( ' foo-metric ' , 1 )
self . assertEqual ( cm . exception . code , 0 )
# We display the notice informing the user of the changes.
self . print_version_change . assert_called_once_with ( 0 )
# We reset the countdown.
self . assert_writes_file (
self . config_file ,
{ ' is-googler ' : True , ' countdown ' : 9 , ' opt-in ' : None , ' version ' : 0 } )
# No metrics are uploaded.
self . assertFalse ( self . Popen . called )
@mock.patch ( ' metrics.metrics_utils.CURRENT_VERSION ' , 5 )
def test_version_change_from_opted_in ( self ) :
# The user has opted in, and we're collecting metrics.
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : true, " version " : 0} '
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
self . collector . add ( ' foo-metric ' , 1 )
self . assertEqual ( cm . exception . code , 0 )
# We display the notice informing the user of the changes.
self . print_version_change . assert_called_once_with ( 0 )
# We reset the countdown.
self . assert_writes_file (
self . config_file ,
{ ' is-googler ' : True , ' countdown ' : 9 , ' opt-in ' : None , ' version ' : 0 } )
# No metrics are uploaded.
self . assertFalse ( self . Popen . called )
@mock.patch ( ' metrics.metrics_utils.CURRENT_VERSION ' , 5 )
def test_version_change_from_opted_out ( self ) :
# The user has opted out and we're not collecting metrics.
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 0, " opt-in " : false, " version " : 0} '
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
self . collector . add ( ' foo-metric ' , 1 )
self . assertEqual ( cm . exception . code , 0 )
# We don't display any notice.
self . assertFalse ( self . print_version_change . called )
self . assertFalse ( self . print_notice . called )
# We don't upload any metrics.
self . assertFalse ( self . Popen . called )
# We don't modify the config.
self . assertFalse ( self . FileWrite . called )
@mock.patch ( ' metrics.metrics_utils.CURRENT_VERSION ' , 5 )
def test_version_change_non_googler ( self ) :
# The user is not a googler and we're not collecting metrics.
self . FileRead . side_effect = [
' { " is-googler " : false, " countdown " : 10, " opt-in " : null, " version " : 0} '
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
self . collector . add ( ' foo-metric ' , 1 )
self . assertEqual ( cm . exception . code , 0 )
# We don't display any notice.
self . assertFalse ( self . print_version_change . called )
self . assertFalse ( self . print_notice . called )
# We don't upload any metrics.
self . assertFalse ( self . Popen . called )
# We don't modify the config.
self . assertFalse ( self . FileWrite . called )
@mock.patch ( ' metrics.metrics_utils.CURRENT_VERSION ' , 5 )
def test_opting_in_updates_version ( self ) :
# The user is seeing the notice telling him of the version changes.
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 8, " opt-in " : null, " version " : 0} '
]
self . collector . config . opted_in = True
# We don't display any notice.
self . assertFalse ( self . print_version_change . called )
self . assertFalse ( self . print_notice . called )
# We don't upload any metrics.
self . assertFalse ( self . Popen . called )
# We update the version and opt-in the user.
self . assert_writes_file (
self . config_file ,
{ ' is-googler ' : True , ' countdown ' : 8 , ' opt-in ' : True , ' version ' : 5 } )
@mock.patch ( ' metrics.metrics_utils.CURRENT_VERSION ' , 5 )
def test_opting_in_by_default_updates_version ( self ) :
# The user will be opted in by default on the next execution.
self . FileRead . side_effect = [
' { " is-googler " : true, " countdown " : 1, " opt-in " : null, " version " : 0} '
]
with self . assertRaises ( SystemExit ) as cm :
with self . collector . print_notice_and_exit ( ) :
self . collector . add ( ' foo-metric ' , 1 )
self . assertEqual ( cm . exception . code , 0 )
# We display the notices.
self . print_notice . assert_called_once_with ( 1 )
self . print_version_change . assert_called_once_with ( 0 )
# We don't upload any metrics.
self . assertFalse ( self . Popen . called )
# We update the version and set the countdown to 0. In subsequent runs,
# we'll start collecting metrics.
self . assert_writes_file (
self . config_file ,
{ ' is-googler ' : True , ' countdown ' : 0 , ' opt-in ' : None , ' version ' : 5 } )
if __name__ == ' __main__ ' :
unittest . main ( )