module AutomatedTestsHelper

Helper methods for Testing Framework forms

Public Instance Methods

can_run_test?() click to toggle source

Verify tests can be executed

# File app/helpers/automated_tests_helper.rb, line 149
def can_run_test?
  if @current_user.admin?
    true
  elsif @current_user.ta?
    true
  elsif @current_user.student?
    # Make sure student belongs to this group
    unless @current_user.accepted_groupings.include?(@grouping)
      return false
    end
    t = @grouping.token
    if t == nil
      raise I18n.t('automated_tests.missing_tokens')
    end
    if t.tokens > 0
      t.decrease_tokens
      true
    else
      false
    end
  end
end
copy_ant_files(assignment, repo_dest_dir) click to toggle source

Copy files needed for testing

# File app/helpers/automated_tests_helper.rb, line 219
def copy_ant_files(assignment, repo_dest_dir)
  # Check if the repository where you want to copy Ant files to exists
  unless File.exists?(repo_dest_dir)
    raise I18n.t('automated_tests.dir_not_exist', {:dir => repo_dest_dir})
  end

  # Create the src repository to put student's files
  assignment_dir = File.join(MarkusConfigurator.markus_config_automated_tests_repository, assignment.short_identifier)
  repo_assignment_dir = File.join(repo_dest_dir, assignment.short_identifier)
  FileUtils.mkdir(File.join(repo_assignment_dir, 'src'))

  # Move student's source files to the src repository
  pwd = FileUtils.pwd
  FileUtils.cd(repo_assignment_dir)
  FileUtils.mv(Dir.glob('*'), File.join(repo_assignment_dir, 'src'), :force => true )

  # You always have to come back to your former working directory if you want to avoid errors
  FileUtils.cd(pwd)

  # Copy the build.xml, build.properties Ant Files and api_helpers (only one is needed)
  if File.exists?(assignment_dir)
    FileUtils.cp(File.join(assignment_dir, 'build.xml'), repo_assignment_dir)
    FileUtils.cp(File.join(assignment_dir, 'build.properties'), repo_assignment_dir)
    FileUtils.cp('lib/tools/api_helper.rb', repo_assignment_dir)
    FileUtils.cp('lib/tools/api_helper.py', repo_assignment_dir)

    # Copy the test folder:
    # If the current user is a student, do not copy tests that are marked 'is_private' over
    # Otherwise, copy all tests over
    if @current_user.student?
      # Create the test folder
      assignment_test_dir = File.join(assignment_dir, 'test')
      repo_assignment_test_dir = File.join(repo_assignment_dir, 'test')
      FileUtils.mkdir(repo_assignment_test_dir)
      # Copy all non-private tests over
      assignment.test_files.find_all_by_filetype_and_is_private('test', 'false').each do |file|
        FileUtils.cp(File.join(assignment_test_dir, file.filename), repo_assignment_test_dir)
      end
    else
      if File.exists?(File.join(assignment_dir, 'test'))
        FileUtils.cp_r(File.join(assignment_dir, 'test'), File.join(repo_assignment_dir, 'test'))
      end
    end

    # Copy the lib folder
    if File.exists?(File.join(assignment_dir, 'lib'))
      FileUtils.cp_r(File.join(assignment_dir, 'lib'), repo_assignment_dir)
    end

    # Copy the parse folder
    if File.exists?(File.join(assignment_dir, 'parse'))
      FileUtils.cp_r(File.join(assignment_dir, 'parse'), repo_assignment_dir)
    end
  else
    raise I18n.t('automated_tests.dir_not_exist', {:dir => assignment_dir})
  end
end
create_ant_test_files(assignment) click to toggle source
# File app/helpers/automated_tests_helper.rb, line 58
def create_ant_test_files(assignment)
  # Create required ant test files - build.xml and build.properties
  if assignment && assignment.test_files.empty?
    @ant_build_file = TestFile.new
    @ant_build_file.assignment = assignment
    @ant_build_file.filetype = 'build.xml'
    @ant_build_file.filename = 'tempbuild.xml'        # temporary placeholder for now
    @ant_build_file.save(:validate => false)

    @ant_build_prop = TestFile.new
    @ant_build_prop.assignment = assignment
    @ant_build_prop.filetype = 'build.properties'
    @ant_build_prop.filename = 'tempbuild.properties' # temporary placeholder for now
    @ant_build_prop.save(:validate => false)

    # Setup Testing Framework repository
    test_dir = File.join(
                MarkusConfigurator.markus_config_automated_tests_repository,
                assignment.short_identifier)
    FileUtils.makedirs(test_dir)

    assignment.reload
  end
