Class: Tddium

Inherits:
Thor
  • Object
show all
Includes:
TddiumConstant
Defined in:
lib/tddium/commands/spec.rb,
lib/tddium.rb,
lib/tddium/commands/login.rb,
lib/tddium/commands/suite.rb,
lib/tddium/commands/heroku.rb,
lib/tddium/commands/logout.rb,
lib/tddium/commands/status.rb,
lib/tddium/commands/account.rb,
lib/tddium/commands/activate.rb,
lib/tddium/commands/password.rb

Overview

Copyright © 2011 Solano Labs All Rights Reserved

Instance Method Summary collapse

Instance Method Details

#accountObject



8
9
10
11
12
13
14
15
16
17
18
# File 'lib/tddium/commands/account.rb', line 8

def 
  set_shell
  set_default_environment
  git_version_ok
  if user_details = user_logged_in?(true, true)
    # User is already logged in, so just display the info
    show_user_details(user_details)
  else
    exit_failure Text::Error::USE_ACTIVATE
  end
end

#activateObject



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/tddium/commands/activate.rb', line 10

def activate
  set_shell
  set_default_environment
  git_version_ok
  if user_details = user_logged_in?
    exit_failure Text::Error::ACTIVATE_LOGGED_IN
  else
    params = get_user_credentials(options.merge(:invited => true))

    # Prompt for the password confirmation if password is not from command line
    unless options[:password]
      password_confirmation = HighLine.ask(Text::Prompt::PASSWORD_CONFIRMATION) { |q| q.echo = "*" }
      unless password_confirmation == params[:password]
        exit_failure Text::Process::PASSWORD_CONFIRMATION_INCORRECT
      end
    end

    begin
      params[:user_git_pubkey] = prompt_ssh_key(options[:ssh_key_file])
    rescue TddiumError => e
      exit_failure e.message
    end

    # Prompt for accepting license
    content =  File.open(License::FILE_NAME) do |file|
      file.read
    end
    say content
    license_accepted = ask(Text::Prompt::LICENSE_AGREEMENT)
    exit_failure unless license_accepted.downcase == Text::Prompt::Response::AGREE_TO_LICENSE.downcase

    begin
      say Text::Process::STARTING_ACCOUNT_CREATION
      new_user = call_api(:post, Api::Path::USERS, {:user => params}, false, false)
      write_api_key(new_user["user"]["api_key"])
      role = new_user["user"]["account_role"]
      if role.nil? || role == "owner"
        u = new_user["user"]
        say Text::Process::ACCOUNT_CREATED % [u["email"], u["trial_remaining"], u["recurly_url"]]
      else
        say Text::Process::ACCOUNT_ADDED % [new_user["user"]["email"], new_user["user"]["account_role"], new_user["user"]["account"]]
      end
    rescue TddiumClient::Error::API => e
      exit_failure ((e.status == Api::ErrorCode::INVALID_INVITATION) ? Text::Error::INVALID_INVITATION : e.message)
    rescue TddiumClient::Error::Base => e
      exit_failure say e.message
    end
  end
end

#herokuObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/tddium/commands/heroku.rb', line 11

def heroku
  set_shell
  set_default_environment
  git_version_ok
  if user_details = user_logged_in?
    # User is already logged in, so just display the info
    show_user_details(user_details)
  else
    begin
      heroku_config = HerokuConfig.read_config(options[:app])
      # User has logged in to heroku, and TDDIUM environment variables are
      # present
      handle_heroku_user(options, heroku_config)
    rescue HerokuConfig::HerokuNotFound
      gemlist = `gem list heroku`
      msg = Text::Error::Heroku::NOT_FOUND % gemlist
      exit_failure msg
    rescue HerokuConfig::TddiumNotAdded
      exit_failure Text::Error::Heroku::NOT_ADDED
    rescue HerokuConfig::InvalidFormat
      exit_failure Text::Error::Heroku::INVALID_FORMAT
    rescue HerokuConfig::NotLoggedIn
      exit_failure Text::Error::Heroku::NOT_LOGGED_IN
    rescue HerokuConfig::AppNotFound
      exit_failure Text::Error::Heroku::APP_NOT_FOUND % options[:app]
    end
  end
end

#loginObject



9
10
11
12
13
14
15
16
17
18
19
# File 'lib/tddium/commands/login.rb', line 9

def 
  set_shell
  set_default_environment
  if user_logged_in?
    say Text::Process::ALREADY_LOGGED_IN
  elsif (:params => get_user_credentials(options), :show_error => true)
    say Text::Process::LOGGED_IN_SUCCESSFULLY 
  else
    exit_failure
  end
