# Extracts the secrets for the XMMP account `account_name`. def xmpp_account(account_name, required_options = []) begin account = $config["Pidgin"]["Accounts"]["XMPP"][account_name] check_keys = ["username", "domain", "password"] + required_options for key in check_keys do assert(account.has_key?(key)) assert_not_nil(account[key]) assert(!account[key].empty?) end rescue NoMethodError, Test::Unit::AssertionFailedError raise( < LIVE_USER).stdout.chomp @chat_room_jid = chat_room + "@" + conference_server @screen.click("PidginJoinChatButton.png") # The following will both make sure that the we joined the chat, and # that it is empty. We'll also deal with the *potential* "Create New # Room" prompt that Pidgin shows for some server configurations. images = ["PidginCreateNewRoomPrompt.png", "PidginChat1UserInRoom.png"] image_found, _ = @screen.waitAny(images, 30) if image_found == "PidginCreateNewRoomPrompt.png" @screen.click("PidginCreateNewRoomAcceptDefaultsButton.png") end $vm.focus_window(@chat_room_jid) @screen.wait("PidginChat1UserInRoom.png", 10) end # Since some servers save the scrollback, and sends it when joining, # it's safer to clear it so we do not get false positives from old # messages when looking for a particular response, or similar. When /^I clear the multi-user chat's scrollback$/ do $vm.focus_window(@chat_room_jid) @screen.click("PidginConversationMenu.png") @screen.wait_and_click("PidginConversationMenuClearScrollback.png", 10) end Then /^I can see that my friend joined the multi-user chat$/ do $vm.focus_window(@chat_room_jid) @screen.wait("PidginChat2UsersInRoom.png", 60) end def configured_pidgin_accounts accounts = Hash.new xml = REXML::Document.new( $vm.file_content("/home/#{LIVE_USER}/.purple/accounts.xml") ) xml.elements.each("account/account") do |e| account = e.elements["name"].text account_name, network = account.split("@") protocol = e.elements["protocol"].text port = e.elements["settings/setting[@name='port']"].text username_element = e.elements["settings/setting[@name='username']"] realname_elemenet = e.elements["settings/setting[@name='realname']"] if username_element nickname = username_element.text else nickname = nil end if realname_elemenet real_name = realname_elemenet.text else real_name = nil end accounts[network] = { 'name' => account_name, 'network' => network, 'protocol' => protocol, 'port' => port, 'nickname' => nickname, 'real_name' => real_name, } end return accounts end def chan_image (account, channel, image) images = { 'conference.riseup.net' => { 'tails' => { 'conversation_tab' => 'PidginTailsConversationTab', 'welcome' => 'PidginTailsChannelWelcome', } }, } return images[account][channel][image] + ".png" end def default_chan (account) chans = { 'conference.riseup.net' => 'tails', } return chans[account] end def pidgin_otr_keys return $vm.file_content("/home/#{LIVE_USER}/.purple/otr.private_key") end Given /^Pidgin has the expected accounts configured with random nicknames$/ do expected = [ ["irc.oftc.net", "prpl-irc", "6697"], ["127.0.0.1", "prpl-irc", "6668"], ] configured_pidgin_accounts.values.each() do |account| assert(account['nickname'] != "XXX_NICK_XXX", "Nickname was no randomised") assert_equal(account['nickname'], account['real_name'], "Nickname and real name are not identical: " + account['nickname'] + " vs. " + account['real_name']) assert_equal(account['name'], account['nickname'], "Account name and nickname are not identical: " + account['name'] + " vs. " + account['nickname']) candidate = [account['network'], account['protocol'], account['port']] assert(expected.include?(candidate), "Unexpected account: #{candidate}") expected.delete(candidate) end assert(expected.empty?, "These Pidgin accounts are not configured: " + "#{expected}") end When /^I open Pidgin's account manager window$/ do @screen.wait_and_click('PidginMenuAccounts.png', 20) @screen.wait_and_click('PidginMenuManageAccounts.png', 20) step "I see Pidgin's account manager window" end When /^I see Pidgin's account manager window$/ do @screen.wait("PidginAccountWindow.png", 40) end When /^I close Pidgin's account manager window$/ do @screen.wait_and_click("PidginDialogCloseButton.png", 10) end When /^I close Pidgin$/ do $vm.focus_window('Buddy List') @screen.type("q", Sikuli::KeyModifier.CTRL) @screen.waitVanish('PidginAvailableStatus.png', 10) end When /^I (de)?activate the "([^"]+)" Pidgin account$/ do |deactivate, account| @screen.click("PidginAccount_#{account}.png") @screen.type(Sikuli::Key.LEFT + Sikuli::Key.SPACE) if deactivate @screen.waitVanish('PidginAccountEnabledCheckbox.png', 5) else # wait for the Pidgin to be connecting, otherwise sometimes the step # that closes the account management dialog happens before the account # is actually enabled @screen.waitAny(['PidginConnecting.png', 'PidginAvailableStatus.png'], 5) end end def deactivate_and_activate_pidgin_account(account) debug_log("Deactivating and reactivating Pidgin account #{account}") step "I open Pidgin's account manager window" step "I deactivate the \"#{account}\" Pidgin account" step "I close Pidgin's account manager window" step "I open Pidgin's account manager window" step "I activate the \"#{account}\" Pidgin account" step "I close Pidgin's account manager window" end Then /^Pidgin successfully connects to the "([^"]+)" account$/ do |account| expected_channel_entry = chan_image(account, default_chan(account), 'roster') reconnect_button = 'PidginReconnect.png' recovery_on_failure = Proc.new do if @screen.exists('PidginReconnect.png') @screen.click('PidginReconnect.png') else deactivate_and_activate_pidgin_account(account) end end retry_tor(recovery_on_failure) do begin $vm.focus_window('Buddy List') rescue ExecutionFailedInVM # Sometimes focusing the window with xdotool will fail with the # conversation window right on top of it. We'll try to close the # conversation window. At worst, the test will still fail... close_pidgin_conversation_window(account) end on_screen, _ = @screen.waitAny([expected_channel_entry, reconnect_button], 60) unless on_screen == expected_channel_entry raise "Connecting to account #{account} failed." end end end Then /^the "([^"]*)" account only responds to PING and VERSION CTCP requests$/ do |irc_server| ctcp_cmds = [ "CLIENTINFO", "DATE", "ERRMSG", "FINGER", "PING", "SOURCE", "TIME", "USERINFO", "VERSION" ] expected_ctcp_replies = { "PING" => /^\d+$/, "VERSION" => /^Purple IRC$/ } spam_target = configured_pidgin_accounts[irc_server]["nickname"] ctcp_check = CtcpChecker.new(irc_server, 6667, spam_target, ctcp_cmds, expected_ctcp_replies) ctcp_check.verify_ctcp_responses end Then /^I can join the( pre-configured)? "([^"]+)" channel on "([^"]+)"$/ do |preconfigured, channel, account| if preconfigured @screen.doubleClick(chan_image(account, channel, 'roster')) focus_pidgin_irc_conversation_window(account) else $vm.focus_window('Buddy List') @screen.wait_and_click("PidginBuddiesMenu.png", 20) @screen.wait_and_click("PidginBuddiesMenuJoinChat.png", 10) @screen.wait_and_click("PidginJoinChatWindow.png", 10) @screen.click_mid_right_edge("PidginJoinChatRoomLabel.png") @screen.type(channel) @screen.click("PidginJoinChatButton.png") @chat_room_jid = channel + "@" + account $vm.focus_window(@chat_room_jid) end @screen.hide_cursor try_for(60) do begin @screen.wait_and_click(chan_image(account, channel, 'conversation_tab'), 5) rescue FindFailed => e # If the channel tab can't be found it could be because there were # multiple connection attempts and the channel tab we want is off the # screen. We'll try closing tabs until the one we want can be found. @screen.type("w", Sikuli::KeyModifier.CTRL) raise e end end @screen.hide_cursor @screen.wait( chan_image(account, channel, 'welcome'), 10) end Then /^I take note of the configured Pidgin accounts$/ do @persistent_pidgin_accounts = configured_pidgin_accounts end Then /^I take note of the OTR key for Pidgin's "([^"]+)" account$/ do |account_name| @persistent_pidgin_otr_keys = pidgin_otr_keys end Then /^Pidgin has the expected persistent accounts configured$/ do current_accounts = configured_pidgin_accounts assert(current_accounts <=> @persistent_pidgin_accounts, "Currently configured Pidgin accounts do not match the persistent ones:\n" + "Current:\n#{current_accounts}\n" + "Persistent:\n#{@persistent_pidgin_accounts}" ) end Then /^Pidgin has the expected persistent OTR keys$/ do assert_equal(pidgin_otr_keys, @persistent_pidgin_otr_keys) end def pidgin_add_certificate_from (cert_file) # Here, we need a certificate that is not already in the NSS database step "I copy \"/usr/share/ca-certificates/mozilla/CNNIC_ROOT.crt\" to \"#{cert_file}\" as user \"amnesia\"" $vm.focus_window('Buddy List') @screen.wait_and_click('PidginToolsMenu.png', 10) @screen.wait_and_click('PidginCertificatesMenuItem.png', 10) @screen.wait('PidginCertificateManagerDialog.png', 10) @screen.wait_and_click('PidginCertificateAddButton.png', 10) begin @screen.wait_and_click('GtkFileChooserDesktopButton.png', 10) rescue FindFailed # The first time we're run, the file chooser opens in the Recent # view, so we have to browse a directory before we can use the # "Type file name" button. But on subsequent runs, the file # chooser is already in the Desktop directory, so we don't need to # do anything. Hence, this noop exception handler. end @screen.wait_and_click('GtkFileTypeFileNameButton.png', 10) @screen.type("l", Sikuli::KeyModifier.ALT) # "Location" field @screen.type(cert_file + Sikuli::Key.ENTER) end Then /^I can add a certificate from the "([^"]+)" directory to Pidgin$/ do |cert_dir| pidgin_add_certificate_from("#{cert_dir}/test.crt") wait_and_focus('PidginCertificateAddHostnameDialog.png', 10, 'Certificate Import') @screen.type("XXX test XXX" + Sikuli::Key.ENTER) wait_and_focus('PidginCertificateTestItem.png', 10, 'Certificate Manager') end Then /^I cannot add a certificate from the "([^"]+)" directory to Pidgin$/ do |cert_dir| pidgin_add_certificate_from("#{cert_dir}/test.crt") wait_and_focus('PidginCertificateImportFailed.png', 10, 'Import Error') end When /^I close Pidgin's certificate manager$/ do wait_and_focus('PidginCertificateManagerDialog.png', 10, 'Certificate Manager') @screen.type(Sikuli::Key.ESC) # @screen.wait_and_click('PidginCertificateManagerClose.png', 10) @screen.waitVanish('PidginCertificateManagerDialog.png', 10) end When /^I close Pidgin's certificate import failure dialog$/ do @screen.type(Sikuli::Key.ESC) # @screen.wait_and_click('PidginCertificateManagerClose.png', 10) @screen.waitVanish('PidginCertificateImportFailed.png', 10) end When /^I see the Tails roadmap URL$/ do try_for(60) do if @screen.exists('PidginServerMessage.png') @screen.click('PidginDialogCloseButton.png') end begin @screen.find('PidginTailsRoadmapUrl.png') rescue FindFailed => e @screen.type(Sikuli::Key.PAGE_UP) raise e end end end When /^I click on the Tails roadmap URL$/ do @screen.click('PidginTailsRoadmapUrl.png') try_for(60) { @torbrowser = Dogtail::Application.new('Firefox') } end