loadaboy 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ begin
18
18
  gem.email = "jean-louis@icehouse.se"
19
19
  gem.homepage = "http://github.com/Jell/loadaboy"
20
20
  gem.authors = ["Jell"]
21
+ gem.files.exclude 'ebin/test_*', 'elib/minitest.erl'
21
22
  gem.files.include(["ext"])
22
23
  gem.extensions << 'ext/extconf.rb'
23
24
  gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
@@ -36,6 +37,10 @@ Rake::TestTask.new(:test) do |test|
36
37
  test.verbose = true
37
38
  end
38
39
 
40
+ task :etest do
41
+ sh "cd ebin && ./test_load_test.erl"
42
+ end
43
+
39
44
  begin
40
45
  require 'rcov/rcovtask'
41
46
  Rcov::RcovTask.new do |test|
@@ -51,7 +56,7 @@ end
51
56
 
52
57
  task :test => :check_dependencies
53
58
 
54
- task :default => :test
59
+ task :default => [:test, :etest]
55
60
 
56
61
  require 'rake/rdoctask'
57
62
  Rake::RDocTask.new do |rdoc|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
data/ebin/run_loadaboy CHANGED
@@ -2,22 +2,22 @@
2
2
  %% -*- erlang -*-
3
3
 
4
4
  main(String) ->
5
- try
6
- error_logger:tty(false),
7
- [Domain, WorkerCount, RequestCount] = String,
8
- LoadTest = load_test:new(Domain, list_to_integer(WorkerCount), list_to_integer(RequestCount)),
9
- LoadTest:start(),
10
- LoadTest:prepare(),
11
- LoadTest:run()
12
- catch
13
- _:_ ->
14
- usage()
15
- end;
5
+ try
6
+ error_logger:tty(false),
7
+ [Domain, WorkerCount, RequestCount | Services ] = String,
8
+ LoadTest = load_test:new(Domain, list_to_integer(WorkerCount), list_to_integer(RequestCount), Services),
9
+ LoadTest:start(),
10
+ LoadTest:prepare(),
11
+ LoadTest:run()
12
+ catch
13
+ _:_ ->
14
+ usage()
15
+ end;
16
16
 
17
17
  main(_) ->
18
- io:format("no header\n"),
19
- usage().
18
+ io:format("no header\n"),
19
+ usage().
20
20
 
21
21
  usage() ->
22
- io:format("usage: loadaboy domain worker-count request-count\n"),
23
- halt(1).
22
+ io:format("usage: loadaboy domain worker-count request-count\n"),
23
+ halt(1).
data/elib/load_test.erl CHANGED
@@ -1,90 +1,92 @@
1
- -module(load_test, [Domain, WorkerCount, RequestCount]).
1
+ -module(load_test, [Domain, WorkerCount, RequestCount, ServiceList]).
2
2
  %-export([start/0, stop/0, run/0, generate_services/1, get_services/0, get_workers/1, get_jobs/2, get_urls/3, set_urls/4]).
3
3
  -compile(export_all).
4
4
  -record(load_entry, {key, urls}).
5
5
 
6
6
  start() ->
7
- inets:start(),
8
- mnesia:start().
7
+ inets:start(),
8
+ mnesia:start().
9
9
 
10
10
  stop() ->
11
- mnesia:stop().
11
+ inets:stop(),
12
+ mnesia:stop().
13
+
14
+ prepare_mnesia() ->
15
+ mnesia:delete_table(load_entry),
16
+ mnesia:create_table(load_entry, [{attributes, record_info(fields, load_entry)}]).
17
+
18
+ open_ruby_port() ->
19
+ Cmd = "ruby ../lib/erlang_interface.rb",
20
+ open_port({spawn, Cmd}, [{packet, 4}, nouse_stdio, exit_status, binary]).
12
21
 
13
22
  prepare() ->
14
- mnesia:delete_table(load_entry),
15
- mnesia:create_table(load_entry, [{attributes, record_info(fields, load_entry)}]),
16
- Cmd = "ruby ../lib/erlang_interface.rb",
17
- Port = open_port({spawn, Cmd}, [{packet, 4}, nouse_stdio, exit_status, binary]),
18
- Payload = term_to_binary([prepare, [WorkerCount, RequestCount]]),
19
- port_command(Port, Payload),
20
- receive
21
- {Port, {data, Data}} ->
22
- {result, Text} = binary_to_term(Data),
23
- [set_urls(test, WorkerName, tuple_to_list(Urls)) || {WorkerName, Urls}<- tuple_to_list(Text)]
24
- end.
25
-
23
+ prepare_mnesia(),
24
+ Port = open_ruby_port(),
25
+ Payload = case ServiceList of
26
+ [Service] ->
27
+ term_to_binary([prepare, [WorkerCount, RequestCount, list_to_binary(Service)]]);
28
+ _ ->
29
+ term_to_binary([prepare, [WorkerCount, RequestCount]])
30
+ end,
31
+ port_command(Port, Payload),
32
+ receive
33
+ {Port, {data, Data}} ->
34
+ {result, Text} = binary_to_term(Data),
35
+ [set_urls(test, WorkerName, tuple_to_list(Urls)) || {WorkerName, Urls}<- tuple_to_list(Text)]
36
+ end.
37
+
26
38
  run() ->