end
delete_test_repo(group, repo_dest_dir) click to toggle source

Delete test repository directory

# File app/helpers/automated_tests_helper.rb, line 210
def delete_test_repo(group, repo_dest_dir)
  repo_dir = File.join(MarkusConfigurator.markus_config_automated_tests_repository, group.repo_name)
  # Delete student's assignment repository if it exists
  if File.exists?(repo_dir)
    FileUtils.rm_rf(repo_dir)
  end
end
export_configuration_files(assignment, group, repo_dest_dir) click to toggle source

Export configuration files for testing

# File app/helpers/automated_tests_helper.rb, line 191
def export_configuration_files(assignment, group, repo_dest_dir)
  assignment_dir = File.join(MarkusConfigurator.markus_config_automated_tests_repository, assignment.short_identifier)
  repo_assignment_dir = File.join(repo_dest_dir, assignment.short_identifier)

  # Store the Api key of the grader or the admin in the api.txt file in the exported repository
  FileUtils.touch(File.join(assignment_dir, 'api.txt'))
  api_key_file = File.open(File.join(repo_assignment_dir, 'api.txt'), 'w')
  api_key_file.write(current_user.api_key)
  api_key_file.close

  # Create a file "export.properties" where group_name and assignment name are stored for Ant
  FileUtils.touch(File.join(assignment_dir, 'export.properties'))
  api_key_file = File.open(File.join(repo_assignment_dir, 'export.properties'), 'w')
  api_key_file.write('group_name = ' + group.group_name + "\n")
  api_key_file.write('assignment = ' + assignment.short_identifier + "\n")
  api_key_file.close
end
export_repository(group, repo_dest_dir) click to toggle source

Export group repository for testing

# File app/helpers/automated_tests_helper.rb, line 173
def export_repository(group, repo_dest_dir)
  # Create the test framework repository
  unless File.exists?(MarkusConfigurator.markus_config_automated_tests_repository)
    FileUtils.mkdir(MarkusConfigurator.markus_config_automated_tests_repository)
  end

  # Delete student's assignment repository if it already exists
  repo_dir = File.join(MarkusConfigurator.markus_config_automated_tests_repository, group.repo_name)
  if File.exists?(repo_dir)
    FileUtils.rm_rf(repo_dir)
  end

  return group.repo.export(repo_dest_dir)
  rescue Exception => e
    return "#{e.message}"
end
parse_test_output(repo_assignment_dir, assignment, logfile, data) click to toggle source

Send output to parser(s) if any

# File app/helpers/automated_tests_helper.rb, line 350
def parse_test_output(repo_assignment_dir, assignment, logfile, data)
  # Store test output
  output = data

  # If any test parsers exist, execute Ant's 'parse' target
  if assignment.test_files.find_by_filetype('parse')
    # Go to the directory where the Ant program must be run
    pwd = FileUtils.pwd
    FileUtils.cd(repo_assignment_dir)

    # Run Ant to parse test output
    system ("ant parse -logger org.apache.tools.ant.DefaultLogger -logfile #{logfile} -Doutput=#{data}")

    # Change back to the Rails Working directory
    FileUtils.cd(pwd)

    # Read in test output logged in logfile
    file = File.open(File.join(repo_assignment_dir, logfile), 'r')
    output = String.new
    file.each_line do |line|
      output += line
    end
    file.close
  end

  # Return parsed (or unparsed) test output
  output
end
process_test_form(assignment, params) click to toggle source

Process Testing Framework form

  • Process new and updated test files (additional validation to be done at the model level)