end

#logoutObject



7
8
9
10
11
12
# File 'lib/tddium/commands/logout.rb', line 7

def logout
  set_shell
  set_default_environment
  FileUtils.rm(tddium_file_name) if File.exists?(tddium_file_name)
  say Text::Process::LOGGED_OUT_SUCCESSFULLY
end

#passwordObject



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/tddium/commands/password.rb', line 8

def password
  set_shell
  set_default_environment
  exit_failure unless tddium_settings
  user_details = user_logged_in?
  exit_failure unless user_details
  
  params = {}
  params[:current_password] = HighLine.ask(Text::Prompt::CURRENT_PASSWORD) { |q| q.echo = "*" }
  params[:password] = HighLine.ask(Text::Prompt::NEW_PASSWORD) { |q| q.echo = "*" }
  params[:password_confirmation] = HighLine.ask(Text::Prompt::PASSWORD_CONFIRMATION) { |q| q.echo = "*" }

  begin
    user_id = user_details["user"]["id"]
    result = call_api(:put, "#{Api::Path::USERS}/#{user_id}/", {:user=>params},
                      tddium_settings["api_key"], false)
    say Text::Process::PASSWORD_CHANGED
  rescue TddiumClient::Error::API => e
    exit_failure Text::Error::PASSWORD_ERROR % e.explanation
  rescue TddiumClient::Error::Base => e
    exit_failure e.message
  end
end

#spec(*pattern) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/tddium/commands/spec.rb', line 15

def spec(*pattern)
  machine_data = {}

  set_shell
  set_default_environment
  git_version_ok
  exit_failure unless git_repo? && tddium_settings && suite_for_current_branch?

  if git_changes then
    exit_failure(Text::Error::GIT_CHANGES_NOT_COMMITTED) if !options[:force]
    warn(Text::Warning::GIT_CHANGES_NOT_COMMITTED)
  end

  test_execution_params = {}

  if user_data_file_path = options[:user_data_file]
    if File.exists?(user_data_file_path)
      user_data = File.open(user_data_file_path) { |file| file.read }
      test_execution_params[:user_data_text] = Base64.encode64(user_data)
      test_execution_params[:user_data_filename] = File.basename(user_data_file_path)
      say Text::Process::USING_SPEC_OPTION[:user_data_file] % user_data_file_path
    else
      exit_failure Text::Error::NO_USER_DATA_FILE % user_data_file_path
    end
  end

  if max_parallelism = options[:max_parallelism]
    test_execution_params[:max_parallelism] = max_parallelism
    say Text::Process::USING_SPEC_OPTION[:max_parallelism] % max_parallelism
  end
  
  test_pattern = nil

  if pattern.is_a?(Array) && pattern.size > 0
    test_pattern = pattern.join(",")
  end

  test_pattern ||= options[:test_pattern]
  if test_pattern
    say Text::Process::USING_SPEC_OPTION[:test_pattern] % test_pattern
  end

  start_time = Time.now

  # Call the API to get the suite and its tests
  suite_details = call_api(:get, current_suite_path)

  exit_failure Text::Error::GIT_REPO_NOT_READY unless suite_details["suite"]["repoman_current"]

  # Push the latest code to git
  exit_failure Text::Error::GIT_PUSH_FAILED unless update_git_remote_and_push(suite_details)

  # Create a session
  new_session = call_api(:post, Api::Path::SESSIONS)
  machine_data[:session_id] = session_id = new_session["session"]["id"]

  # Register the tests
  call_api(:post, "#{Api::Path::SESSIONS}/#{session_id}/#{Api::Path::REGISTER_TEST_EXECUTIONS}", {:suite_id => current_suite_id, :test_pattern => test_pattern})

  # Start the tests
  start_test_executions = call_api(:post, "#{Api::Path::SESSIONS}/#{session_id}/#{Api::Path::START_TEST_EXECUTIONS}", test_execution_params)

  num_tests_started = start_test_executions["started"].to_i
  
  say Text::Process::STARTING_TEST % num_tests_started.to_s

  tests_not_finished_yet = true
  finished_tests = {}
  test_statuses = Hash.new(0)

  say Text::Process::CHECK_TEST_REPORT % start_test_executions["report"] unless options[:machine]
  say Text::Process::TERMINATE_INSTRUCTION unless options[:machine]
  while tests_not_finished_yet do
    # Poll the API to check the status
    current_test_executions = call_api(:get, "#{Api::Path::SESSIONS}/#{session_id}/#{Api::Path::TEST_EXECUTIONS}")

    # Catch Ctrl-C to interrupt the test
    Signal.trap(:INT) do
      say Text::Process::INTERRUPT
      say Text::Process::CHECK_TEST_STATUS
      tests_not_finished_yet = false
    end

    # Print out the progress of running tests
    current_test_executions["tests"].each do |test_name, result_params|
      test_status = result_params["status"]
      if result_params["finished"] && !finished_tests[test_name]
        message = case test_status
                    when "passed" then [".", :green, false]
                    when "failed" then ["F", :red, false]
                    when "error" then ["E", nil, false]
                    when "pending" then ["*", :yellow, false]
                    when "skipped" then [".", :yellow, false]
                    else [".", nil, false]
                  end
        finished_tests[test_name] = test_status
        test_statuses[test_status] += 1
        say *message
      end
    end

    # If all tests finished, exit the loop else sleep
    if finished_tests.size >= num_tests_started
      tests_not_finished_yet = false
    else
      sleep(Default::SLEEP_TIME_BETWEEN_POLLS)
    end
  end

  # Print out the result
  say ""
  say Text::Process::FINISHED_TEST % (Time.now - start_time)
  say "#{finished_tests.size} tests, #{test_statuses["failed"]} failures, #{test_statuses["error"]} errors, #{test_statuses["pending"]} pending"

  write_suite(suite_details["suite"].merge({"id" => current_suite_id}))

  exit_failure if test_statuses["failed"] > 0 || test_statuses["error"] > 0
