diff --git a/Gemfile b/Gemfile --- a/Gemfile +++ b/Gemfile @@ -1,15 +1,20 @@ source 'https://rubygems.org' +#rails gem 'rails', '~>4.2.0' gem 'activerecord-session_store' -gem 'select2-rails' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' +#---------------- database --------------------- +#the database gem 'mysql2' +#for testing gem 'sqlite3' +#for dumping database into yaml +gem 'yaml_db' # Gems used only for assets and not required # in production environments by default. @@ -21,7 +26,8 @@ gem 'uglifier' - +gem 'haml' +gem 'haml-rails' # gem 'prototype-rails' # To use ActiveModel has_secure_password @@ -63,16 +69,20 @@ gem 'momentjs-rails' gem 'rails_bootstrap_sortable' +#----------- user interface ----------------- +#select 2 +gem 'select2-rails' #ace editor gem 'ace-rails-ap' +#paginator +gem 'will_paginate', '~> 3.0.7' -gem 'haml' -gem 'haml-rails' gem 'mail' gem 'rdiscount' -gem 'test-unit' -gem 'will_paginate', '~> 3.0.7' gem 'dynamic_form' gem 'in_place_editing' gem 'verification', :git => 'https://github.com/sikachu/verification.git' + +#---------------- testiing ----------------------- +gem 'minitest-reporters' diff --git a/Gemfile.lock b/Gemfile.lock --- a/Gemfile.lock +++ b/Gemfile.lock @@ -51,6 +51,7 @@ minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + ansi (1.5.0) arel (6.0.4) autoprefixer-rails (6.6.0) execjs @@ -111,13 +112,17 @@ mime-types-data (3.2016.0521) mini_portile2 (2.1.0) minitest (5.10.1) + minitest-reporters (1.1.13) + ansi + builder + minitest (>= 5.0) + ruby-progressbar momentjs-rails (2.15.1) railties (>= 3.1) multi_json (1.12.1) mysql2 (0.4.5) nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) - power_assert (0.4.1) rack (1.6.5) rack-test (0.6.3) rack (>= 1.0) @@ -150,6 +155,7 @@ rake (12.0.0) rdiscount (2.2.0.1) rouge (2.0.7) + ruby-progressbar (1.8.1) ruby_parser (3.8.3) sexp_processor (~> 4.1) sass (3.4.23) @@ -170,8 +176,6 @@ activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.3.12) - test-unit (3.2.3) - power_assert thor (0.19.4) thread_safe (0.3.5) tilt (2.0.5) @@ -180,6 +184,9 @@ uglifier (3.0.4) execjs (>= 0.3.0, < 3) will_paginate (3.0.12) + yaml_db (0.4.2) + rails (>= 3.0, < 5.1) + rake (>= 0.8.7) PLATFORMS ruby @@ -203,6 +210,7 @@ jquery-timepicker-addon-rails jquery-ui-rails mail + minitest-reporters momentjs-rails mysql2 rails (~> 4.2.0) @@ -212,10 +220,10 @@ sass-rails select2-rails sqlite3 - test-unit uglifier verification! will_paginate (~> 3.0.7) + yaml_db BUNDLED WITH 1.13.6 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,7 @@ class ApplicationController < ActionController::Base protect_from_forgery - before_filter :current_user + before_filter :current_user SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode' MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login' @@ -37,6 +37,15 @@ end end + def testcase_authorization + #admin always has privileged + if @current_user.admin? + return true + end + + unauthorized_redirect if GraderConfiguration["right.view_testcase"] + end + protected def authenticate diff --git a/app/controllers/problems_controller.rb b/app/controllers/problems_controller.rb --- a/app/controllers/problems_controller.rb +++ b/app/controllers/problems_controller.rb @@ -1,6 +1,7 @@ class ProblemsController < ApplicationController - before_filter :authenticate, :authorization + before_action :authenticate, :authorization + before_action :testcase_authorization, only: [:show_testcase] in_place_edit_for :problem, :name in_place_edit_for :problem, :full_name @@ -221,6 +222,10 @@ redirect_to :action => 'manage' end + def show_testcase + @problem = Problem.includes(:testcases).find(params[:id]) + end + ################################## protected diff --git a/app/controllers/testcases_controller.rb b/app/controllers/testcases_controller.rb new file mode 100644 --- /dev/null +++ b/app/controllers/testcases_controller.rb @@ -0,0 +1,24 @@ +class TestcasesController < ApplicationController + before_action :set_testcase, only: [:download_input,:download_sol] + before_action :testcase_authorization + + def download_input + send_data @testcase.input, type: 'text/plain', filename: "#{@testcase.problem.name}.#{@testcase.num}.in" + end + + def download_sol + send_data @testcase.sol, type: 'text/plain', filename: "#{@testcase.problem.name}.#{@testcase.num}.sol" + end + + + private + # Use callbacks to share common setup or constraints between actions. + def set_testcase + @testcase = Testcase.find(params[:id]) + end + + # Only allow a trusted parameter "white list" through. + def testcase_params + params[:testcase] + end +end diff --git a/app/helpers/testcases_helper.rb b/app/helpers/testcases_helper.rb new file mode 100644 --- /dev/null +++ b/app/helpers/testcases_helper.rb @@ -0,0 +1,2 @@ +module TestcasesHelper +end diff --git a/app/models/grader_configuration.rb b/app/models/grader_configuration.rb --- a/app/models/grader_configuration.rb +++ b/app/models/grader_configuration.rb @@ -10,6 +10,8 @@ MULTICONTESTS_KEY = 'system.multicontests' CONTEST_TIME_LIMIT_KEY = 'contest.time_limit' MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login' + VIEW_TESTCASE = 'right.view_testcase' + SINGLE_USER_KEY = 'system.single_user_mode' cattr_accessor :config_cache cattr_accessor :task_grading_info_cache @@ -70,6 +72,10 @@ return (get(SYSTEM_MODE_CONF_KEY)=='analysis') end + def self.show_testcase + return get(VIEW_TESTCASE) + end + def self.allow_test_request(user) mode = get(SYSTEM_MODE_CONF_KEY) early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY) diff --git a/app/views/main/_login_box.html.haml b/app/views/main/_login_box.html.haml --- a/app/views/main/_login_box.html.haml +++ b/app/views/main/_login_box.html.haml @@ -12,7 +12,7 @@ %hr/ %div{ :style => "border: solid 1px gray; padding: 4px; background: #eeeeff;"} - = form_tag :controller => 'login', :action => 'login' do + = form_tag login_login_path do %table %tr %td{:align => "right"} diff --git a/app/views/main/_submission_short.html.haml b/app/views/main/_submission_short.html.haml --- a/app/views/main/_submission_short.html.haml +++ b/app/views/main/_submission_short.html.haml @@ -24,4 +24,6 @@ = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'} = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info' = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info' + - if GraderConfiguration.show_testcase + = link_to "testcases", show_testcase_problem_path(problem_id), class: 'btn btn-xs btn-info' diff --git a/app/views/main/list.html.haml b/app/views/main/list.html.haml --- a/app/views/main/list.html.haml +++ b/app/views/main/list.html.haml @@ -50,3 +50,14 @@ = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';" Announcement.registerRefreshEventTimer(); +.modal.fade#compiler{tabindex: -1,role: 'dialog'} + .modal-dialog.modal-lg{role:'document'} + .modal-content + .modal-header + %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}} + %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} × + %h4 Compiler message + .modal-body + %pre#compiler_msg + .modal-footer + %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close diff --git a/app/views/problems/show_testcase.html.haml b/app/views/problems/show_testcase.html.haml new file mode 100644 --- /dev/null +++ b/app/views/problems/show_testcase.html.haml @@ -0,0 +1,25 @@ +%h1 Test cases +%h2= @problem.long_name + +/navbar +%ul.nav.nav-pills{role: :tablist} + - @problem.testcases.each.with_index do |tc,id| + %li{role: :presentation, class: ('active' if id == 0)} + %a{href:"#tc#{tc.id}", role: 'tab', data: {toggle: 'tab'}}= tc.num + +/actual data +.tab-content + - @problem.testcases.each.with_index do |tc,id| + .tab-pane{id: "tc#{tc.id}",class: ('active' if id == 0)} + .row + .col-md-6 + %h3 Input + = link_to "Download",download_input_problem_testcase_path(@problem,tc),class: 'btn btn-info btn-sm' + .col-md-6 + %h3 Output + = link_to "Download",download_sol_problem_testcase_path(@problem,tc),class: 'btn btn-info btn-sm' + .row + .col-md-6 + %textarea{ rows: 25,readonly: true,style: "width:100%;resize=none;overflow-y: scroll;"}= tc.input + .col-md-6 + %textarea{ rows: 25,readonly: true,style: "width:100%;resize=none;overflow-y: scroll;"}= tc.sol diff --git a/app/views/submissions/compiler_msg.js.haml b/app/views/submissions/compiler_msg.js.haml --- a/app/views/submissions/compiler_msg.js.haml +++ b/app/views/submissions/compiler_msg.js.haml @@ -1,3 +1,4 @@ -:javascript - $("#compiler_msg").html("#{j @submissionhcompiler_msg}") - +:plain + $("#compiler_msg").html("#{j @submission.compiler_message}"); + $("#compiler").modal(); + diff --git a/app/views/submissions/get_latest_submission_status.js.haml b/app/views/submissions/get_latest_submission_status.js.haml --- a/app/views/submissions/get_latest_submission_status.js.haml +++ b/app/views/submissions/get_latest_submission_status.js.haml @@ -1,2 +1,2 @@ -:javascript +:plain $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}") diff --git a/config/routes.rb b/config/routes.rb --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,9 @@ root :to => 'main#login' + #logins + get 'login/login', to: 'login#login' + resources :contests resources :sites @@ -18,6 +21,7 @@ get 'toggle' get 'toggle_test' get 'stat' + get 'show_testcase' end collection do get 'turn_all_off' @@ -25,6 +29,13 @@ get 'import' get 'manage' end + + resources :testcases, only: [] do + member do + get 'download_input' + get 'download_sol' + end + end end resources :grader_configuration, controller: 'configurations' diff --git a/db/schema.rb b/db/schema.rb --- a/db/schema.rb +++ b/db/schema.rb @@ -14,200 +14,200 @@ ActiveRecord::Schema.define(version: 20161031063337) do create_table "announcements", force: :cascade do |t| - t.string "author" - t.text "body" + t.string "author", limit: 255 + t.text "body", limit: 65535 t.boolean "published" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "frontpage", default: false - t.boolean "contest_only", default: false - t.string "title" - t.string "notes" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "frontpage", default: false + t.boolean "contest_only", default: false + t.string "title", limit: 255 + t.string "notes", limit: 255 end create_table "contests", force: :cascade do |t| - t.string "title" + t.string "title", limit: 255 t.boolean "enabled" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name", limit: 255 end create_table "contests_problems", id: false, force: :cascade do |t| - t.integer "contest_id" - t.integer "problem_id" + t.integer "contest_id", limit: 4 + t.integer "problem_id", limit: 4 end create_table "contests_users", id: false, force: :cascade do |t| - t.integer "contest_id" - t.integer "user_id" + t.integer "contest_id", limit: 4 + t.integer "user_id", limit: 4 end create_table "countries", force: :cascade do |t| - t.string "name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "name", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "descriptions", force: :cascade do |t| - t.text "body" + t.text "body", limit: 65535 t.boolean "markdowned" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "grader_configurations", force: :cascade do |t| - t.string "key" - t.string "value_type" - t.string "value" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.text "description" + t.string "key", limit: 255 + t.string "value_type", limit: 255 + t.string "value", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "description", limit: 65535 end create_table "grader_processes", force: :cascade do |t| - t.string "host" - t.integer "pid" - t.string "mode" + t.string "host", limit: 255 + t.integer "pid", limit: 4 + t.string "mode", limit: 255 t.boolean "active" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "task_id" - t.string "task_type" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "task_id", limit: 4 + t.string "task_type", limit: 255 t.boolean "terminated" end - add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid" + add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid", using: :btree create_table "heart_beats", force: :cascade do |t| - t.integer "user_id" - t.string "ip_address" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "status" + t.integer "user_id", limit: 4 + t.string "ip_address", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "status", limit: 255 end - add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at" + add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at", using: :btree create_table "languages", force: :cascade do |t| t.string "name", limit: 10 - t.string "pretty_name" + t.string "pretty_name", limit: 255 t.string "ext", limit: 10 - t.string "common_ext" + t.string "common_ext", limit: 255 end create_table "logins", force: :cascade do |t| - t.integer "user_id" - t.string "ip_address" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "user_id", limit: 4 + t.string "ip_address", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "messages", force: :cascade do |t| - t.integer "sender_id" - t.integer "receiver_id" - t.integer "replying_message_id" - t.text "body" + t.integer "sender_id", limit: 4 + t.integer "receiver_id", limit: 4 + t.integer "replying_message_id", limit: 4 + t.text "body", limit: 65535 t.boolean "replied" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "problems", force: :cascade do |t| t.string "name", limit: 30 - t.string "full_name" - t.integer "full_score" + t.string "full_name", limit: 255 + t.integer "full_score", limit: 4 t.date "date_added" t.boolean "available" - t.string "url" - t.integer "description_id" + t.string "url", limit: 255 + t.integer "description_id", limit: 4 t.boolean "test_allowed" t.boolean "output_only" - t.string "description_filename" + t.string "description_filename", limit: 255 end create_table "rights", force: :cascade do |t| - t.string "name" - t.string "controller" - t.string "action" + t.string "name", limit: 255 + t.string "controller", limit: 255 + t.string "action", limit: 255 end create_table "rights_roles", id: false, force: :cascade do |t| - t.integer "right_id" - t.integer "role_id" + t.integer "right_id", limit: 4 + t.integer "role_id", limit: 4 end - add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id" + add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id", using: :btree create_table "roles", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 end create_table "roles_users", id: false, force: :cascade do |t| - t.integer "role_id" - t.integer "user_id" + t.integer "role_id", limit: 4 + t.integer "user_id", limit: 4 end - add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id" + add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id", using: :btree create_table "sessions", force: :cascade do |t| - t.string "session_id" - t.text "data" + t.string "session_id", limit: 255 + t.text "data", limit: 65535 t.datetime "updated_at" end - add_index "sessions", ["session_id"], name: "index_sessions_on_session_id" - add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at" + add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree + add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree create_table "sites", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 t.boolean "started" t.datetime "start_time" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "country_id" - t.string "password" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "country_id", limit: 4 + t.string "password", limit: 255 end create_table "submission_view_logs", force: :cascade do |t| - t.integer "user_id" - t.integer "submission_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "user_id", limit: 4 + t.integer "submission_id", limit: 4 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "submissions", force: :cascade do |t| - t.integer "user_id" - t.integer "problem_id" - t.integer "language_id" - t.text "source" - t.binary "binary" + t.integer "user_id", limit: 4 + t.integer "problem_id", limit: 4 + t.integer "language_id", limit: 4 + t.text "source", limit: 65535 + t.binary "binary", limit: 65535 t.datetime "submitted_at" t.datetime "compiled_at" - t.text "compiler_message" + t.text "compiler_message", limit: 65535 t.datetime "graded_at" - t.integer "points" - t.text "grader_comment" - t.integer "number" - t.string "source_filename" - t.float "max_runtime" - t.integer "peak_memory" - t.integer "effective_code_length" - t.string "ip_address" + t.integer "points", limit: 4 + t.text "grader_comment", limit: 65535 + t.integer "number", limit: 4 + t.string "source_filename", limit: 255 + t.float "max_runtime", limit: 24 + t.integer "peak_memory", limit: 4 + t.integer "effective_code_length", limit: 4 + t.string "ip_address", limit: 255 end - add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true - add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id" + add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true, using: :btree + add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id", using: :btree create_table "tasks", force: :cascade do |t| - t.integer "submission_id" + t.integer "submission_id", limit: 4 t.datetime "created_at" - t.integer "status" + t.integer "status", limit: 4 t.datetime "updated_at" end create_table "test_pairs", force: :cascade do |t| - t.integer "problem_id" + t.integer "problem_id", limit: 4 t.text "input", limit: 16777215 t.text "solution", limit: 16777215 t.datetime "created_at", null: false @@ -215,66 +215,66 @@ end create_table "test_requests", force: :cascade do |t| - t.integer "user_id" - t.integer "problem_id" - t.integer "submission_id" - t.string "input_file_name" - t.string "output_file_name" - t.string "running_stat" - t.integer "status" - t.datetime "updated_at", null: false + t.integer "user_id", limit: 4 + t.integer "problem_id", limit: 4 + t.integer "submission_id", limit: 4 + t.string "input_file_name", limit: 255 + t.string "output_file_name", limit: 255 + t.string "running_stat", limit: 255 + t.integer "status", limit: 4 + t.datetime "updated_at", null: false t.datetime "submitted_at" t.datetime "compiled_at" - t.text "compiler_message" + t.text "compiler_message", limit: 65535 t.datetime "graded_at" - t.string "grader_comment" - t.datetime "created_at", null: false - t.float "running_time" - t.string "exit_status" - t.integer "memory_usage" + t.string "grader_comment", limit: 255 + t.datetime "created_at", null: false + t.float "running_time", limit: 24 + t.string "exit_status", limit: 255 + t.integer "memory_usage", limit: 4 end - add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id" + add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id", using: :btree create_table "testcases", force: :cascade do |t| - t.integer "problem_id" - t.integer "num" - t.integer "group" - t.integer "score" - t.text "input" - t.text "sol" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "problem_id", limit: 4 + t.integer "num", limit: 4 + t.integer "group", limit: 4 + t.integer "score", limit: 4 + t.text "input", limit: 65535 + t.text "sol", limit: 65535 + t.datetime "created_at" + t.datetime "updated_at" end - add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id" + add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id", using: :btree create_table "user_contest_stats", force: :cascade do |t| - t.integer "user_id" + t.integer "user_id", limit: 4 t.datetime "started_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.boolean "forced_logout" end create_table "users", force: :cascade do |t| t.string "login", limit: 50 - t.string "full_name" - t.string "hashed_password" + t.string "full_name", limit: 255 + t.string "hashed_password", limit: 255 t.string "salt", limit: 5 - t.string "alias" - t.string "email" - t.integer "site_id" - t.integer "country_id" - t.boolean "activated", default: false + t.string "alias", limit: 255 + t.string "email", limit: 255 + t.integer "site_id", limit: 4 + t.integer "country_id", limit: 4 + t.boolean "activated", default: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "enabled", default: true - t.string "remark" - t.string "last_ip" - t.string "section" + t.boolean "enabled", default: true + t.string "remark", limit: 255 + t.string "last_ip", limit: 255 + t.string "section", limit: 255 end - add_index "users", ["login"], name: "index_users_on_login", unique: true + add_index "users", ["login"], name: "index_users_on_login", unique: true, using: :btree end diff --git a/db/seeds.rb b/db/seeds.rb --- a/db/seeds.rb +++ b/db/seeds.rb @@ -89,6 +89,12 @@ :description => 'Heart beat response text' }, + { + :key => 'right.view_testcase', + :value_type => 'boolean', + :default_value => 'false', + :description => 'When true, any user can view/download test data' + }, # If Configuration['system.online_registration'] is true, the # system allows online registration, and will use these # information for sending confirmation emails. diff --git a/lib/grader_script.rb b/lib/grader_script.rb --- a/lib/grader_script.rb +++ b/lib/grader_script.rb @@ -49,10 +49,25 @@ output = `#{cmd}` Dir.chdir(cur_dir) - + return "import CMD: #{cmd}\n" + output end return '' end + def self.call_import_testcase(problem_name) + if GraderScript.grader_control_enabled? + cur_dir = `pwd`.chomp + Dir.chdir(GRADER_ROOT_DIR) + + script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase") + cmd = "#{script_name} #{problem_name}" + + output = `#{cmd}` + + Dir.chdir(cur_dir) + return "Testcase import result:\n" + output + end + end + end diff --git a/lib/tasks/fixtures.rake b/lib/tasks/fixtures.rake new file mode 100644 --- /dev/null +++ b/lib/tasks/fixtures.rake @@ -0,0 +1,41 @@ +# Original from http://snippets.dzone.com/posts/show/4468 by MichaelBoutros +# +# Optimized version which uses to_yaml for content creation and checks +# that models are ActiveRecord::Base models before trying to fetch +# them from database. +namespace :db do + namespace :fixtures do + desc 'Dumps all models into fixtures.' + task :dump => :environment do + puts "rails root = #{Rails.root}" + models = Dir.glob(Rails.root.to_s + '/app/models/**.rb').map do |s| + Pathname.new(s).basename.to_s.gsub(/\.rb$/,'').camelize + end + + puts "Found models: " + models.join(', ') + + models.each do |m| + model = m.constantize + next unless model.ancestors.include?(ActiveRecord::Base) + + puts "Dumping model: " + m + entries = model.all.order(id: :asc) + + increment = 1 + + model_file = Rails.root.to_s + '/test/fixtures2/' + m.underscore.pluralize + '.yml' + File.open(model_file, 'w') do |f| + entries.each do |a| + attrs = a.attributes + attrs.delete_if{|k,v| v.blank?} + + output = {m + '_' + increment.to_s => attrs} + f << output.to_yaml.gsub(/^--- \n/,'') + "\n" + + increment += 1 + end + end + end + end + end +end diff --git a/lib/testdata_importer.rb b/lib/testdata_importer.rb --- a/lib/testdata_importer.rb +++ b/lib/testdata_importer.rb @@ -38,6 +38,9 @@ @log_msg << import_problem_pdf(dirname) @log_msg << import_full_score(dirname) + #import test data + @log_msg << GraderScript.call_import_testcase(@problem.name) + return true end diff --git a/test/controllers/testcases_controller_test.rb b/test/controllers/testcases_controller_test.rb new file mode 100644 --- /dev/null +++ b/test/controllers/testcases_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class TestcasesControllerTest < ActionController::TestCase + setup do + @testcase = testcases(:one) + end +end diff --git a/test/fixtures/grader_configurations.yml b/test/fixtures/grader_configurations.yml new file mode 100644 --- /dev/null +++ b/test/fixtures/grader_configurations.yml @@ -0,0 +1,144 @@ +GraderConfiguration_1: + key: system.single_user_mode + value_type: boolean + value: 'false' + description: Only admins can log in to the system when running under single user mode. + +GraderConfiguration_2: + key: ui.front.title + value_type: string + value: Grader + +GraderConfiguration_3: + key: ui.front.welcome_message + value_type: string + value: Welcome! + +GraderConfiguration_4: + key: ui.show_score + value_type: boolean + value: 'true' + +GraderConfiguration_5: + key: contest.time_limit + value_type: string + value: unlimited + description: Time limit in format hh:mm, or "unlimited" for contests with no time + limits. This config is CACHED. Restart the server before the change can take + effect. + +GraderConfiguration_6: + key: system.mode + value_type: string + value: standard + description: Current modes are "standard", "contest", "indv-contest", and "analysis". + + +GraderConfiguration_7: + key: contest.name + value_type: string + value: Grader + description: This name will be shown on the user header bar. + + +GraderConfiguration_8: + key: contest.multisites + value_type: boolean + value: 'false' + description: If the server is in contest mode and this option is true, on the log + in of the admin a menu for site selections is shown. + + +GraderConfiguration_9: + key: right.user_hall_of_fame + value_type: boolean + value: 'false' + description: If true, any user can access hall of fame page. + + +GraderConfiguration_10: + key: right.multiple_ip_login + value_type: boolean + value: 'true' + description: When change from true to false, a user can login from the first IP + they logged into afterward. + + +GraderConfiguration_11: + key: right.user_view_submission + value_type: boolean + value: 'false' + description: If true, any user can view submissions of every one. + + +GraderConfiguration_12: + key: right.bypass_agreement + value_type: boolean + value: 'true' + description: When false, a user must accept usage agreement before login + + +GraderConfiguration_13: + key: right.heartbeat_response + value_type: string + value: OK + description: Heart beat response text + + +GraderConfiguration_14: + key: right.view_testcase + value_type: boolean + value: 'false' + description: When true, any user can view/download test data + + +GraderConfiguration_15: + key: system.online_registration.smtp + value_type: string + value: smtp.somehost.com + + +GraderConfiguration_16: + key: system.online_registration.from + value_type: string + value: your.email@address + + +GraderConfiguration_17: + key: system.admin_email + value_type: string + value: admin@admin.email + + +GraderConfiguration_18: + key: system.user_setting_enabled + value_type: boolean + value: 'true' + description: If this option is true, users can change their settings + + +GraderConfiguration_19: + key: contest.test_request.early_timeout + value_type: boolean + value: 'false' + + +GraderConfiguration_20: + key: system.multicontests + value_type: boolean + value: 'false' + + +GraderConfiguration_21: + key: contest.confirm_indv_contest_start + value_type: boolean + value: 'false' + + +GraderConfiguration_22: + key: contest.default_contest_name + value_type: string + value: none + description: New user will be assigned to this contest automatically, if it exists. Set + to 'none' if there is no default contest. + diff --git a/test/fixtures/languages.yml b/test/fixtures/languages.yml --- a/test/fixtures/languages.yml +++ b/test/fixtures/languages.yml @@ -1,5 +1,36 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html -one: - id: 1 -two: - id: 2 +Language_1: + name: c + pretty_name: C + ext: c + common_ext: c + +Language_2: + name: cpp + pretty_name: C++ + ext: cpp + common_ext: cpp,cc + +Language_3: + name: pas + pretty_name: Pascal + ext: pas + common_ext: pas + +Language_4: + name: ruby + pretty_name: Ruby + ext: rb + common_ext: rb + +Language_5: + name: python + pretty_name: Python + ext: py + common_ext: py + +Language_6: + name: java + pretty_name: Java + ext: java + common_ext: java + diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -13,10 +13,10 @@ salt: <%= salt %> activated: true -mary: - login: mary - full_name: mary - hashed_password: <%= User.encrypt("goodbye",salt) %> +admin: + login: admin + full_name: admin + hashed_password: <%= User.encrypt("admin",salt) %> salt: <%= salt %> roles: admin activated: true diff --git a/test/integration/login_test.rb b/test/integration/login_test.rb --- a/test/integration/login_test.rb +++ b/test/integration/login_test.rb @@ -5,9 +5,36 @@ # assert true # end - test "login with valid information" do + test "login with invalid information" do + get root_path + assert_response :success + post login_login_path, login: "root", password: "hahaha" + assert_redirected_to root_path + end + + test "normal user login" do get root_path assert_response :success + post login_login_path, {login: "john", password: "hello" } + assert_redirected_to main_list_path + end + test "normal user login in single_user mode" do + GraderConfiguration.find_by(key: GraderConfiguration::SINGLE_USER_KEY).update_attributes(value: 'true') + GraderConfiguration.reload + get root_path + assert_response :success + post login_login_path, {login: "john", password: "hello" } + follow_redirect! + assert_redirected_to root_path + end + + test "root login in in single_user mode" do + GraderConfiguration.find_by(key: GraderConfiguration::SINGLE_USER_KEY).update_attributes(value: 'true') + GraderConfiguration.reload + get root_path + assert_response :success + post login_login_path, {login: "admin", password: "admin" } + assert_redirected_to main_list_path end end diff --git a/test/test_helper.rb b/test/test_helper.rb --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,6 +2,10 @@ require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' +#reporter +require "minitest/reporters" +Minitest::Reporters.use! + class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. #