# File app/helpers/automated_tests_helper.rb, line 85
def process_test_form(assignment, params)

  # Hash for storing new and updated test files
  updated_files = {}

  # Retrieve all test file entries
  testfiles = params[:assignment][:test_files_attributes]

  # First check for duplicate filenames:
  filename_array = []
  testfiles.values.each do |tfile|
    if tfile['filename'].respond_to?(:original_filename)
      fname = tfile['filename'].original_filename
      # If this is a duplicate filename, raise error and return
      if filename_array.include?(fname)
        raise I18n.t('automated_tests.duplicate_filename') + fname
      else
        filename_array << fname
      end
    end
  end

  # Filter out files that need to be created and updated:
  testfiles.each_key do |key|

    tfile = testfiles[key]

    # Check to see if this is an update or a new file:
    # - If 'id' exists, this is an update
    # - If 'id' does not exist, this is a new test file
    tf_id = tfile['id']

    # If only the 'id' exists in the hash, other attributes were not updated so we skip this entry.
    # Otherwise, this test file possibly requires an update
    if tf_id != nil && tfile.size > 1

      # Find existing test file to update
      @existing_testfile = TestFile.find_by_id(tf_id)
      if @existing_testfile
        # Store test file for any possible updating
        updated_files[key] = tfile
      end
    end

    # Test file needs to be created since record doesn't exist yet
    if tf_id.nil? && tfile['filename']
      updated_files[key] = tfile
    end
  end

  # Update test file attributes
  assignment.test_files_attributes = updated_files

  # Update assignment enable_test and tokens_per_day attributes
  assignment.enable_test = params[:assignment][:enable_test]
  num_tokens = params[:assignment][:tokens_per_day]
  if num_tokens
    assignment.tokens_per_day = num_tokens
  end

  assignment
end
run_ant_file(result, assignment, repo_dest_dir) click to toggle source

Execute Ant which will run the tests against the students’ code

# File app/helpers/automated_tests_helper.rb, line 278
def run_ant_file(result, assignment, repo_dest_dir)
  # Check if the repository where you want to copy Ant files to exists
  unless File.exists?(repo_dest_dir)
    raise I18n.t('automated_tests.dir_not_exist', {:dir => repo_dest_dir})
  end

  # Go to the directory where the Ant program must be run
  repo_assignment_dir = File.join(repo_dest_dir, assignment.short_identifier)
  pwd = FileUtils.pwd
  FileUtils.cd(repo_assignment_dir)

  # Execute Ant and log output in a temp logfile
  logfile = 'build_log.xml'
  system ("ant -logger org.apache.tools.ant.DefaultLogger -logfile #{logfile}")

  # Change back to the Rails Working directory
  FileUtils.cd(pwd)

  # File to store build details
  filename = I18n.l(Time.zone.now, :format => :ant_date) + '.log'
  # Status of Ant build
  status = ''

  # Handle output depending on if the system command:
  # - executed successfully (ie. Ant returns a BUILD SUCCESSFUL exit(0))
  # - failed (ie. Ant returns a BUILD FAILED exit(1) possibly due to a compilation issue) or
  # - errored out for an unknown reason (ie. Ant returns exit != 0 or 1)
  if $?.exitstatus == 0
    # Build ran succesfully
    status = 'success'
  elsif $?.exitstatus == 1
    # Build failed
    status = 'failed'

    # Go back to the directory where the Ant program must be run
    pwd = FileUtils.pwd
    FileUtils.cd(repo_assignment_dir)

    # Re-run in verbose mode and log issues for diagnosing purposes
    system ("ant -logger org.apache.tools.ant.XmlLogger -logfile #{logfile} -verbose")

    # Change back to the Rails Working directory
    FileUtils.cd(pwd)
  else
    # Otherwise, some other unknown error with Ant has occurred so we simply log
    # the output for problem diagnosing purposes.
    status = 'error'
  end

  # Read in test output logged in build_log.xml
  file = File.open(File.join(repo_assignment_dir, logfile), 'r')
  data = String.new
  file.each_line do |line|
    data += line
  end
  file.close

  # If the build was successful, send output to parser(s)
  if $?.exitstatus == 0
    data = parse_test_output(repo_assignment_dir, assignment, logfile, data)
  end

  # Create TestResult object
  # (Build failures and errors will be logged and stored as well for diagnostic purposes)
  TestResult.create(:filename => filename,
    :file_content => data,
    :submission_id => result.submission.id,
    :status => status,
    :user_id => @current_user.id)
end