27
- {atomic, Services} = get_services(),
28
- generate_services(lists:usort(Services)),
29
- wait_for_workers(WorkerCount).
39
+ {atomic, Services} = get_services(),
40
+ generate_services(lists:usort(Services)),
41
+ wait_for_workers(WorkerCount).
30
42
 
31
- generate_services(Services) ->
32
- case Services of
33
- [] ->
34
- done;
35
- [Service | Remaining] ->
36
- {atomic, Workers} = get_workers(Service),
37
- generate_workers(Service, Workers),
38
- generate_services(Remaining)
39
- end.
43
+ generate_services([]) ->
44
+ done;
45
+ generate_services([Service | Remaining]) ->
46
+ {atomic, Workers} = get_workers(Service),
47
+ generate_workers(Service, Workers),
48
+ generate_services(Remaining).
40
49
 
41
- generate_workers(Service, Workers) ->
42
- case Workers of
43
- [] ->
44
- done;
45
- [WorkerName | Remaining] ->
46
- {atomic, [Urls]} = get_urls(Service, WorkerName),
47
- Worker = worker:new(self(), WorkerName, Domain, Urls),
48
- Worker:run(),
49
- generate_workers(Service, Remaining)
50
- end.
50
+ generate_workers(_Service, []) ->
51
+ done;
52
+ generate_workers(Service, [WorkerName | Remaining]) ->
53
+ {atomic, [Urls]} = get_urls(Service, WorkerName),
54
+ gen_server:start_link({local, WorkerName}, worker, [self(), WorkerName, Domain, Urls], []),
55
+ gen_server:cast(WorkerName, run),
56
+ generate_workers(Service, Remaining).
51
57
 
58
+ wait_for_workers(0) ->
59
+ done;
52
60
  wait_for_workers(RemainingWorkers) ->
53
- receive
54
- done ->
55
- case RemainingWorkers - 1 of
56
- 0 ->
57
- done;
58
- _ ->
59
- wait_for_workers(RemainingWorkers - 1)
60
- end
61
- end.
61
+ receive
62
+ done ->
63
+ wait_for_workers(RemainingWorkers - 1)
64
+ end.
62
65
 
63
66
  get_services() ->
64
- F = fun() ->
65
- LoadEntry = #load_entry{key = {'$1', '_'}, urls = '_'},
66
- mnesia:select(load_entry, [{LoadEntry, [], ['$1']}])
67
- end,
68
- mnesia:transaction(F).
67
+ F = fun() ->
68
+ LoadEntry = #load_entry{key = {'$1', '_'}, urls = '_'},
69
+ mnesia:select(load_entry, [{LoadEntry, [], ['$1']}])
70
+ end,
71
+ mnesia:transaction(F).
69
72
 
70
73
  get_workers(Service) ->
71
- F = fun() ->
72
- LoadEntry = #load_entry{key = {Service, '$1'}, urls = '_'},
73
- mnesia:select(load_entry, [{LoadEntry, [], ['$1']}])
74
- end,
75
- mnesia:transaction(F).
76
-
74
+ F = fun() ->
75
+ LoadEntry = #load_entry{key = {Service, '$1'}, urls = '_'},
76
+ mnesia:select(load_entry, [{LoadEntry, [], ['$1']}])
77
+ end,
78
+ mnesia:transaction(F).
77
79
 
78
80
  get_urls(Service, Worker) ->
79
- F = fun() ->
80
- LoadEntry = #load_entry{key = {Service, Worker}, urls = '$1'},
81
- mnesia:select(load_entry, [{LoadEntry, [], ['$1']}])
82
- end,
83
- mnesia:transaction(F).
81
+ F = fun() ->
82
+ LoadEntry = #load_entry{key = {Service, Worker}, urls = '$1'},
83
+ mnesia:select(load_entry, [{LoadEntry, [], ['$1']}])
84
+ end,
85
+ mnesia:transaction(F).
84
86
 
85
87
  set_urls(Service, Worker, Urls) ->