rescue TddiumClient::Error::Base
  exit_failure "Failed due to error communicating with Tddium"
rescue RuntimeError => e
  exit_failure "Failed due to internal error: #{e.inspect} #{e.backtrace}"
ensure
  if options[:machine] && machine_data.size > 0
    say "%%%% TDDIUM CI DATA BEGIN %%%%"
    say YAML.dump(machine_data)
    say "%%%% TDDIUM CI DATA END %%%%"
  end
end

#statusObject



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/tddium/commands/status.rb', line 7

def status
  set_shell
  set_default_environment
  git_version_ok
  exit_failure unless git_repo? && tddium_settings

  begin
    current_suites = call_api(:get, Api::Path::SUITES)
    if current_suites["suites"].size == 0
      say Text::Status::NO_SUITE
    else
      if current_suite = current_suites["suites"].detect {|suite| suite["id"] == current_suite_id}
        say Text::Status::CURRENT_SUITE % current_suite["repo_name"]
        display_attributes(DisplayedAttributes::SUITE, current_suite)
        say Text::Status::SEPARATOR
      else
        say Text::Status::CURRENT_SUITE_UNAVAILABLE
      end
    end
    show_session_details({:active => false, :order => "date", :limit => 10}, Text::Status::NO_INACTIVE_SESSION, Text::Status::INACTIVE_SESSIONS)
    show_session_details({:active => true, :order => "date"}, Text::Status::NO_ACTIVE_SESSION, Text::Status::ACTIVE_SESSIONS)
  rescue TddiumClient::Error::Base => e
    exit_failure e.message
  end
end

#suiteObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/tddium/commands/suite.rb', line 16

def suite
  set_default_environment
  git_version_ok
  exit_failure unless tddium_settings && git_repo?

  params = {}
  begin
    if current_suite_id
      current_suite = call_api(:get, current_suite_path)["suite"]

      if options[:edit]
        update_suite(current_suite, options)
      else
        say Text::Process::EXISTING_SUITE % format_suite_details(current_suite)
      end
    else
      params[:branch] = current_git_branch
      default_suite_name = File.basename(Dir.pwd)
      params[:repo_name] = options[:name] || default_suite_name

      say Text::Process::NO_CONFIGURED_SUITE % [params[:repo_name], params[:branch]]

      use_existing_suite, existing_suite = resolve_suite_name(options, params, default_suite_name)

      if use_existing_suite
        # Write to file and exit when using the existing suite
        write_suite(existing_suite)
        say Text::Status::USING_SUITE % format_suite_details(existing_suite)
        return
      end

      prompt_suite_params(options, params)

      params.each do |k,v|
        params.delete(k) if v == 'disable'
      end

      # Create new suite if it does not exist yet
      say Text::Process::CREATING_SUITE % [params[:repo_name], params[:branch]]
      new_suite = call_api(:post, Api::Path::SUITES, {:suite => params})
      # Save the created suite
      write_suite(new_suite["suite"])

      say Text::Process::CREATED_SUITE % format_suite_details(new_suite["suite"])
    end
  rescue TddiumClient::Error::Base
    exit_failure
  end
end

#versionObject



55
56
57
# File 'lib/tddium.rb', line 55

def version
  say TddiumVersion::VERSION
end