From 9c2633cba85f8dae95ff2b748a027dcfb7729848 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 13 Jul 2016 07:36:12 -0700 Subject: SL-323: adding in unit tests --- indra/viewer_components/manager/SL_Launcher | 90 +- .../manager/tests/data/settings.xml | 1184 ++++++++++++++++++++ indra/viewer_components/manager/tests/summary.json | 1 + .../manager/tests/test_InstallerError.py | 39 + .../tests/test_check_for_completed_download.py | 53 + .../tests/test_convert_version_file_style.py | 55 + .../manager/tests/test_get_filename.py | 60 + .../manager/tests/test_get_log_file_handle.py | 63 ++ .../manager/tests/test_get_parent_path.py | 79 ++ .../manager/tests/test_get_platform_key.py | 40 + .../manager/tests/test_get_settings.py | 82 ++ .../manager/tests/test_make_VVM_UUID_hash.py | 42 + .../manager/tests/test_make_download_dir.py | 42 + .../manager/tests/test_query_vvm.py | 67 ++ .../manager/tests/test_silent_write.py | 43 + .../manager/tests/test_summary.py | 39 + .../manager/tests/with_setup_args.py | 66 ++ indra/viewer_components/manager/update_manager.py | 71 +- 18 files changed, 2093 insertions(+), 23 deletions(-) create mode 100644 indra/viewer_components/manager/tests/data/settings.xml create mode 100644 indra/viewer_components/manager/tests/summary.json create mode 100644 indra/viewer_components/manager/tests/test_InstallerError.py create mode 100644 indra/viewer_components/manager/tests/test_check_for_completed_download.py create mode 100644 indra/viewer_components/manager/tests/test_convert_version_file_style.py create mode 100644 indra/viewer_components/manager/tests/test_get_filename.py create mode 100644 indra/viewer_components/manager/tests/test_get_log_file_handle.py create mode 100644 indra/viewer_components/manager/tests/test_get_parent_path.py create mode 100644 indra/viewer_components/manager/tests/test_get_platform_key.py create mode 100644 indra/viewer_components/manager/tests/test_get_settings.py create mode 100644 indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py create mode 100644 indra/viewer_components/manager/tests/test_make_download_dir.py create mode 100644 indra/viewer_components/manager/tests/test_query_vvm.py create mode 100644 indra/viewer_components/manager/tests/test_silent_write.py create mode 100644 indra/viewer_components/manager/tests/test_summary.py create mode 100644 indra/viewer_components/manager/tests/with_setup_args.py (limited to 'indra') 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 -- {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) = 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 @@ + + + AllowMultipleViewers + + Comment + Allow multiple viewers. + Type + Boolean + Value + 1 + + AllowTapTapHoldRun + + Comment + Tapping a direction key twice and holding it down makes avatar run + Type + Boolean + Value + 0 + + AppearanceCameraMovement + + Comment + When entering appearance editing mode, camera zooms in on currently selected portion of avatar + Type + Boolean + Value + 0 + + AudioLevelMedia + + Comment + Audio level of Quicktime movies + Type + F32 + Value + 0.699999988079071044921875 + + AudioLevelMic + + Comment + Audio level of microphone input + Type + F32 + Value + 0.1749999970197677612304688 + + AudioLevelMusic + + Comment + Audio level of streaming music + Type + F32 + Value + 0 + + AudioLevelSFX + + Comment + Audio level of in-world sound effects + Type + F32 + Value + 0.699999988079071044921875 + + AudioLevelVoice + + Comment + Audio level of voice chat + Type + F32 + Value + 1 + + AudioStreamingMedia + + Comment + Enable streaming + Type + Boolean + Value + 0 + + AvatarAxisDeadZone0 + + Comment + Avatar axis 0 dead zone. + Type + F32 + Value + 0.1000000014901161193847656 + + AvatarAxisDeadZone1 + + Comment + Avatar axis 1 dead zone. + Type + F32 + Value + 0.1000000014901161193847656 + + AvatarAxisDeadZone2 + + Comment + Avatar axis 2 dead zone. + Type + F32 + Value + 0.1000000014901161193847656 + + AvatarAxisDeadZone3 + + Comment + Avatar axis 3 dead zone. + Type + F32 + Value + 1 + + AvatarAxisDeadZone4 + + Comment + Avatar axis 4 dead zone. + Type + F32 + Value + 0.01999999955296516418457031 + + AvatarAxisDeadZone5 + + Comment + Avatar axis 5 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + AvatarAxisScale3 + + Comment + Avatar axis 3 scaler. + Type + F32 + Value + 0 + + AvatarAxisScale4 + + Comment + Avatar axis 4 scaler. + Type + F32 + Value + 2 + + AvatarAxisScale5 + + Comment + Avatar axis 5 scaler. + Type + F32 + Value + 2 + + AvatarFeathering + + Comment + Avatar feathering (less is softer) + Type + F32 + Value + 6 + + AvatarFileName + + Comment + Alternative avatar file name + Type + String + Value + avatar_lad_tentacles.xml + + BuildAxisDeadZone0 + + Comment + Build axis 0 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone1 + + Comment + Build axis 1 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone2 + + Comment + Build axis 2 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone3 + + Comment + Build axis 3 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone4 + + Comment + Build axis 4 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone5 + + Comment + Build axis 5 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisScale0 + + Comment + Build axis 0 scaler. + Type + F32 + Value + 6 + + BuildAxisScale1 + + Comment + Build axis 1 scaler. + Type + F32 + Value + 6 + + BuildAxisScale2 + + Comment + Build axis 2 scaler. + Type + F32 + Value + 6 + + BuildAxisScale3 + + Comment + Build axis 3 scaler. + Type + F32 + Value + 6 + + BuildAxisScale4 + + Comment + Build axis 4 scaler. + Type + F32 + Value + 6 + + BuildAxisScale5 + + Comment + Build axis 5 scaler. + Type + F32 + Value + 6 + + BuildFeathering + + Comment + Build feathering (less is softer) + Type + F32 + Value + 12 + + BulkChangeEveryoneCopy + + Comment + Bulk changed objects can be copied by everyone + Type + Boolean + Value + 1 + + BulkChangeNextOwnerCopy + + Comment + Bulk changed objects can be copied by next owner + Type + Boolean + Value + 1 + + BulkChangeNextOwnerModify + + Comment + Bulk changed objects can be modified by next owner + Type + Boolean + Value + 1 + + BulkChangeShareWithGroup + + Comment + Bulk changed objects are shared with the currently active group + Type + Boolean + Value + 1 + + CacheValidateCounter + + Comment + Used to distribute cache validation + Type + U32 + Value + 122 + + CameraPosOnLogout + + Comment + Camera position when last logged out (global coordinates) + Type + Vector3D + Value + + 288290.4477181434631347656 + 275988.5277819633483886719 + 49.10921102762222290039062 + + + ClickToWalk + + Comment + Click in world to walk to location + Type + Boolean + Value + 0 + + ConversationSortOrder + + Comment + Specifies sort key for conversations + Type + U32 + Value + 0 + + CurrentGrid + + Comment + Currently Selected Grid + Type + String + Value + util.agni.lindenlab.com + + Cursor3D + + Comment + Treat Joystick values as absolute positions (not deltas). + Type + Boolean + Value + 0 + + FirstLoginThisInstall + + Comment + Specifies that you have not logged in with the viewer since you performed a clean install + Type + Boolean + Value + 0 + + FirstRunThisInstall + + Comment + Specifies that you have not run the viewer since you performed a clean install + Type + Boolean + Value + 0 + + FlycamAxisDeadZone0 + + Comment + Flycam axis 0 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone1 + + Comment + Flycam axis 1 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone2 + + Comment + Flycam axis 2 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone3 + + Comment + Flycam axis 3 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone4 + + Comment + Flycam axis 4 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone5 + + Comment + Flycam axis 5 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone6 + + Comment + Flycam axis 6 dead zone. + Type + F32 + Value + 1 + + FlycamAxisScale0 + + Comment + Flycam axis 0 scaler. + Type + F32 + Value + 42 + + FlycamAxisScale1 + + Comment + Flycam axis 1 scaler. + Type + F32 + Value + 40 + + FlycamAxisScale2 + + Comment + Flycam axis 2 scaler. + Type + F32 + Value + 40 + + FlycamAxisScale3 + + Comment + Flycam axis 3 scaler. + Type + F32 + Value + 0 + + FlycamAxisScale4 + + Comment + Flycam axis 4 scaler. + Type + F32 + Value + 2 + + FlycamAxisScale5 + + Comment + Flycam axis 5 scaler. + Type + F32 + Value + 3 + + FlycamAxisScale6 + + Comment + Flycam axis 6 scaler. + Type + F32 + Value + 0 + + FlycamFeathering + + Comment + Flycam feathering (less is softer) + Type + F32 + Value + 5 + + FocusPosOnLogout + + Comment + Camera focus point when last logged out (global coordinates) + Type + Vector3D + Value + + 288287.8830481640761718154 + 275991.5973855691263452172 + 47.96361158013021963597566 + + + ForceShowGrid + + Comment + Always show grid dropdown on login screen + Type + Boolean + Value + 1 + + HttpProxyType + + Comment + Proxy type to use for HTTP operations + Type + String + Value + None + + JoystickInitialized + + Comment + Whether or not a joystick has been detected and initiailized. + Type + String + Value + UnknownDevice + + LSLFindCaseInsensitivity + + Comment + Use case insensitivity when searching in LSL editor + Type + Boolean + Value + 1 + + LastFeatureVersion + + Comment + [DO NOT MODIFY] Feature Table Version number for tracking rendering system changes + Type + S32 + Value + 37 + + LastGPUString + + Comment + [DO NOT MODIFY] previous GPU id string for tracking hardware changes + Type + String + Value + NVIDIA Corporation NVIDIA GeForce GT 750M OpenGL Engine + + LastPrefTab + + Comment + Last selected tab in preferences window + Type + S32 + Value + 1 + + LastRunVersion + + Comment + Version number of last instance of the viewer that you ran + Type + String + Value + Second Life Project Bento 5.0.0.315657 + + LocalCacheVersion + + Comment + Version number of cache + Type + S32 + Value + 7 + + LoginLocation + + Comment + Default Login location ('last', 'home') preference + Type + String + Value + home + + MapScale + + Comment + World map zoom level (pixels per region) + Type + F32 + Value + 256 + + MaxJointsPerMeshObject + + Comment + Maximum joints per rigged mesh object + Type + U32 + Value + 51 + + MediaEnablePopups + + Comment + If true, enable targeted links and javascript in media to open new media browser windows without a prompt. + Type + Boolean + Value + 1 + + MediaShowOnOthers + + Comment + Whether or not to show media on other avatars + Type + Boolean + Value + 1 + + MigrateCacheDirectory + + Comment + Check for old version of disk cache to migrate to current location + Type + Boolean + Value + 0 + + NavBarShowParcelProperties + + Comment + Show parcel property icons in navigation bar + Type + Boolean + Value + 0 + + NextLoginLocation + + Comment + Location to log into for this session - set from command line or the login panel, cleared following a successfull login. + Type + String + Value + home + + NotificationConferenceIMOptions + + Comment + + Specifies how the UI responds to Conference IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + none + + NotificationFriendIMOptions + + Comment + + Specifies how the UI responds to Friend IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + openconversations + + NotificationGroupChatOptions + + Comment + + Specifies how the UI responds to Group Chat Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + none + + NotificationNearbyChatOptions + + Comment + + Specifies how the UI responds to Nearby Chat Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + none + + NotificationNonFriendIMOptions + + Comment + + Specifies how the UI responds to Non Friend IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + openconversations + + NumSessions + + Comment + Number of successful logins to Second Life + Type + S32 + Value + 1674 + + PlayTypingAnim + + Comment + Your avatar plays the typing animation whenever you type in the chat bar + Type + Boolean + Value + 0 + + PoolSizeExpCache + + Comment + Coroutine Pool size for ExpCache + Type + U32 + Value + 5 + + PreferredBrowserBehavior + + Comment + 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). + Type + U32 + Value + 0 + + PreferredMaturity + + Comment + Setting for the user's preferred maturity level (consts in indra_constants.h) + Type + U32 + Value + 42 + + PresetGraphicActive + + Comment + Name of currently selected preference + Type + String + Value + Default + + ProbeHardwareOnStartup + + Comment + Query current hardware configuration on application startup + Type + Boolean + Value + 0 + + QAMode + + Comment + Enable Testing Features. + Type + Boolean + Value + 1 + + RenderAnisotropic + + Comment + Render textures using anisotropic filtering + Type + Boolean + Value + 1 + + RenderAvatarCloth + + Comment + Controls if avatars use wavy cloth + Type + Boolean + Value + 0 + + RenderAvatarLODFactor + + Comment + Controls level of detail of avatars (multiplier for current screen area when calculated level of detail) + Type + F32 + Value + 1 + + RenderFSAASamples + + Comment + Number of samples to use for FSAA (0 = no AA). + Type + U32 + Value + 2 + + RenderFarClip + + Comment + Distance of far clip plane from camera (meters) + Type + F32 + Value + 128 + + RenderMaxPartCount + + Comment + Maximum number of particles to display on screen + Type + S32 + Value + 2048 + + RenderQualityPerformance + + Comment + Which graphics settings you've chosen + Type + U32 + Value + 4 + + RenderReflectionDetail + + Comment + Detail of reflection render pass. + Type + S32 + Value + 0 + + RenderTerrainLODFactor + + Comment + Controls level of detail of terrain (multiplier for current screen area when calculated level of detail) + Type + F32 + Value + 2 + + RenderVBOEnable + + Comment + Use GL Vertex Buffer Objects + Type + Boolean + Value + 0 + + RenderVolumeLODFactor + + Comment + Controls level of detail of primitives (multiplier for current screen area when calculated level of detail) + Type + F32 + Value + 1.125 + + ShowAdvancedGraphicsSettings + + Comment + Show advanced graphics settings + Type + Boolean + Value + 1 + + ShowBanLines + + Comment + Show in-world ban/access borders + Type + Boolean + Value + 0 + + ShowStartLocation + + Comment + Display starting location menu on login screen + Type + Boolean + Value + 1 + + SkeletonFileName + + Comment + Alternative skeleton file name + Type + String + Value + avatar_skeleton_tentacles.xml + + SkyPresetName + + Comment + Sky preset to use. May be superseded by region settings or by a day cycle (see DayCycleName). + Type + String + Value + Sunset + + SnapshotConfigURL + + Comment + URL to fetch Snapshot Sharing configuration data from. + Type + String + Value + http://photos.apps.avatarsunited.com/viewer_config + + SnapshotFormat + + Comment + Save snapshots in this format (0 = PNG, 1 = JPEG, 2 = BMP) + Type + S32 + Value + 1 + + SnapshotQuality + + Comment + Quality setting of postcard JPEGs (0 = worst, 100 = best) + Type + S32 + Value + 100 + + SpellCheck + + Comment + Enable spellchecking on line and text editors + Type + Boolean + Value + 0 + + SpellCheckDictionary + + Comment + Current primary and secondary dictionaries used for spell checking + Type + String + Value + English (United States) + + TextureMemory + + Comment + Amount of memory to use for textures in MB (0 = autodetect) + Type + S32 + Value + 256 + + UseDayCycle + + Comment + Whether to use use a day cycle or a fixed sky. + Type + Boolean + Value + 0 + + UseDebugMenus + + Comment + Turns on "Debug" menu + Type + Boolean + Value + 1 + + UseEnvironmentFromRegion + + Comment + Choose whether to use the region's environment settings, or override them with the local settings. + Type + Boolean + Value + 0 + + VFSOldSize + + Comment + [DO NOT MODIFY] Controls resizing of local file cache + Type + U32 + Value + 102 + + VFSSalt + + Comment + [DO NOT MODIFY] Controls local file caching behavior + Type + U32 + Value + 260093998 + + VersionChannelName + + Comment + Version information generated by running the viewer + Type + String + Value + Second Life Release + + VertexShaderEnable + + Comment + Enable/disable all GLSL shaders (debug) + Type + Boolean + Value + 1 + + VoiceInputAudioDevice + + Comment + Audio input device to use for voice + Type + String + Value + C-Media USB Audio Device + + VoiceOutputAudioDevice + + Comment + Audio output device to use for voice + Type + String + Value + C-Media USB Audio Device + + WLSkyDetail + + Comment + Controls vertex detail on the WindLight sky. Lower numbers will give better performance and uglier skies. + Type + U32 + Value + 48 + + WebProfileFloaterRect + + Comment + Web profile floater dimensions + Type + Rect + Value + + 1189 + 957 + 1674 + 277 + + + WindowHeight + + Comment + SL viewer window height + Type + U32 + Value + 1000 + + WindowWidth + + Comment + SL viewer window width + Type + U32 + Value + 1626 + + WindowX + + Comment + X coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) + Type + S32 + Value + 50 + + WindowY + + Comment + Y coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) + Type + S32 + Value + 50 + + + 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): """ - 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(): 0 """ - 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) -- cgit v1.2.3