86
- LoadEntry = #load_entry{key = {Service, Worker}, urls = Urls},
87
- F = fun() ->
88
- mnesia:write(LoadEntry)
89
- end,
90
- mnesia:transaction(F).
88
+ LoadEntry = #load_entry{key = {Service, Worker}, urls = Urls},
89
+ F = fun() ->
90
+ mnesia:write(LoadEntry)
91
+ end,
92
+ mnesia:transaction(F).
data/elib/worker.erl CHANGED
@@ -1,51 +1,67 @@
1
- -module(worker, [Parent, ProfileName, Domain, Urls]).
2
- -compile(export_all).
3
-
4
- run() ->
5
- spawn(
6
- fun() ->
7
- inets:start(httpc, [{profile, ProfileName}]),
8
- fetch_urls(Urls)
9
- end
10
- ).
11
-
12
- fetch_urls(UrlList) ->
13
- case UrlList of
14
- [] ->
15
- Parent!done;
16
- [Url|Tail] ->
17
- {Name, Request} = Url,
18
- [Header, Time] = fetch_url(Domain ++ binary_to_list(Request)),
19
- io:format("~p ~p ~p~n", [Header, Name, Time]),
20
- fetch_urls(Tail)
21
- end.
22
-
23
- fetch_url(Url) ->
24
- try
25
- Tic = erlang:now(),
26
- Result = get_over_http(Url),
27
- Tac = erlang:now(),
28
- Time = time_to_ms(Tac) - time_to_ms(Tic),
29
- case Result of
30
- timeout ->
31
- [{"HTTP/1.1", 408, "Request Timeout"}, Time];
32
- {Header, _Params, _Body} ->
33
- [Header, Time]
34
- end
35
- catch
36
- _:_ ->
37
- [{"HTTP/1.1", 400, "Bad Request"}, 0]
38
- end.
1
+ -module(worker).
2
+ -export([code_change/3, handle_cast/2, handle_call/3, handle_info/2, init/1, terminate/2]).
3
+ -behavior(gen_server).
4
+
5
+ init([Parent, ProfileName, Domain, Urls]) ->
6
+ inets:start(httpc, [{profile, ProfileName}]),
7
+ {ok, [Parent, ProfileName, Domain, Urls]}.
8
+
9
+ handle_call(_Message, _From, [Parent, ProfileName, Domain, Urls]) ->
10
+ fetch_urls([ProfileName, Domain, Urls]),
11
+ {reply, done, [Parent, ProfileName, Domain, Urls]}.
12
+
13
+ handle_cast(run, [Parent, ProfileName, Domain, Urls]) ->
14
+ fetch_urls([ProfileName, Domain, Urls]),
15
+ Parent!done,
16
+ {noreply, [Parent, ProfileName, Domain, Urls]}.
17
+
18
+ handle_info(Msg, State) ->
19
+ io:format("Unexpected message: ~p~n",[Msg]),
20
+ {noreply, State}.
21
+
22
+ code_change(_OldVsn, State, _Extra) ->
23
+ %% No change planned. The function is there for the behaviour,
24
+ %% but will not be used. Only a version on the next
25
+ {ok, State}.
26
+
27
+ terminate(_, _) -> ok.
28
+
29
+ fetch_urls([_ProfileName, _Domain, []]) ->
30
+ done;
31
+ fetch_urls([ProfileName, Domain, [{Name, Url}|Tail]]) ->
32
+ [Header, Time] = fetch_url(Domain ++ binary_to_list(Url), ProfileName),
33
+ forecast_result(Header, Name, Time, Url),
34
+ fetch_urls([ProfileName, Domain, Tail]).
35
+
36
+ forecast_result(Header, Name, Time, Url) ->
37
+ io:format("~p ~p ~p ~s~n", [Header, Name, Time, Url]).
38
+
39
+ fetch_url(Url, ProfileName) ->
40
+ try
41
+ Tic = erlang:now(),
42
+ Result = get_over_http(Url, ProfileName),
43
+ Tac = erlang:now(),
44
+ Time = time_to_ms(Tac) - time_to_ms(Tic),
45
+ case Result of
46
+ timeout ->
47
+ [{"HTTP/1.1", 408, "Request Timeout"}, Time];
48
+ {Header, _Params, _Body} ->
49
+ [Header, Time]
50
+ end
51
+ catch
52
+ _:_ ->
53
+ [{"HTTP/1.1", 400, "Bad Request"}, 0]
54
+ end.
39
55
 
40
56
  time_to_ms({Mega, Sec, Micro})->
