diff --git a/launchers/darwin/CMakeLists.txt b/launchers/darwin/CMakeLists.txt
index fa4c9d5f45..b495893b97 100644
--- a/launchers/darwin/CMakeLists.txt
+++ b/launchers/darwin/CMakeLists.txt
@@ -45,6 +45,8 @@ set(src_files
src/CustomUI.m
src/NSTask+NSTaskExecveAdditions.h
src/NSTask+NSTaskExecveAdditions.m
+ src/HQDefaults.h
+ src/HQDefaults.m
src/main.mm
nib/Window.xib
nib/SplashScreen.xib
@@ -118,6 +120,10 @@ add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/images "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
+add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_SOURCE_DIR}/data/HQDefaults.plist "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
+
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND updater
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/updater" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
@@ -146,3 +152,17 @@ set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc"
set(CPACK_GENERATOR "DragNDrop")
include(CPack)
+
+include(FindXCTest)
+
+include_directories(${CMAKE_SOURCE_DIR}/src)
+
+xctest_add_bundle(HQLauncherTests HQLauncher
+ ${CMAKE_SOURCE_DIR}/src/HQDefaults.m
+ ${CMAKE_SOURCE_DIR}/tests/HQDefaultsTests.m
+ ${CMAKE_SOURCE_DIR}/tests/Info.plist
+)
+
+set_target_properties(HQLauncherTests PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/tests/Info.plist
+ )
diff --git a/launchers/darwin/data/HQDefaults.plist b/launchers/darwin/data/HQDefaults.plist
new file mode 100644
index 0000000000..98a29664c9
--- /dev/null
+++ b/launchers/darwin/data/HQDefaults.plist
@@ -0,0 +1,14 @@
+
+
+
+
+
+ name
+ thunderURL
+ defaultValue
+ https://thunder.highfidelity.com
+ environmentVariable
+ HIFI_THUNDER_URL
+
+
+
diff --git a/launchers/darwin/src/HQDefaults.h b/launchers/darwin/src/HQDefaults.h
new file mode 100644
index 0000000000..631a672ddf
--- /dev/null
+++ b/launchers/darwin/src/HQDefaults.h
@@ -0,0 +1,46 @@
+//
+// Created by Matt Hardcastle
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/*
+ * `HQDefaults` loads defaults from the `HQDefaults.plist` and allows
+ * defaults in that plist to be overwritten using an environment variable.
+ *
+ * For example, the following `HQDefaults.plist` will set a default "foo"
+ * with a value "bar". The "bar" value can be overwritten with the value of
+ * the FOO environment variables, if the FOO environment variable is set.
+ *
+ *
+ *
+ *
+ *
+ *
+ * name
+ * foo
+ * defaultValue
+ * bar
+ * environmentVariable
+ * FOO
+ *
+ *
+ *
+ */
+
+@interface HQDefaults : NSObject
+
+-(NSString *)defaultNamed:(NSString *)name;
++(HQDefaults *)sharedDefaults;
+
+@property (strong, readonly) NSDictionary *defaults;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/launchers/darwin/src/HQDefaults.m b/launchers/darwin/src/HQDefaults.m
new file mode 100644
index 0000000000..5795b05554
--- /dev/null
+++ b/launchers/darwin/src/HQDefaults.m
@@ -0,0 +1,74 @@
+//
+// Created by Matt Hardcastle
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#import "HQDefaults.h"
+
+@implementation HQDefaults;
+
+// This initializer is for testing purposes. See `init()` for normal use.
+-(id)initWithArray:(NSArray *)array environment:(NSDictionary *)environment
+{
+ self = [super init];
+ if (self) {
+ NSMutableDictionary *__defaults = [[NSMutableDictionary alloc] init];
+ NSString *name, *defaultValue, *environmentVariable;
+
+ for (NSDictionary *obj in array) {
+ NSMutableArray *missingKeys = [[NSMutableArray alloc] init];
+ if ((name = [obj objectForKey:@"name"]) == nil) {
+ [missingKeys addObject:@"name"];
+ }
+ if ((defaultValue = [obj objectForKey:@"defaultValue"]) == nil) {
+ [missingKeys addObject:@"defaultValue"];
+ }
+ if ([missingKeys count] > 0) {
+ @throw [NSException exceptionWithName:@"InvalidHQDefaults"
+ reason:@"A required key is missing"
+ userInfo:@{@"missingKeys": missingKeys}];
+ }
+
+ environmentVariable = [obj objectForKey:@"environmentVariable"];
+ if (environmentVariable == nil) {
+ __defaults[name] = defaultValue;
+ continue;
+ }
+
+ NSString *value = environment[environmentVariable];
+ __defaults[name] = value == nil ? defaultValue : value;
+ }
+
+ // Make the dictionary immutable.
+ _defaults = __defaults;
+ }
+ return self;
+}
+
+// Initialize an `HQLauncher` object using the bundles "HQDefaults.plist" and the current process's environment.
+-(id)init {
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSString *defaultsPath = [bundle pathForResource:@"HQDefaults" ofType:@"plist"];
+ NSArray *array = [NSArray arrayWithContentsOfFile:defaultsPath];
+ return [self initWithArray:array environment:NSProcessInfo.processInfo.environment];
+}
+
+// Retrieve a default.
+-(NSString *)defaultNamed:(NSString *)name {
+ return _defaults[name];
+}
+
+// A singleton HQDefaults using the mainBundle's "HQDefaults.plist" and the environment.
++(HQDefaults *)sharedDefaults {
+ static HQDefaults *defaults = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ defaults = [[HQDefaults alloc] init];
+ });
+ return defaults;
+}
+
+@end
diff --git a/launchers/darwin/src/LatestBuildRequest.m b/launchers/darwin/src/LatestBuildRequest.m
index 45ed4418ad..43cd5de213 100644
--- a/launchers/darwin/src/LatestBuildRequest.m
+++ b/launchers/darwin/src/LatestBuildRequest.m
@@ -1,6 +1,7 @@
#import "LatestBuildRequest.h"
#import "Launcher.h"
#import "Settings.h"
+#import "HQDefaults.h"
@implementation LatestBuildRequest
@@ -8,7 +9,13 @@
NSString* buildsURL = [[[NSProcessInfo processInfo] environment] objectForKey:@"HQ_LAUNCHER_BUILDS_URL"];
if ([buildsURL length] == 0) {
- buildsURL = @"https://thunder.highfidelity.com/builds/api/tags/latest?format=json";
+ NSString *thunderURL = [[HQDefaults sharedDefaults] defaultNamed:@"thunderURL"];
+ if (thunderURL == nil) {
+ @throw [NSException exceptionWithName:@"DefaultMissing"
+ reason:@"The thunderURL default is missing"
+ userInfo:nil];
+ }
+ buildsURL = [NSString stringWithFormat:@"%@/builds/api/tags/latest?format=json", thunderURL];
}
NSLog(@"Making request for builds to: %@", buildsURL);
diff --git a/launchers/darwin/src/Launcher.m b/launchers/darwin/src/Launcher.m
index 9a70ddf0e7..62242d12ff 100644
--- a/launchers/darwin/src/Launcher.m
+++ b/launchers/darwin/src/Launcher.m
@@ -9,6 +9,7 @@
#import "Settings.h"
#import "NSTask+NSTaskExecveAdditions.h"
#import "Interface.h"
+#import "HQDefaults.h"
@interface Launcher ()
@@ -483,6 +484,17 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
[self.window makeKeyAndOrderFront:self];
}
+-(void)applicationDidFinishLaunching:(NSNotification *)notification
+{
+ // Sanity check the HQDefaults so we fail early if there's an issue.
+ HQDefaults *defaults = [HQDefaults sharedDefaults];
+ if ([defaults defaultNamed:@"thunderURL"] == nil) {
+ @throw [NSException exceptionWithName:@"DefaultsNotConfigured"
+ reason:@"thunderURL is not configured"
+ userInfo:nil];
+ }
+}
+
- (void) setDownloadFilename:(NSString *)aFilename
{
self.filename = aFilename;
diff --git a/launchers/darwin/tests/HQDefaultsTests.m b/launchers/darwin/tests/HQDefaultsTests.m
new file mode 100644
index 0000000000..483a54e945
--- /dev/null
+++ b/launchers/darwin/tests/HQDefaultsTests.m
@@ -0,0 +1,78 @@
+//
+// Created by Matt Hardcastle
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#import
+
+#import "HQDefaults.h"
+
+// Expose the `initWithArray:environment` initializer for testing.
+@interface HQDefaults(HQDefaultsTesting)
+-(id)initWithArray:(NSArray *)array environment:(NSDictionary *)environment;
+@end
+
+@interface HQDefaultsTests : XCTestCase
+
+@end
+
+@implementation HQDefaultsTests
+
+// If there is not environment the default value should be used.
+- (void)testNoEnvironmentReturnsDefaultValue {
+ NSArray *array = @[@{@"name":@"foo",
+ @"environmentVariable":@"FOO",
+ @"defaultValue":@"bar"}];
+
+ HQDefaults *defaults = [[HQDefaults alloc] initWithArray:array
+ environment:@{}];
+
+ XCTAssertEqual([defaults defaultNamed:@"foo"], @"bar");
+}
+
+// The value from the environment should overwrite the default value.
+- (void)testEnvironmentOverWritesDefaultValue {
+ NSArray *array = @[@{@"name":@"foo",
+ @"environmentVariable":@"FOO",
+ @"defaultValue":@"bar"}];
+ NSDictionary *environment = @{@"FOO":@"FOO_VALUE_FROM_ENVIRONMENT"};
+
+ HQDefaults *defaults = [[HQDefaults alloc] initWithArray:array environment:environment];
+
+ NSString *value = [defaults defaultNamed:@"foo"];
+
+ XCTAssertEqual(value, @"FOO_VALUE_FROM_ENVIRONMENT");
+}
+
+// An exception should be thrown if a defaults object is missing `name`.
+- (void)testMissingNameThrowsException {
+ NSArray *array = @[@{@"defaultValue":@"bar"}];
+ XCTAssertThrowsSpecificNamed([[HQDefaults alloc] initWithArray:array environment:@{}],
+ NSException,
+ @"InvalidHQDefaults");
+}
+
+// An exception should be thrown if a defaults object is missing `defaultValue`.
+- (void)testMissingDefaultValueThrowsException {
+ NSArray *array = @[@{@"name":@"foo" }];
+ XCTAssertThrowsSpecificNamed([[HQDefaults alloc] initWithArray:array environment:@{}],
+ NSException,
+ @"InvalidHQDefaults");
+}
+
+// An exception should **NOT** be thrown if a defaults object is missing `environmentVariable`.
+- (void)testMissingEnvironmentVariableDoesNotThrowException {
+ NSArray *array = @[@{@"name":@"foo", @"defaultValue":@"bar"}];
+ XCTAssertNoThrow([[HQDefaults alloc] initWithArray:array environment:@{}]);
+}
+
+// A `nil` should be returned if a default is missing.
+- (void)testEmptyDefaultIsNil {
+ HQDefaults *defaults = [[HQDefaults alloc] initWithArray:@[] environment:@{}];
+ XCTAssertNil([defaults defaultNamed:@"foo"]);
+}
+
+@end
diff --git a/launchers/darwin/tests/Info.plist b/launchers/darwin/tests/Info.plist
new file mode 100644
index 0000000000..6c40a6cd0c
--- /dev/null
+++ b/launchers/darwin/tests/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+