Nice work Geoff. The code looks good, just marking as "Needs fixing" as there are some aesthetics that could be worked out. [1] 82 + def __init__(self, configuration): 83 + self.__lock_out = True 84 + self.__lock = threading.Lock() 85 + self.__initial_server_host_name = self.DEFAULT_SERVER_HOST_NAME 86 + self.__configuration = configuration 87 + self.__configuration 88 + self.__configuration.load([]) 89 + self.__load_data_from_config() 90 + self.__modified = False 91 + self.unlock() We don't need that self.__configuration in 87. [2] 93 + def default_dedicated(self): 94 + """ 95 + Set L{server_host_name} to something sane when switching from hosted to 96 + dedicated 97 + """ We need a period at the end of line. [3] 160 + def __derive_server_host_name_from_url(self, url): 161 + "Extract the hostname part from a url" 162 + try: 163 + without_protocol = url[url.index("://") + 3:] 164 + except ValueError: 165 + without_protocol = url 166 + try: 167 + return without_protocol[:without_protocol.index("/")] 168 + except ValueError: 169 + return without_protocol Maybe we could store the result of urlparse.urlparse(url) and reuse it whenever we need it. Also, the docstring must be a sentence ending with a period. I've observed it in some other methods as well. [4] 354 + def setUp(self): 355 + super(LandscapeSettingsApplicationControllerUISetupTest, self).setUp() 356 + 357 + def fake_run(obj): 358 + """ 359 + Retard X11 mapping. 360 + """ 361 + pass 362 + self._real_run = Gtk.Dialog.run 363 + Gtk.Dialog.run = fake_run Do you think the test suite will evolve much more for GUI? Maybe we could have another iteration to create some GUI specific base class and add stuff like this in it. [5] 365 + def get_config(): 366 + configdata = """ 367 +[client] 368 +data_path = %s 369 +http_proxy = http://proxy.localdomain:3192 370 +tags = a_tag 371 +url = https://landscape.canonical.com/message-system 372 +account_name = foo 373 +registration_password = bar 374 +computer_title = baz 375 +https_proxy = https://proxy.localdomain:6192 376 +ping_url = http://landscape.canonical.com/ping 377 + 378 +""" % sys.path[0] Maybe we could make it less unindented: def get_config(): configdata = ("[client]" "data_path = %s" "http_proxy = http://proxy.localdomain:3192" "tags = a_tag" "url = https://landscape.canonical.com/message-system" "account_name = foo" "registration_password = bar" "computer_title = baz" "https_proxy = https://proxy.localdomain:6192" "ping_url = http://landscape.canonical.com/ping") % sys.path[0] [6] 381 + class MyLandscapeSettingsConfiguration( 382 + LandscapeSettingsConfiguration): 383 + default_config_filenames = [config_filename] 384 + config = MyLandscapeSettingsConfiguration(None) 385 + return config 386 + self.app = ConnectionRecordingLandscapeSettingsApplicationController( 387 + get_config_f=get_config) Maybe we could make it a little easier on the eyes: 381 + class MyLandscapeSettingsConfiguration( 382 + LandscapeSettingsConfiguration): 383 + default_config_filenames = [config_filename] 384 + 385 + 386 + config = MyLandscapeSettingsConfiguration(None) 387 + return config 388 + 389 + self.app = ConnectionRecordingLandscapeSettingsApplicationController( 390 + get_config_f=get_config) [7] 418 + def setUp(self): 419 + super(ConfigControllerTest, self).setUp() 420 + config = """ 421 +[client] 422 +data_path = /var/lib/landscape/client 423 +http_proxy = http://proxy.localdomain:3192 424 +tags = a_tag 425 +url = https://landscape.canonical.com/message-system 426 +account_name = foo 427 +registration_password = bar 428 +computer_title = baz 429 +https_proxy = https://proxy.localdomain:6192 430 +ping_url = http://landscape.canonical.com/ping 431 + 432 +""" If you consider the indentation I proposed up there, maybe you could fix this one as well. And the same goes for: 435 + class MyLandscapeSettingsConfiguration(LandscapeSettingsConfiguration): 436 + default_config_filenames = [self.config_filename] 437 + self.config = MyLandscapeSettingsConfiguration(None) Also in this block: 938 + def setUp(self): 939 + super(ConfigurationViewCommitTest, self).setUp() 940 + config = """ 941 +[client] 942 +data_path = %s 943 +http_proxy = http://proxy.localdomain:3192 944 +tags = a_tag 945 +url = https://landscape.canonical.com/message-system 946 +account_name = foo 947 +registration_password = bar 948 +computer_title = baz 949 +https_proxy = https://proxy.localdomain:6192 950 +ping_url = http://landscape.canonical.com/ping 951 +""" % sys.path[0] 952 + self.config_filename = self.makeFile(config) 953 + 954 + class MyLandscapeSetupConfiguration(LandscapeSetupConfiguration): 955 + default_config_filenames = [self.config_filename] 956 + self.config = MyLandscapeSetupConfiguration(None) 957 + self.real_write_back = LandscapeClientSettingsDialog._write_back 958 + self.write_back_called = False Line breaks in between 955 and 956. 959 + 960 + def fake_write_back(obj): 961 + self.write_back_called = True 962 + LandscapeClientSettingsDialog._write_back = fake_write_back 963 + self.controller = ConfigController(self.config) 964 + self.dialog = LandscapeClientSettingsDialog(self.controller) Like breaks between 961 and 962. You can double check all the setUp code in the tests as you define the config every time and check that we have the same pattern. Thanks!