41
- Mega * 1000000000 + Sec * 1000 + Micro / 1000.
42
-
43
- get_over_http(Url) ->
44
- {ok, RequestId} = httpc:request(get, {Url, []}, [], [{sync, false}], ProfileName),
45
- receive
46
- {http, {RequestId, Result}} ->
47
- Result
48
- after
49
- 10000 ->
50
- timeout
51
- end.
57
+ Mega * 1000000000 + Sec * 1000 + Micro / 1000.
58
+
59
+ get_over_http(Url, ProfileName) ->
60
+ {ok, RequestId} = httpc:request(get, {Url, []}, [], [{sync, false}], ProfileName),
61
+ receive
62
+ {http, {RequestId, Result}} ->
63
+ Result
64
+ after
65
+ 10000 ->
66
+ timeout
67
+ end.
@@ -9,7 +9,8 @@ require "#{LOADABOY_EXEC_DIR}/Loadafile" if File.exist? "#{LOADABOY_EXEC_DIR}/Lo
9
9
 
10
10
  receive do |f|
11
11
  f.when([:prepare, Array]) do |array|
12
- f.send!([:result, generate_requests(array[0], array[1])])
12
+ workers, jobs, service = array
13
+ f.send!([:result, generate_requests(workers, jobs, service)])
13
14
  f.receive_loop
14
15
  end
15
16
  end
data/lib/loadaboy.rb CHANGED
@@ -3,29 +3,31 @@ require 'enumerator'
3
3
  module LoadaBoy
4
4
 
5
5
  class << self
6
- def generator(&block)
7
- define_method :generate, &block
6
+ def generator(service = 'default', &block)
7
+ define_method "generate_#{service}", &block
8
+ end
9
+ def set_generator(name)
10
+ alias_method(:generate, "generate_#{name}")
8
11
  end
9
12
  end
10
13
 
11
- protected
12
-
13
- def generate
14
+ def generate_default
14
15
  { :default => "/" }
15
16
  end
16
17
 
17
- def generate_requests(workers_count, jobs_count)
18
+ alias_method :generate, :generate_default
19
+
20
+ protected
21
+
22
+ def generate_requests(workers_count, jobs_count, service = nil)
23
+ methods.include?("generate_#{service}") ? LoadaBoy.set_generator(service) : LoadaBoy.set_generator(:default)
18
24
  (1..workers_count).map do |i|
19
25
  ["worker#{i}".to_sym, generate_urls(jobs_count)]
20
26
  end
21
27
  end
22
28
 
23
29
  def generate_urls(jobs_count)
24
- urls = []
25
- (1..jobs_count).map { generate.to_a }.flatten.each_slice(2) do |slice|
26
- urls << slice
27
- end
28
- urls
30
+ (1..jobs_count).inject([]){ |acc, _| acc + generate.to_a}
29
31
  end
30
32
  end
31
33
 
data/loadaboy.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{loadaboy}
8
- s.version = "0.1.1"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jell"]
12
- s.date = %q{2010-10-26}
12
+ s.date = %q{2010-11-15}
13
13
  s.default_executable = %q{loadaboy}
14
14
  s.description = %q{LoadaBoy is a load testing gem.}
15
15
  s.email = %q{jean-louis@icehouse.se}
data/test/helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ require 'mocha'
4
5
 
5
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -1,7 +1,48 @@
1
1
  require 'helper'
2
2
 
3
3
  class TestLoadaboy < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
4
+ include LoadaBoy
5
+
6
+ context ".generator" do
7
+ should "define a generate function running the given block" do
8
+ LoadaBoy.generator do
9
+ "custom generator"
10
+ end
11
+ assert_equal "custom generator", generate_default
12
+ end
13
+ should "define a generate function running the given block with proper name" do
14
+ LoadaBoy.generator :test do
15
+ "custom generator"
16
+ end
17
+ assert_equal "custom generator", generate_test
18
+ end
19
+ end
20
+
21
+ context "#generate" do
22
+ should "return a default url" do
23
+ assert_equal({:default => "/"}, generate)
24
+ end
25
+ end
26
+
27
+ context "#generate_requests" do
28
+ should "return a list of n-workers with their url list" do
29
+ stubs(:generate_urls).returns("url_list")
30
+ assert_equal [[:worker1, "url_list"], [:worker2, "url_list"]], generate_requests(2, 1)
31
+ end
32
+ should "set proper generator" do
33
+ LoadaBoy.generator :test do
34
+ "custom generator"
35
+ end
36
+ generate_requests(2, 1, :test)
37
+ assert_equal "custom generator", generate
38
+ end
39
+ end
40
+
41
+ context "#generate_urls" do
42
+ should "return a list of n urls" do
43
+ stubs(:generate).returns("name" => "url")
44
+ assert_equal [["name", "url"], ["name", "url"]], generate_urls(2)
45
+ end
6
46
  end
47
+
7
48
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loadaboy
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jell
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-26 00:00:00 +02:00
18
+ date: 2010-11-15 00:00:00 +01:00
19
19
  default_executable: loadaboy
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency