summaryrefslogtreecommitdiff
path: root/indra/viewer_components
diff options
context:
space:
mode:
authorGlenn Glazer <coyot@lindenlab.com>2016-07-13 07:36:12 -0700
committerGlenn Glazer <coyot@lindenlab.com>2016-07-13 07:36:12 -0700
commit9c2633cba85f8dae95ff2b748a027dcfb7729848 (patch)
treecfc9a942813d898dfe61378b5614109d262c9817 /indra/viewer_components
parent7ce516f004835277860043a6c8219fd8b68df148 (diff)
SL-323: adding in unit tests
Diffstat (limited to 'indra/viewer_components')
-rwxr-xr-xindra/viewer_components/manager/SL_Launcher90
-rw-r--r--indra/viewer_components/manager/tests/data/settings.xml1184
-rw-r--r--indra/viewer_components/manager/tests/summary.json1
-rw-r--r--indra/viewer_components/manager/tests/test_InstallerError.py39
-rw-r--r--indra/viewer_components/manager/tests/test_check_for_completed_download.py53
-rw-r--r--indra/viewer_components/manager/tests/test_convert_version_file_style.py55
-rw-r--r--indra/viewer_components/manager/tests/test_get_filename.py60
-rw-r--r--indra/viewer_components/manager/tests/test_get_log_file_handle.py63
-rw-r--r--indra/viewer_components/manager/tests/test_get_parent_path.py79
-rw-r--r--indra/viewer_components/manager/tests/test_get_platform_key.py40
-rw-r--r--indra/viewer_components/manager/tests/test_get_settings.py82
-rw-r--r--indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py42
-rw-r--r--indra/viewer_components/manager/tests/test_make_download_dir.py42
-rw-r--r--indra/viewer_components/manager/tests/test_query_vvm.py67
-rw-r--r--indra/viewer_components/manager/tests/test_silent_write.py43
-rw-r--r--indra/viewer_components/manager/tests/test_summary.py39
-rw-r--r--indra/viewer_components/manager/tests/with_setup_args.py66
-rwxr-xr-xindra/viewer_components/manager/update_manager.py71
18 files changed, 2093 insertions, 23 deletions
diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher
index ecf88a1105..1d4c19fa86 100755
--- a/indra/viewer_components/manager/SL_Launcher
+++ b/indra/viewer_components/manager/SL_Launcher
@@ -18,8 +18,11 @@
# Copyright (c) 2013, Linden Research, Inc.
import argparse
+import collections
import InstallerUserMessage
+import llsd
import os
+import platform
import sys
import subprocess
import update_manager
@@ -31,7 +34,88 @@ def after_frame(my_message, timeout = 10000):
#this is done before basic_message so that we aren't blocked by mainloop()
frame.after(timout, lambda: frame._delete_window)
frame.basic_message(message = my_message)
+
+def get_cmd_line():
+ platform_name = platform.system()
+ #find the parent of the logs and user_settings directories
+ if (platform_name == 'mac'):
+ settings_file = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'Resources/app_settings/cmd_line.xml')
+ elif (platform_name == 'lnx'):
+ settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml')
+ #using list format of join is important here because the Windows pathsep in a string escapes the next char
+ elif (platform_name == 'win'):
+ settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml')
+ else:
+ settings_dir = None
+
+ try:
+ cmd_line = llsd.parse((open(settings_file)).read())
+ except:
+ cmd_line = None
+
+ return cmd_line
+
+def get_settings():
+ #return the settings file parsed into a dict
+ try:
+ settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml'))
+ settings = llsd.parse((open(settings_file)).read())
+ except llsd.LLSDParseError as lpe:
+ silent_write(log_file_handle, "Could not parse settings file %s" % lpe)
+ return None
+ return settings
+
+def capture_vmp_args(arg_list = None, cmd_line = None):
+ #expected input format: arg_list = ['--set', 'foo', 'bar', '-X', '-Y', 'qux']
+ #take a copy of the viewer parameters that are of interest to VMP.
+ #the regex for a parameter is --<param> {opt1} {opt2}
+ cli_overrides = {}
+ cmd_line = get_cmd_line()
+
+ vmp_params = {'--channel':'channel', '--settings':'settings', '--update-service':'update-service', '--set':'set'}
+ #the settings set with --set. All such settings have only one argument.
+ vmp_setters = ('UpdaterMaximumBandwidth', 'UpdaterServiceCheckPeriod', 'UpdaterServicePath', 'UpdaterServiceSetting', 'UpdaterServiceURL', 'UpdaterWillingToTest')
+
+ #Here turn the list into a queue, popping off the left as we go. Note that deque() makes a copy by value, not by reference
+ #Because of the complexity introduced by the uncertainty of how many options a parameter can take, this is far less complicated code than the more
+ #pythonic (x,y) = <some generator> since we will sometimes have (x), sometimes (x,y) and sometimes (x,y,z)
+ #also, because the pop is destructive, we prevent ourselves from iterating back over list elements that iterator methods would peek ahead at
+ vmp_queue = collections.deque(arg_list)
+ while (len(vmp_queue)):
+ param = vmp_queue.popleft()
+ #if it is not one of ours, pop through args until we get to the next parameter
+ if param in vmp_params.keys():
+ if param == '--set':
+ setting_name = vmp_queue.popleft()
+ setting_value = vmp_queue.popleft()
+ if setting_name in vmp_setters:
+ cli_overrides[vmp_params[param]] = (setting_name, setting_value)
+ else:
+ #find out how many args this parameter has
+ count = cmd_line[param]['count']
+ param_args = []
+ if count:
+ for argh in range(1,count):
+ param_args.append(vmp_queue.popleft())
+ #the parameter name is the key, the (possibly empty) list of args is the value
+ cli_overrides[vmp_params[param]] = param_args
+
+ #to prevent KeyErrors on missing keys, set the remainder to None
+ for key in vmp_params:
+ if key != '--set':
+ try:
+ cli_overrides[key]
+ except KeyError:
+ cli_overrides[key] = None
+ else:
+ for arg in vmp_setters:
+ try:
+ cli_overrides[key][arg]
+ except KeyError:
+ cli_overrides[key][arg] = None
+ return cli_overrides
+
cwd = os.path.dirname(os.path.realpath(__file__))
executable_name = ""
@@ -58,11 +142,15 @@ viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),execu
parser = argparse.ArgumentParser()
args = parser.parse_known_args(sys.argv)
+print args[1]
+sys.exit()
+#args[1] looks like ['./SL_Launcher', '--set', 'foo', 'bar', '-X', '-Y', 'qux'], dump the progname
args_list_to_pass = args[1][1:]
+vmp_args = capture_vmp_args(args_list_to_pass)
#make a copy by value, not by reference
command = list(args_list_to_pass)
-(success, state, condition) = update_manager.update_manager()
+(success, state, condition) = update_manager.update_manager(cli_overrides)
# From update_manager:
# (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing)
# (False, 'download', version): we failed to download the new version
diff --git a/indra/viewer_components/manager/tests/data/settings.xml b/indra/viewer_components/manager/tests/data/settings.xml
new file mode 100644
index 0000000000..07e420dcb3
--- /dev/null
+++ b/indra/viewer_components/manager/tests/data/settings.xml
@@ -0,0 +1,1184 @@
+<llsd>
+ <map>
+ <key>AllowMultipleViewers</key>
+ <map>
+ <key>Comment</key>
+ <string>Allow multiple viewers.</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>AllowTapTapHoldRun</key>
+ <map>
+ <key>Comment</key>
+ <string>Tapping a direction key twice and holding it down makes avatar run</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>AppearanceCameraMovement</key>
+ <map>
+ <key>Comment</key>
+ <string>When entering appearance editing mode, camera zooms in on currently selected portion of avatar</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>AudioLevelMedia</key>
+ <map>
+ <key>Comment</key>
+ <string>Audio level of Quicktime movies</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.699999988079071044921875</real>
+ </map>
+ <key>AudioLevelMic</key>
+ <map>
+ <key>Comment</key>
+ <string>Audio level of microphone input</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.1749999970197677612304688</real>
+ </map>
+ <key>AudioLevelMusic</key>
+ <map>
+ <key>Comment</key>
+ <string>Audio level of streaming music</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0</real>
+ </map>
+ <key>AudioLevelSFX</key>
+ <map>
+ <key>Comment</key>
+ <string>Audio level of in-world sound effects</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.699999988079071044921875</real>
+ </map>
+ <key>AudioLevelVoice</key>
+ <map>
+ <key>Comment</key>
+ <string>Audio level of voice chat</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1</real>
+ </map>
+ <key>AudioStreamingMedia</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable streaming</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>AvatarAxisDeadZone0</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 0 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.1000000014901161193847656</real>
+ </map>
+ <key>AvatarAxisDeadZone1</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 1 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.1000000014901161193847656</real>
+ </map>
+ <key>AvatarAxisDeadZone2</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 2 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.1000000014901161193847656</real>
+ </map>
+ <key>AvatarAxisDeadZone3</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 3 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1</real>
+ </map>
+ <key>AvatarAxisDeadZone4</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 4 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.01999999955296516418457031</real>
+ </map>
+ <key>AvatarAxisDeadZone5</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 5 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>AvatarAxisScale3</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 3 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0</real>
+ </map>
+ <key>AvatarAxisScale4</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 4 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>2</real>
+ </map>
+ <key>AvatarAxisScale5</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar axis 5 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>2</real>
+ </map>
+ <key>AvatarFeathering</key>
+ <map>
+ <key>Comment</key>
+ <string>Avatar feathering (less is softer)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>6</real>
+ </map>
+ <key>AvatarFileName</key>
+ <map>
+ <key>Comment</key>
+ <string>Alternative avatar file name</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>avatar_lad_tentacles.xml</string>
+ </map>
+ <key>BuildAxisDeadZone0</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 0 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>BuildAxisDeadZone1</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 1 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>BuildAxisDeadZone2</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 2 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>BuildAxisDeadZone3</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 3 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>BuildAxisDeadZone4</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 4 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>BuildAxisDeadZone5</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 5 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>BuildAxisScale0</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 0 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>6</real>
+ </map>
+ <key>BuildAxisScale1</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 1 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>6</real>
+ </map>
+ <key>BuildAxisScale2</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 2 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>6</real>
+ </map>
+ <key>BuildAxisScale3</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 3 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>6</real>
+ </map>
+ <key>BuildAxisScale4</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 4 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>6</real>
+ </map>
+ <key>BuildAxisScale5</key>
+ <map>
+ <key>Comment</key>
+ <string>Build axis 5 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>6</real>
+ </map>
+ <key>BuildFeathering</key>
+ <map>
+ <key>Comment</key>
+ <string>Build feathering (less is softer)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>12</real>
+ </map>
+ <key>BulkChangeEveryoneCopy</key>
+ <map>
+ <key>Comment</key>
+ <string>Bulk changed objects can be copied by everyone</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>BulkChangeNextOwnerCopy</key>
+ <map>
+ <key>Comment</key>
+ <string>Bulk changed objects can be copied by next owner</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>BulkChangeNextOwnerModify</key>
+ <map>
+ <key>Comment</key>
+ <string>Bulk changed objects can be modified by next owner</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>BulkChangeShareWithGroup</key>
+ <map>
+ <key>Comment</key>
+ <string>Bulk changed objects are shared with the currently active group</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>CacheValidateCounter</key>
+ <map>
+ <key>Comment</key>
+ <string>Used to distribute cache validation</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>122</integer>
+ </map>
+ <key>CameraPosOnLogout</key>
+ <map>
+ <key>Comment</key>
+ <string>Camera position when last logged out (global coordinates)</string>
+ <key>Type</key>
+ <string>Vector3D</string>
+ <key>Value</key>
+ <array>
+ <real>288290.4477181434631347656</real>
+ <real>275988.5277819633483886719</real>
+ <real>49.10921102762222290039062</real>
+ </array>
+ </map>
+ <key>ClickToWalk</key>
+ <map>
+ <key>Comment</key>
+ <string>Click in world to walk to location</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>ConversationSortOrder</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies sort key for conversations</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>CurrentGrid</key>
+ <map>
+ <key>Comment</key>
+ <string>Currently Selected Grid</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>util.agni.lindenlab.com</string>
+ </map>
+ <key>Cursor3D</key>
+ <map>
+ <key>Comment</key>
+ <string>Treat Joystick values as absolute positions (not deltas).</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>FirstLoginThisInstall</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies that you have not logged in with the viewer since you performed a clean install</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>FirstRunThisInstall</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies that you have not run the viewer since you performed a clean install</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>FlycamAxisDeadZone0</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 0 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>FlycamAxisDeadZone1</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 1 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>FlycamAxisDeadZone2</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 2 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>FlycamAxisDeadZone3</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 3 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>FlycamAxisDeadZone4</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 4 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>FlycamAxisDeadZone5</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 5 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.009999999776482582092285156</real>
+ </map>
+ <key>FlycamAxisDeadZone6</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 6 dead zone.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1</real>
+ </map>
+ <key>FlycamAxisScale0</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 0 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>42</real>
+ </map>
+ <key>FlycamAxisScale1</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 1 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>40</real>
+ </map>
+ <key>FlycamAxisScale2</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 2 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>40</real>
+ </map>
+ <key>FlycamAxisScale3</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 3 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0</real>
+ </map>
+ <key>FlycamAxisScale4</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 4 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>2</real>
+ </map>
+ <key>FlycamAxisScale5</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 5 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>3</real>
+ </map>
+ <key>FlycamAxisScale6</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam axis 6 scaler.</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0</real>
+ </map>
+ <key>FlycamFeathering</key>
+ <map>
+ <key>Comment</key>
+ <string>Flycam feathering (less is softer)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>5</real>
+ </map>
+ <key>FocusPosOnLogout</key>
+ <map>
+ <key>Comment</key>
+ <string>Camera focus point when last logged out (global coordinates)</string>
+ <key>Type</key>
+ <string>Vector3D</string>
+ <key>Value</key>
+ <array>
+ <real>288287.8830481640761718154</real>
+ <real>275991.5973855691263452172</real>
+ <real>47.96361158013021963597566</real>
+ </array>
+ </map>
+ <key>ForceShowGrid</key>
+ <map>
+ <key>Comment</key>
+ <string>Always show grid dropdown on login screen</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>HttpProxyType</key>
+ <map>
+ <key>Comment</key>
+ <string>Proxy type to use for HTTP operations</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>None</string>
+ </map>
+ <key>JoystickInitialized</key>
+ <map>
+ <key>Comment</key>
+ <string>Whether or not a joystick has been detected and initiailized.</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>UnknownDevice</string>
+ </map>
+ <key>LSLFindCaseInsensitivity</key>
+ <map>
+ <key>Comment</key>
+ <string>Use case insensitivity when searching in LSL editor</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>LastFeatureVersion</key>
+ <map>
+ <key>Comment</key>
+ <string>[DO NOT MODIFY] Feature Table Version number for tracking rendering system changes</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>37</integer>
+ </map>
+ <key>LastGPUString</key>
+ <map>
+ <key>Comment</key>
+ <string>[DO NOT MODIFY] previous GPU id string for tracking hardware changes</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>NVIDIA Corporation NVIDIA GeForce GT 750M OpenGL Engine</string>
+ </map>
+ <key>LastPrefTab</key>
+ <map>
+ <key>Comment</key>
+ <string>Last selected tab in preferences window</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>LastRunVersion</key>
+ <map>
+ <key>Comment</key>
+ <string>Version number of last instance of the viewer that you ran</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>Second Life Project Bento 5.0.0.315657</string>
+ </map>
+ <key>LocalCacheVersion</key>
+ <map>
+ <key>Comment</key>
+ <string>Version number of cache</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>7</integer>
+ </map>
+ <key>LoginLocation</key>
+ <map>
+ <key>Comment</key>
+ <string>Default Login location (&apos;last&apos;, &apos;home&apos;) preference</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>home</string>
+ </map>
+ <key>MapScale</key>
+ <map>
+ <key>Comment</key>
+ <string>World map zoom level (pixels per region)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>256</real>
+ </map>
+ <key>MaxJointsPerMeshObject</key>
+ <map>
+ <key>Comment</key>
+ <string>Maximum joints per rigged mesh object</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <real>51</real>
+ </map>
+ <key>MediaEnablePopups</key>
+ <map>
+ <key>Comment</key>
+ <string>If true, enable targeted links and javascript in media to open new media browser windows without a prompt.</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>MediaShowOnOthers</key>
+ <map>
+ <key>Comment</key>
+ <string>Whether or not to show media on other avatars</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>MigrateCacheDirectory</key>
+ <map>
+ <key>Comment</key>
+ <string>Check for old version of disk cache to migrate to current location</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>NavBarShowParcelProperties</key>
+ <map>
+ <key>Comment</key>
+ <string>Show parcel property icons in navigation bar</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>NextLoginLocation</key>
+ <map>
+ <key>Comment</key>
+ <string>Location to log into for this session - set from command line or the login panel, cleared following a successfull login.</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>home</string>
+ </map>
+ <key>NotificationConferenceIMOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>
+ Specifies how the UI responds to Conference IM Notifications.
+ Allowed values: [openconversations,toast,flash,noaction]
+ </string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>none</string>
+ </map>
+ <key>NotificationFriendIMOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>
+ Specifies how the UI responds to Friend IM Notifications.
+ Allowed values: [openconversations,toast,flash,noaction]
+ </string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>openconversations</string>
+ </map>
+ <key>NotificationGroupChatOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>
+ Specifies how the UI responds to Group Chat Notifications.
+ Allowed values: [openconversations,toast,flash,noaction]
+ </string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>none</string>
+ </map>
+ <key>NotificationNearbyChatOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>
+ Specifies how the UI responds to Nearby Chat Notifications.
+ Allowed values: [openconversations,toast,flash,noaction]
+ </string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>none</string>
+ </map>
+ <key>NotificationNonFriendIMOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>
+ Specifies how the UI responds to Non Friend IM Notifications.
+ Allowed values: [openconversations,toast,flash,noaction]
+ </string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>openconversations</string>
+ </map>
+ <key>NumSessions</key>
+ <map>
+ <key>Comment</key>
+ <string>Number of successful logins to Second Life</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1674</integer>
+ </map>
+ <key>PlayTypingAnim</key>
+ <map>
+ <key>Comment</key>
+ <string>Your avatar plays the typing animation whenever you type in the chat bar</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>PoolSizeExpCache</key>
+ <map>
+ <key>Comment</key>
+ <string>Coroutine Pool size for ExpCache</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>5</integer>
+ </map>
+ <key>PreferredBrowserBehavior</key>
+ <map>
+ <key>Comment</key>
+ <string>Use system browser for any links (0), use builtin browser for SL links and system one for others (1) or use builtin browser only (2).</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <string>0</string>
+ </map>
+ <key>PreferredMaturity</key>
+ <map>
+ <key>Comment</key>
+ <string>Setting for the user&apos;s preferred maturity level (consts in indra_constants.h)</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>42</integer>
+ </map>
+ <key>PresetGraphicActive</key>
+ <map>
+ <key>Comment</key>
+ <string>Name of currently selected preference</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>Default</string>
+ </map>
+ <key>ProbeHardwareOnStartup</key>
+ <map>
+ <key>Comment</key>
+ <string>Query current hardware configuration on application startup</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>QAMode</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable Testing Features.</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>RenderAnisotropic</key>
+ <map>
+ <key>Comment</key>
+ <string>Render textures using anisotropic filtering</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>RenderAvatarCloth</key>
+ <map>
+ <key>Comment</key>
+ <string>Controls if avatars use wavy cloth</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>RenderAvatarLODFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Controls level of detail of avatars (multiplier for current screen area when calculated level of detail)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1</real>
+ </map>
+ <key>RenderFSAASamples</key>
+ <map>
+ <key>Comment</key>
+ <string>Number of samples to use for FSAA (0 = no AA).</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>2</integer>
+ </map>
+ <key>RenderFarClip</key>
+ <map>
+ <key>Comment</key>
+ <string>Distance of far clip plane from camera (meters)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>128</real>
+ </map>
+ <key>RenderMaxPartCount</key>
+ <map>
+ <key>Comment</key>
+ <string>Maximum number of particles to display on screen</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <real>2048</real>
+ </map>
+ <key>RenderQualityPerformance</key>
+ <map>
+ <key>Comment</key>
+ <string>Which graphics settings you&apos;ve chosen</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <real>4</real>
+ </map>
+ <key>RenderReflectionDetail</key>
+ <map>
+ <key>Comment</key>
+ <string>Detail of reflection render pass.</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>RenderTerrainLODFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Controls level of detail of terrain (multiplier for current screen area when calculated level of detail)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>2</real>
+ </map>
+ <key>RenderVBOEnable</key>
+ <map>
+ <key>Comment</key>
+ <string>Use GL Vertex Buffer Objects</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>RenderVolumeLODFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Controls level of detail of primitives (multiplier for current screen area when calculated level of detail)</string>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.125</real>
+ </map>
+ <key>ShowAdvancedGraphicsSettings</key>
+ <map>
+ <key>Comment</key>
+ <string>Show advanced graphics settings</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>ShowBanLines</key>
+ <map>
+ <key>Comment</key>
+ <string>Show in-world ban/access borders</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>ShowStartLocation</key>
+ <map>
+ <key>Comment</key>
+ <string>Display starting location menu on login screen</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>SkeletonFileName</key>
+ <map>
+ <key>Comment</key>
+ <string>Alternative skeleton file name</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>avatar_skeleton_tentacles.xml</string>
+ </map>
+ <key>SkyPresetName</key>
+ <map>
+ <key>Comment</key>
+ <string>Sky preset to use. May be superseded by region settings or by a day cycle (see DayCycleName).</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>Sunset</string>
+ </map>
+ <key>SnapshotConfigURL</key>
+ <map>
+ <key>Comment</key>
+ <string>URL to fetch Snapshot Sharing configuration data from.</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>http://photos.apps.avatarsunited.com/viewer_config</string>
+ </map>
+ <key>SnapshotFormat</key>
+ <map>
+ <key>Comment</key>
+ <string>Save snapshots in this format (0 = PNG, 1 = JPEG, 2 = BMP)</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>SnapshotQuality</key>
+ <map>
+ <key>Comment</key>
+ <string>Quality setting of postcard JPEGs (0 = worst, 100 = best)</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>100</integer>
+ </map>
+ <key>SpellCheck</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable spellchecking on line and text editors</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>SpellCheckDictionary</key>
+ <map>
+ <key>Comment</key>
+ <string>Current primary and secondary dictionaries used for spell checking</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>English (United States)</string>
+ </map>
+ <key>TextureMemory</key>
+ <map>
+ <key>Comment</key>
+ <string>Amount of memory to use for textures in MB (0 = autodetect)</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>256</integer>
+ </map>
+ <key>UseDayCycle</key>
+ <map>
+ <key>Comment</key>
+ <string>Whether to use use a day cycle or a fixed sky.</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>UseDebugMenus</key>
+ <map>
+ <key>Comment</key>
+ <string>Turns on &quot;Debug&quot; menu</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>UseEnvironmentFromRegion</key>
+ <map>
+ <key>Comment</key>
+ <string>Choose whether to use the region&apos;s environment settings, or override them with the local settings.</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>0</boolean>
+ </map>
+ <key>VFSOldSize</key>
+ <map>
+ <key>Comment</key>
+ <string>[DO NOT MODIFY] Controls resizing of local file cache</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>102</integer>
+ </map>
+ <key>VFSSalt</key>
+ <map>
+ <key>Comment</key>
+ <string>[DO NOT MODIFY] Controls local file caching behavior</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>260093998</integer>
+ </map>
+ <key>VersionChannelName</key>
+ <map>
+ <key>Comment</key>
+ <string>Version information generated by running the viewer</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>Second Life Release</string>
+ </map>
+ <key>VertexShaderEnable</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable/disable all GLSL shaders (debug)</string>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <boolean>1</boolean>
+ </map>
+ <key>VoiceInputAudioDevice</key>
+ <map>
+ <key>Comment</key>
+ <string>Audio input device to use for voice</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>C-Media USB Audio Device</string>
+ </map>
+ <key>VoiceOutputAudioDevice</key>
+ <map>
+ <key>Comment</key>
+ <string>Audio output device to use for voice</string>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>C-Media USB Audio Device</string>
+ </map>
+ <key>WLSkyDetail</key>
+ <map>
+ <key>Comment</key>
+ <string>Controls vertex detail on the WindLight sky. Lower numbers will give better performance and uglier skies.</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>48</integer>
+ </map>
+ <key>WebProfileFloaterRect</key>
+ <map>
+ <key>Comment</key>
+ <string>Web profile floater dimensions</string>
+ <key>Type</key>
+ <string>Rect</string>
+ <key>Value</key>
+ <array>
+ <integer>1189</integer>
+ <integer>957</integer>
+ <integer>1674</integer>
+ <integer>277</integer>
+ </array>
+ </map>
+ <key>WindowHeight</key>
+ <map>
+ <key>Comment</key>
+ <string>SL viewer window height</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>1000</integer>
+ </map>
+ <key>WindowWidth</key>
+ <map>
+ <key>Comment</key>
+ <string>SL viewer window width</string>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>1626</integer>
+ </map>
+ <key>WindowX</key>
+ <map>
+ <key>Comment</key>
+ <string>X coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels)</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>50</integer>
+ </map>
+ <key>WindowY</key>
+ <map>
+ <key>Comment</key>
+ <string>Y coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels)</string>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>50</integer>
+ </map>
+ </map>
+</llsd>
diff --git a/indra/viewer_components/manager/tests/summary.json b/indra/viewer_components/manager/tests/summary.json
new file mode 100644
index 0000000000..b78859d427
--- /dev/null
+++ b/indra/viewer_components/manager/tests/summary.json
@@ -0,0 +1 @@
+{"Type":"viewer","Version":"4.0.5.315117","Channel":"Second Life Release"}
diff --git a/indra/viewer_components/manager/tests/test_InstallerError.py b/indra/viewer_components/manager/tests/test_InstallerError.py
new file mode 100644
index 0000000000..d722208b7f
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_InstallerError.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_InstallerError.py
+@author coyot
+@date 2016-06-01
+"""
+
+from nose.tools import assert_equal
+
+import InstallerError
+import os
+
+def test_InstallerError():
+ try:
+ #try to make our own homedir, this will fail on all three platforms
+ homedir = os.path.abspath(os.path.expanduser('~'))
+ os.mkdir(homedir)
+ except OSError, oe:
+ ie = InstallerError.InstallerError(oe, "Installer failed to create a homedir that already exists.")
+
+ assert_equal( str(ie),
+ "[Errno [Errno 17] File exists: '%s'] Installer failed to create a homedir that already exists." % homedir)
diff --git a/indra/viewer_components/manager/tests/test_check_for_completed_download.py b/indra/viewer_components/manager/tests/test_check_for_completed_download.py
new file mode 100644
index 0000000000..388bc900e9
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_check_for_completed_download.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_check_for_completed_download.py
+@author coyot
+@date 2016-06-03
+"""
+
+from nose.tools import *
+from nose import with_setup
+
+import os
+import shutil
+import tempfile
+import update_manager
+import with_setup_args
+
+def check_for_completed_download_setup():
+ tmpdir1 = tempfile.mkdtemp(prefix = 'test1')
+ tmpdir2 = tempfile.mkdtemp(prefix = 'test2')
+ tempfile.mkstemp(suffix = '.done', dir = tmpdir1)
+
+ return [tmpdir1,tmpdir2], {}
+
+def check_for_completed_download_teardown(tmpdir1,tmpdir2):
+ shutil.rmtree(tmpdir1, ignore_errors = True)
+ shutil.rmtree(tmpdir2, ignore_errors = True)
+
+@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown)
+def test_completed_check_for_completed_download(tmpdir1,tmpdir2):
+ assert_equal(update_manager.check_for_completed_download(tmpdir1), 'done'), "Failed to find completion marker"
+
+@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown)
+def test_incomplete_check_for_completed_download(tmpdir1,tmpdir2):
+ #should return False
+ incomplete = not update_manager.check_for_completed_download(tmpdir2)
+ assert incomplete, "False positive, should not mark complete without a marker" \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_convert_version_file_style.py b/indra/viewer_components/manager/tests/test_convert_version_file_style.py
new file mode 100644
index 0000000000..d700f91b84
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_convert_version_file_style.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_convert_version_file_style.py
+@author coyot
+@date 2016-06-01
+"""
+
+from nose.tools import assert_equal
+
+import update_manager
+
+def test_normal_form():
+ version = '1.2.3.456789'
+ golden = '1_2_3_456789'
+ converted = update_manager.convert_version_file_style(version)
+
+ assert_equal(golden, converted)
+
+def test_short_form():
+ version = '1.23'
+ golden = '1_23'
+ converted = update_manager.convert_version_file_style(version)
+
+ assert_equal(golden, converted)
+
+def test_idempotent():
+ version = '123'
+ golden = '123'
+ converted = update_manager.convert_version_file_style(version)
+
+ assert_equal(golden, converted)
+
+def test_none():
+ version = None
+ golden = None
+ converted = update_manager.convert_version_file_style(version)
+
+ assert_equal(golden, converted) \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_get_filename.py b/indra/viewer_components/manager/tests/test_get_filename.py
new file mode 100644
index 0000000000..95771d75dc
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_get_filename.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_get_filename.py
+@author coyot
+@date 2016-06-30
+"""
+
+from nose.tools import *
+from nose import with_setup
+
+import os
+import shutil
+import tempfile
+import apply_update
+import with_setup_args
+
+def get_filename_setup():
+ tmpdir1 = tempfile.mkdtemp(prefix = 'lnx')
+ tmpdir2 = tempfile.mkdtemp(prefix = 'mac')
+ tmpdir3 = tempfile.mkdtemp(prefix = 'win')
+ tmpdir4 = tempfile.mkdtemp(prefix = 'bad')
+ tempfile.mkstemp(suffix = '.bz2', dir = tmpdir1)
+ tempfile.mkstemp(suffix = '.dmg', dir = tmpdir2)
+ tempfile.mkstemp(suffix = '.exe', dir = tmpdir3)
+
+ return [tmpdir1,tmpdir2, tmpdir3, tmpdir4], {}
+
+def get_filename_teardown(tmpdir1,tmpdir2, tmpdir3, tmpdir4):
+ shutil.rmtree(tmpdir1, ignore_errors = True)
+ shutil.rmtree(tmpdir2, ignore_errors = True)
+ shutil.rmtree(tmpdir3, ignore_errors = True)
+ shutil.rmtree(tmpdir4, ignore_errors = True)
+
+@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown)
+def test_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4):
+ assert_is_not_none(apply_update.get_filename(tmpdir1)), "Failed to find installable"
+ assert_is_not_none(apply_update.get_filename(tmpdir2)), "Failed to find installable"
+ assert_is_not_none(apply_update.get_filename(tmpdir3)), "Failed to find installable"
+
+@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown)
+def test_missing_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4):
+ not_found = not apply_update.get_filename(tmpdir4)
+ assert not_found, "False positive, should not find an installable in an empty dir" \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_get_log_file_handle.py b/indra/viewer_components/manager/tests/test_get_log_file_handle.py
new file mode 100644
index 0000000000..c5b3c89550
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_get_log_file_handle.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_get_log_file_handle.py
+@author coyot
+@date 2016-06-08
+"""
+
+from nose.tools import *
+
+import os
+import shutil
+import tempfile
+import update_manager
+import with_setup_args
+
+def get_log_file_handle_setup():
+ tmpdir1 = tempfile.mkdtemp(prefix = 'test1')
+ tmpdir2 = tempfile.mkdtemp(prefix = 'test2')
+ log_file_path = os.path.abspath(os.path.join(tmpdir1,"update_manager.log"))
+ #not using tempfile because we want a particular filename
+ open(log_file_path, 'w+').close
+
+ return [tmpdir1,tmpdir2,log_file_path], {}
+
+def get_log_file_handle_teardown(tmpdir1,tmpdir2,log_file_path):
+ shutil.rmtree(tmpdir1, ignore_errors = True)
+ shutil.rmtree(tmpdir2, ignore_errors = True)
+
+@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown)
+def test_existing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path):
+ handle = update_manager.get_log_file_handle(tmpdir1)
+ if not handle:
+ print "Failed to find existing log file"
+ assert False
+ elif not os.path.exists(os.path.abspath(log_file_path+".old")):
+ print "Failed to rotate update manager log"
+ assert False
+ assert True
+
+@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown)
+def test_missing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path):
+ handle = update_manager.get_log_file_handle(tmpdir2)
+ if not os.path.exists(log_file_path):
+ print "Failed to touch new log file"
+ assert False
+ assert True \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_get_parent_path.py b/indra/viewer_components/manager/tests/test_get_parent_path.py
new file mode 100644
index 0000000000..3cfd72310e
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_get_parent_path.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_get_parent_path.py
+@author coyot
+@date 2016-06-02
+"""
+
+from nose.tools import *
+from nose import with_setup
+
+import os
+import shutil
+import update_manager
+import with_setup_args
+
+def get_parent_path_setup():
+ key = update_manager.get_platform_key()
+ try:
+ if key == 'mac':
+ settings_dir = os.path.join(os.path.expanduser('~'),'Library','Application Support','SecondLife')
+ elif key == 'lnx':
+ settings_dir = os.path.join(os.path.expanduser('~'),'.secondlife')
+ elif key == 'win':
+ settings_dir = os.path.join(os.path.expanduser('~'),'AppData','Roaming','SecondLife')
+ else:
+ raise Exception("Invalid Platform Key")
+
+ #preserve existing settings dir if any
+ if os.path.exists(settings_dir):
+ old_dir = settings_dir + ".tmp"
+ if os.path.exists(old_dir):
+ shutil.rmtree(old_dir, ignore_errors = True)
+ os.rename(settings_dir, old_dir)
+ os.makedirs(settings_dir)
+ except Exception, e:
+ print "get_parent_path_setup failed due to: %s" % str(e)
+ assert False
+
+ #this is we don't have to rediscover settings_dir for test and teardown
+ return [settings_dir], {}
+
+def get_parent_path_teardown(settings_dir):
+ try:
+ shutil.rmtree(settings_dir, ignore_errors = True)
+ #restore previous settings dir if any
+ old_dir = settings_dir + ".tmp"
+ if os.path.exists(old_dir):
+ os.rename(old_dir, settings_dir)
+ except:
+ #cleanup is best effort
+ pass
+
+@with_setup_args.with_setup_args(get_parent_path_setup, get_parent_path_teardown)
+def test_get_parent_path(settings_dir):
+ key = update_manager.get_platform_key()
+ got_settings_dir = update_manager.get_parent_path(key)
+
+ assert settings_dir, "test_get_parent_path failed to obtain parent path"
+
+ assert_equal(settings_dir, got_settings_dir)
+
+ \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_get_platform_key.py b/indra/viewer_components/manager/tests/test_get_platform_key.py
new file mode 100644
index 0000000000..37c570532c
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_get_platform_key.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_get_platform_key.py
+@author coyot
+@date 2016-06-01
+"""
+
+from nose.tools import assert_equal
+
+import platform
+import update_manager
+
+def test_get_platform_key():
+ key = update_manager.get_platform_key()
+ if key == 'mac':
+ assert_equal(platform.system(),'Darwin')
+ elif key == 'lnx':
+ assert_equal(platform.system(),'Linux')
+ elif key == 'win':
+ assert_equal(platform.system(),'Windows')
+ else:
+ assert_equal(key, None)
+ \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_get_settings.py b/indra/viewer_components/manager/tests/test_get_settings.py
new file mode 100644
index 0000000000..46b779cbd5
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_get_settings.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_get_settings.py
+@author coyot
+@date 2016-06-03
+"""
+
+from nose.tools import *
+from nose import with_setup
+
+import os
+import shutil
+import update_manager
+import with_setup_args
+
+def get_settings_setup():
+ try:
+ key = update_manager.get_platform_key()
+ settings_dir = os.path.join(update_manager.get_parent_path(key), "user_settings")
+ print settings_dir
+
+ #preserve existing settings dir if any
+ if os.path.exists(settings_dir):
+ old_dir = settings_dir + ".tmp"
+ if os.path.exists(old_dir):
+ shutil.rmtree(old_dir, ignore_errors = True)
+ os.rename(settings_dir, old_dir)
+ os.makedirs(settings_dir)
+
+ #the data subdir of the tests dir that this script is in
+ data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")
+ #the test settings file
+ settings_file = os.path.join(data_dir, "settings.xml")
+ shutil.copyfile(settings_file, os.path.join(settings_dir, "settings.xml"))
+
+ except Exception, e:
+ print "get_settings_setup failed due to: %s" % str(e)
+ assert False
+
+ #this is we don't have to rediscover settings_dir for test and teardown
+ return [settings_dir], {}
+
+def get_settings_teardown(settings_dir):
+ try:
+ shutil.rmtree(settings_dir, ignore_errors = True)
+ #restore previous settings dir if any
+ old_dir = settings_dir + ".tmp"
+ if os.path.exists(old_dir):
+ os.rename(old_dir, settings_dir)
+ except:
+ #cleanup is best effort
+ pass
+
+@with_setup_args.with_setup_args(get_settings_setup, get_settings_teardown)
+def test_get_settings(settings_dir):
+ key = update_manager.get_platform_key()
+ parent = update_manager.get_parent_path(key)
+ log_file = update_manager.get_log_file_handle(parent)
+ got_settings = update_manager.get_settings(log_file, parent)
+
+ assert got_settings, "test_get_settings failed to find a settings.xml file"
+
+ #test one key just to make sure it parsed
+ assert_equal(got_settings['CurrentGrid']['Value'], 'util.agni.lindenlab.com')
+
diff --git a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py
new file mode 100644
index 0000000000..513502a6ca
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_make_VVM_UUID_hash.py
+@author coyot
+@date 2016-06-03
+"""
+
+from nose.tools import *
+
+import update_manager
+
+def test_make_VVM_UUID_hash():
+ #because the method returns different results on different hosts
+ #it is not easy to unit test it reliably.
+ #About the best we can do is check for the exception from subprocess
+ key = update_manager.get_platform_key()
+ try:
+ UUID_hash = update_manager.make_VVM_UUID_hash(key)
+ except Exception, e:
+ print "Test failed due to: %s" % str(e)
+ assert False
+
+ #make_UUID_hash returned None
+ assert UUID_hash, "make_UUID_hash failed to make a hash."
+
diff --git a/indra/viewer_components/manager/tests/test_make_download_dir.py b/indra/viewer_components/manager/tests/test_make_download_dir.py
new file mode 100644
index 0000000000..5198a6de05
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_make_download_dir.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_make_download_dir.py
+@author coyot
+@date 2016-06-03
+"""
+
+from nose.tools import *
+
+import update_manager
+
+def test_make_download_dir():
+ key = update_manager.get_platform_key()
+ path = update_manager.get_parent_path(key)
+ version = '1.2.3.456789'
+ try:
+ download_dir = update_manager.make_download_dir(path, version)
+ except OSError, e:
+ print "make_download_dir failed to eat OSError %s" % str(e)
+ assert False
+ except Exception, e:
+ print "make_download_dir raised an unexpected exception %s" % str(e)
+ assert False
+
+ assert download_dir, "make_download_dir returned None for path %s and version %s" % (path, version) \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_query_vvm.py b/indra/viewer_components/manager/tests/test_query_vvm.py
new file mode 100644
index 0000000000..40a0f7b215
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_query_vvm.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_query_vvm.py
+@author coyot
+@date 2016-06-08
+"""
+
+from nose.tools import *
+
+import os
+import re
+import shutil
+import tempfile
+import update_manager
+import with_setup_args
+
+def query_vvm_setup():
+ tmpdir1 = tempfile.mkdtemp(prefix = 'test1')
+ handle = update_manager.get_log_file_handle(tmpdir1)
+
+ return [tmpdir1,handle], {}
+
+def query_vvm_teardown(tmpdir1, handle):
+ shutil.rmtree(tmpdir1, ignore_errors = True)
+
+@with_setup_args.with_setup_args(query_vvm_setup, query_vvm_teardown)
+def test_query_vvm(tmpdir1, handle):
+ key = update_manager.get_platform_key()
+ parent = update_manager.get_parent_path(key)
+ settings = update_manager.get_settings(handle, parent)
+ launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))
+ summary = update_manager.get_summary(key, launcher_path)
+
+ #for unit testing purposes, just testing a value from results. If no update, then None and it falls through
+ #for formal QA see:
+ # https://docs.google.com/document/d/1WNjOPdKlq0j_7s7gdNe_3QlyGnQDa3bFNvtyVM6Hx8M/edit
+ # https://wiki.lindenlab.com/wiki/Login_Test#Test_Viewer_Updater
+ #for test plans on all cases, as it requires setting up a fake VVM service
+
+ try:
+ results = update_manager.query_vvm(handle, key, settings, summary)
+ except Exception, e:
+ print "query_vvm threw unexpected exception %s" % str(e)
+ assert False
+
+ if results:
+ pattern = re.compile('Second Life')
+ assert pattern.search(results['channel']), "Bad results returned %s" % str(results)
+
+ assert True
diff --git a/indra/viewer_components/manager/tests/test_silent_write.py b/indra/viewer_components/manager/tests/test_silent_write.py
new file mode 100644
index 0000000000..a55665799f
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_silent_write.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_silent_write.py
+@author coyot
+@date 2016-06-02
+"""
+
+from nose.tools import *
+
+import tempfile
+import update_manager
+
+def test_silent_write_to_file():
+ test_log = tempfile.TemporaryFile()
+ try:
+ update_manager.silent_write(test_log, "This is a test.")
+ except Exception, e:
+ print "Test failed due to: %s" % str(e)
+ assert False
+
+def test_silent_write_to_null():
+ try:
+ update_manager.silent_write(None, "This is a test.")
+ except Exception, e:
+ print "Test failed due to: %s" % str(e)
+ assert False \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/test_summary.py b/indra/viewer_components/manager/tests/test_summary.py
new file mode 100644
index 0000000000..b318012b54
--- /dev/null
+++ b/indra/viewer_components/manager/tests/test_summary.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+
+"""
+@file test_summary.py
+@author coyot
+@date 2016-06-02
+"""
+
+from nose.tools import *
+
+import os.path
+import tempfile
+import update_manager
+
+def test_get_summary():
+ key = update_manager.get_platform_key()
+ #launcher is one dir above tests
+ launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))
+ summary_json = update_manager.get_summary(key, launcher_path)
+
+ #we aren't testing the JSON library, one key pair is enough
+ #so we will use the one pair that is actually a constant
+ assert_equal(summary_json['Type'],'viewer') \ No newline at end of file
diff --git a/indra/viewer_components/manager/tests/with_setup_args.py b/indra/viewer_components/manager/tests/with_setup_args.py
new file mode 100644
index 0000000000..38350ab8c4
--- /dev/null
+++ b/indra/viewer_components/manager/tests/with_setup_args.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+# $LicenseInfo:firstyear=2016&license=internal$
+#
+# Copyright (c) 2016, Linden Research, Inc.
+#
+# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+# this source code is governed by the Linden Lab Source Code Disclosure
+# Agreement ("Agreement") previously entered between you and Linden
+# Lab. By accessing, using, copying, modifying or distributing this
+# software, you acknowledge that you have been informed of your
+# obligations under the Agreement and agree to abide by those obligations.
+#
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+#
+# Taken from: https://gist.github.com/garyvdm/392ae20c673c7ee58d76
+
+"""
+@file with_setup_args.py
+@author garyvdm
+@date 2016-06-02
+"""
+
+
+def with_setup_args(setup, teardown=None):
+ """Decorator to add setup and/or teardown methods to a test function::
+ @with_setup_args(setup, teardown)
+ def test_something():
+ " ... "
+ The setup function should return (args, kwargs) which will be passed to
+ test function, and teardown function.
+ Note that `with_setup_args` is useful *only* for test functions, not for test
+ methods or inside of TestCase subclasses.
+ """
+ def decorate(func):
+ args = []
+ kwargs = {}
+
+ def test_wrapped():
+ func(*args, **kwargs)
+
+ test_wrapped.__name__ = func.__name__
+
+ def setup_wrapped():
+ a, k = setup()
+ args.extend(a)
+ kwargs.update(k)
+ if hasattr(func, 'setup'):
+ func.setup()
+ test_wrapped.setup = setup_wrapped
+
+ if teardown:
+ def teardown_wrapped():
+ if hasattr(func, 'teardown'):
+ func.teardown()
+ teardown(*args, **kwargs)
+
+ test_wrapped.teardown = teardown_wrapped
+ else:
+ if hasattr(func, 'teardown'):
+ test_wrapped.teardown = func.teardown()
+ return test_wrapped
+ return decorate \ No newline at end of file
diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py
index ec6df17a6c..a7e0a19aef 100755
--- a/indra/viewer_components/manager/update_manager.py
+++ b/indra/viewer_components/manager/update_manager.py
@@ -204,11 +204,14 @@ def make_VVM_UUID_hash(platform_key):
#fake it
return hashlib.md5(str(uuid.uuid1())).hexdigest()
-def query_vvm(log_file_handle, platform_key, settings, summary_dict):
+def query_vvm(log_file_handle = None, platform_key = None, settings = None, summary_dict = None, UpdaterServiceURL = None, UpdaterWillingToTest = None):
result_data = None
#URI template /update/v1.1/channelname/version/platformkey/platformversion/willing-to-test/uniqueid
#https://wiki.lindenlab.com/wiki/Viewer_Version_Manager_REST_API#Viewer_Update_Query
- base_URI = 'https://update.secondlife.com/update/'
+ if UpdaterServiceURL:
+ baseURI = UpdaterServiceURL
+ else:
+ base_URI = 'https://update.secondlife.com/update/'
channelname = summary_dict['Channel']
#this is kind of a mess because the settings value a) in a map and b) is both the cohort and the version
version = summary_dict['Version']
@@ -229,11 +232,17 @@ def query_vvm(log_file_handle, platform_key, settings, summary_dict):
</map>
</map>
"""
- try:
- test_ok = settings['test']['Value']
- except KeyError as ke:
- #normal case, no testing key
- test_ok = 'testok'
+ if UpdaterWillingToTest is not None:
+ if UpdaterWillingToTest:
+ test_ok = 'testok'
+ else:
+ test_ok = 'testno'
+ else:
+ try:
+ test_ok = settings['test']['Value']
+ except KeyError:
+ #normal case, no testing key
+ test_ok = 'testok'
UUID = make_VVM_UUID_hash(platform_key)
#because urljoin can't be arsed to take multiple elements
query_string = '/v1.0/' + channelname + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID
@@ -245,7 +254,7 @@ def query_vvm(log_file_handle, platform_key, settings, summary_dict):
return None
return result_data
-def download(url = None, version = None, download_dir = None, size = 0, background = False):
+def download(url = None, version = None, download_dir = None, size = 0, background = False, chunk_size = 1024):
download_tries = 0
download_success = False
#for background execution
@@ -259,7 +268,7 @@ def download(url = None, version = None, download_dir = None, size = 0, backgrou
after_frame(message = "Trying again to download new version " + version + " Please wait.")
if not background:
try:
- download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True)
+ download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True, chunk_size = chunk_size)
download_success = True
except:
download_tries += 1
@@ -268,7 +277,7 @@ def download(url = None, version = None, download_dir = None, size = 0, backgrou
try:
#Python does not have a facility to multithread a method, so we make the method a standalone
#and subprocess that
- subprocess.call(path_to_downloader, "--url = %s --dir = %s --pb --size= %s" % (url, download_dir, size))
+ subprocess.call(path_to_downloader, "--url = %s --dir = %s --pb --size = %s --chunk_size = %s" % (url, download_dir, size, chunk_size))
download_success = True
except:
download_tries += 1
@@ -294,11 +303,12 @@ def install(platform_key = None, download_dir = None, log_file_handle = None, in
silent_write(log_file_handle, "Failed to update viewer to " + version)
return False
-def download_and_install(downloaded = None, url = None, version = None, download_dir = None, size = None, platform_key = None, log_file_handle = None, in_place = None):
+def download_and_install(downloaded = None, url = None, version = None, download_dir = None, size = None,
+ platform_key = None, log_file_handle = None, in_place = None, chunk_size = 1024):
#extracted to a method because we do it twice in update_manager() and this makes the logic clearer
if not downloaded:
#do the download, exit if we fail
- if not download(url = url, version = version, download_dir = download_dir, size = size):
+ if not download(url = url, version = version, download_dir = download_dir, size = size, chunk_size = chunk_size):
return (False, 'download', version)
#do the install
path_to_new_launcher = install(platform_key = platform_key, download_dir = download_dir,
@@ -313,7 +323,8 @@ def download_and_install(downloaded = None, url = None, version = None, download
#propagate failure
return (False, 'apply', version)
-def update_manager():
+def update_manager(cli_overrides = None):
+ #cli_overrides is a dict where the keys are specific parameters of interest and the values are the arguments to
#comments that begin with '323:' are steps taken from the algorithm in the description of SL-323.
# Note that in the interest of efficiency, such as determining download success once at the top
# The code does follow precisely the same order as the algorithm.
@@ -356,7 +367,11 @@ def update_manager():
print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None)
return (False, 'setup', None)
- settings = get_settings(log_file_handle, parent_dir)
+ if cli_overrides['settings'] is not None:
+ settings = get_settings(log_file_handle, cli_overrides['settings'])
+ else:
+ settings = get_settings(log_file_handle, parent_dir)
+
if settings is None:
silent_write(log_file_handle, "Failed to load viewer settings")
print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None)
@@ -375,22 +390,34 @@ def update_manager():
<string>0</string>
</map>
"""
- try:
- install_automatically = settings['UpdaterServiceSetting']['Value']
- #because, for some godforsaken reason, we delete the setting rather than changing the value
- except KeyError:
- install_automatically = 1
+ if cli_overrides['set']['UpdaterServiceSetting'] is not None:
+ install_automatically = cli_overrides['set']['UpdaterServiceSetting']
+ else:
+ try:
+ install_automatically = settings['UpdaterServiceSetting']['Value']
+ #because, for some godforsaken reason, we delete the setting rather than changing the value
+ except KeyError:
+ install_automatically = 1
+
+ #use default chunk size if none is given
+ if cli_overrides['set']['UpdaterMaximumBandwidth ']:
+ chunk_size = cli_overrides['set']['UpdaterMaximumBandwidth ']
+ else:
+ chunk_size = 1024
#get channel and version
try:
summary_dict = get_summary(platform_key, os.path.abspath(os.path.realpath(__file__)))
+ if cli_overrides['channel']:
+ summary_dict['Channel'] = cli_overrides['channel']
except:
silent_write(log_file_handle, "Could not obtain channel and version, exiting.")
print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None)
return (False, 'setup', None)
#323: On launch, the Viewer Manager should query the Viewer Version Manager update api.
- result_data = query_vvm(log_file_handle, platform_key, settings, summary_dict)
+ UpdaterServiceURL = cli_overrides['update-service']
+ result_data = query_vvm(log_file_handle, platform_key, settings, summary_dict, UpdaterServiceURL)
#nothing to do or error
if not result_data:
silent_write.write(og_file_handle, "No update found.")
@@ -417,7 +444,7 @@ def update_manager():
#323: Check for a completed download of the required update; if found, display an alert, install the required update, and launch the newly installed viewer.
#323: If [optional download and] Install Automatically: display an alert, install the update and launch updated viewer.
return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir,
- size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place)
+ size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size)
else:
#323: If the update response indicates that there is an optional update:
#323: Check to see if the optional update has already been downloaded.
@@ -436,7 +463,7 @@ def update_manager():
choice = frame.choice.get()
if choice == 1:
return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir,
- size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place)
+ size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size)
elif choice == 2:
tempfile.mkstmp(suffix = ".next", dir = download_dir)
return (True, None, None)