neptune 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/README +7 -4
  2. data/doc/AppControllerClient.html +12 -4
  3. data/doc/CommonFunctions.html +55 -42
  4. data/doc/Kernel.html +187 -0
  5. data/doc/LICENSE.html +2 -0
  6. data/doc/Object.html +488 -198
  7. data/doc/README.html +26 -5
  8. data/doc/bin/neptune.html +1 -1
  9. data/doc/created.rid +6 -6
  10. data/doc/index.html +20 -2
  11. data/doc/lib/app_controller_client_rb.html +2 -2
  12. data/doc/lib/common_functions_rb.html +2 -2
  13. data/doc/lib/neptune_rb.html +3 -1
  14. data/lib/app_controller_client.rb +2 -2
  15. data/lib/common_functions.rb +50 -24
  16. data/lib/neptune.rb +224 -159
  17. data/samples/appscale/add_appserver.rb +10 -0
  18. data/samples/appscale/add_database.rb +9 -0
  19. data/samples/appscale/add_loadbalancer.rb +9 -0
  20. data/samples/appscale/add_slave.rb +9 -0
  21. data/samples/c/compile_helloworld.rb +10 -0
  22. data/samples/c/helloworld/helloworld.c +6 -0
  23. data/samples/erlang/compile_erlang_ring.rb +10 -0
  24. data/samples/erlang/get_erlang_output.rb +8 -0
  25. data/samples/erlang/ring/Makefile +3 -0
  26. data/samples/erlang/ring/ring.erl +90 -0
  27. data/samples/erlang/run_erlang_ring.rb +6 -0
  28. data/samples/go/compile_hello.rb +10 -0
  29. data/samples/go/get_hello_output.rb +6 -0
  30. data/samples/go/hello/hello.go +8 -0
  31. data/samples/go/put_input.rb +8 -0
  32. data/samples/go/run_hello.rb +9 -0
  33. data/samples/mapreduce/expected-output.txt +7078 -0
  34. data/samples/mapreduce/get_mapreduce_output.rb +4 -0
  35. data/samples/mapreduce/hadoop-0.20.0-examples.jar +0 -0
  36. data/samples/mapreduce/input-10 +64 -0
  37. data/samples/mapreduce/input-30 +64 -0
  38. data/samples/mapreduce/input-7 +4 -0
  39. data/samples/mapreduce/map.rb +48 -0
  40. data/samples/mapreduce/reduce.rb +48 -0
  41. data/samples/mapreduce/run_java_mr.rb +14 -0
  42. data/samples/mapreduce/run_mapreduce.rb +13 -0
  43. data/samples/mapreduce/the-end-of-time.txt +11256 -0
  44. data/samples/mpi/Makefile +22 -0
  45. data/samples/mpi/MpiQueen +0 -0
  46. data/samples/mpi/compile_mpi_ring.rb +10 -0
  47. data/samples/mpi/compile_x10_nqueens.rb +8 -0
  48. data/samples/mpi/cpi +0 -0
  49. data/samples/mpi/get_mpi_output.rb +5 -0
  50. data/samples/mpi/get_ring_output.rb +5 -0
  51. data/samples/mpi/hw2.c +205 -0
  52. data/samples/mpi/hw2harness.c +84 -0
  53. data/samples/mpi/hw2harness.h +45 -0
  54. data/samples/mpi/powermethod +0 -0
  55. data/samples/mpi/ring/Makefile +2 -0
  56. data/samples/mpi/ring/Ring.c +76 -0
  57. data/samples/mpi/run_mpi_cpi.rb +10 -0
  58. data/samples/mpi/run_mpi_nqueens.np +6 -0
  59. data/samples/mpi/run_mpi_powermethod.rb +8 -0
  60. data/samples/mpi/run_mpi_ring.rb +12 -0
  61. data/samples/r/compile_hello.rb +10 -0
  62. data/samples/r/get_hello_output.rb +6 -0
  63. data/samples/r/hello/hello.r +1 -0
  64. data/samples/r/put_input.rb +8 -0
  65. data/samples/r/run_hello.rb +9 -0
  66. data/samples/upc/compile_upc_helloworld.rb +10 -0
  67. data/samples/upc/compile_upc_ring.rb +11 -0
  68. data/samples/upc/get_mpi_output.rb +8 -0
  69. data/samples/upc/helloworld/HelloWorld.c +9 -0
  70. data/samples/upc/helloworld/Makefile +3 -0
  71. data/samples/upc/ring/Makefile +3 -0
  72. data/samples/upc/ring/Ring.c +116 -0
  73. data/samples/upc/run_upc_helloworld.rb +12 -0
  74. data/samples/upc/run_upc_ring.rb +12 -0
  75. data/samples/x10/MyPowerMethod +0 -0
  76. data/samples/x10/MyPowerMethod.x10 +236 -0
  77. data/samples/x10/NQueensDist +0 -0
  78. data/samples/x10/NQueensDist.x10 +112 -0
  79. data/samples/x10/compile_x10_nqueens.rb +7 -0
  80. data/samples/x10/compile_x10_ring.rb +12 -0
  81. data/samples/x10/get_x10_output.rb +8 -0
  82. data/samples/x10/ring/Makefile +3 -0
  83. data/samples/x10/ring/Ring.x10 +28 -0
  84. data/samples/x10/ring/RingOld.x10 +68 -0
  85. data/samples/x10/run_x10_nqueens.rb +6 -0
  86. data/samples/x10/run_x10_powermethod.rb +7 -0
  87. data/samples/x10/run_x10_ring.rb +6 -0
  88. data/test/{tc_c.rb → integration/tc_c.rb} +2 -2
  89. data/test/{tc_dfsp.rb → integration/tc_dfsp.rb} +0 -0
  90. data/test/{tc_dwssa.rb → integration/tc_dwssa.rb} +0 -0
  91. data/test/{tc_erlang.rb → integration/tc_erlang.rb} +0 -0
  92. data/test/{tc_mapreduce.rb → integration/tc_mapreduce.rb} +0 -0
  93. data/test/{tc_mpi.rb → integration/tc_mpi.rb} +0 -0
  94. data/test/{tc_storage.rb → integration/tc_storage.rb} +0 -0
  95. data/test/{tc_upc.rb → integration/tc_upc.rb} +0 -0
  96. data/test/{tc_x10.rb → integration/tc_x10.rb} +0 -0
  97. data/test/{test_helper.rb → integration/test_helper.rb} +0 -0
  98. data/test/{ts_neptune.rb → integration/ts_neptune.rb} +2 -2
  99. data/test/unit/test_app_controller_client.rb +106 -0
  100. data/test/unit/test_common_functions.rb +106 -0
  101. data/test/unit/test_neptune.rb +208 -0
  102. data/test/unit/ts_all.rb +6 -0
  103. metadata +91 -15
Binary file
@@ -0,0 +1,112 @@
1
+ /*
2
+ * This file is part of the X10 project (http://x10-lang.org).
3
+ *
4
+ * This file is licensed to You under the Eclipse Public License (EPL);
5
+ * You may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * http://www.opensource.org/licenses/eclipse-1.0.php
8
+ *
9
+ * (C) Copyright IBM Corporation 2006-2010.
10
+ */
11
+
12
+ import x10.io.Console;
13
+
14
+ /**
15
+ * A distributed version of NQueens. Runs over NUM_PLACES.
16
+ * Identical to NQueensPar, except that it runs over multiple placs.
17
+ * Converted to 2.1 on 9/1/2010
18
+ */
19
+ public class NQueensDist {
20
+ public static val expectedSolutions =
21
+ [0, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712, 365596, 2279184, 14772512];
22
+
23
+ val N:Int;
24
+ val P:Int;
25
+ val results:DistArray[Int](1);
26
+
27
+ def this(N:Int, P:Int) {
28
+ this.N=N;
29
+ this.P=P;
30
+ this.results = DistArray.make[Int](Dist.makeUnique(), (Point)=>0);
31
+ }
32
+ def start() {
33
+ new Board().search();
34
+ }
35
+ def run():Int {
36
+ finish start();
37
+ val result = results.reduce(Int.+,0);
38
+ return result;
39
+ }
40
+
41
+ /**
42
+ * Return an array of P regions, which together block divide the 1-D region R.
43
+ */
44
+ public static def block(R: Region(1){rect}, P: Int): Array[Region(1){rect}](1) = {
45
+ assert P >= 0;
46
+ val low = R.min()(0), high = R.max()(0), count = high-low+1;
47
+ val baseSize = count/P, extra = count - baseSize*P;
48
+ new Array[Region(1){rect}](P, (i:int):Region(1){rect} => {
49
+ val start = low+i*baseSize+ (i < extra? i:extra);
50
+ start..start+baseSize+(i < extra?0:-1)
51
+ })
52
+ }
53
+
54
+ class Board {
55
+ val q: Array[Int](1);
56
+ def this() {
57
+ q = new Array[Int](0);
58
+ }
59
+ def this(old:Array[Int](1), newItem:Int) {
60
+ val n = old.size;
61
+ q = new Array[Int](n+1, (i:int)=> (i < n? old(i) : newItem));
62
+ }
63
+ def safe(j: int) {
64
+ val n = q.size;
65
+ for ([k] in 0..n-1) {
66
+ if (j == q(k) || Math.abs(n-k) == Math.abs(j-q(k)))
67
+ return false;
68
+ }
69
+ return true;
70
+ }
71
+ /** Search for all solutions in parallel, on finding
72
+ * a solution update nSolutions.
73
+ */
74
+ def search(R: Region(1){rect}) {
75
+ for ([k] in R)
76
+ if (safe(k))
77
+ new Board(q, k).search();
78
+ }
79
+
80
+ def search() {
81
+ if (q.size == N) {
82
+ atomic NQueensDist.this.results(here.id)++;
83
+ return;
84
+ }
85
+ if (q.size == 0) {
86
+ val R = block(0..N-1, P);
87
+ ateach ([q] in Dist.makeUnique())
88
+ // copy of this made across the at divide
89
+ search(R(q));
90
+ } else search(0..N-1);
91
+ }
92
+ }
93
+
94
+ public static def main(args:Array[String](1)) {
95
+ val n = args.size > 0 ? Int.parse(args(0)) : 16;
96
+ println("N=" + n);
97
+ //warmup
98
+ //finish new NQueensPar(12, 1).start();
99
+ val P = Place.MAX_PLACES;
100
+ val nq = new NQueensDist(n,P);
101
+ var start:Long = -System.nanoTime();
102
+ val answer = nq.run();
103
+ val result = answer==expectedSolutions(n);
104
+ start += System.nanoTime();
105
+ start /= 1000000;
106
+ println("NQueensPar " + nq.N + "(P=" + P +
107
+ ") has " + answer + " solutions" +
108
+ (result? " (ok)." : " (wrong).") + "time=" + start + "ms");
109
+ }
110
+
111
+ static def println(s:String) = Console.OUT.println(s);
112
+ }
@@ -0,0 +1,7 @@
1
+ neptune :type => "compile",
2
+ #:keyname => "cluster",
3
+ :code => "NQueensDist.x10",
4
+ :output => "/baz",
5
+ :lang => "x10",
6
+ :copy_to => "NQueensCompiled"
7
+
@@ -0,0 +1,12 @@
1
+ result = neptune (
2
+ :type => "compile",
3
+ #:keyname => "cluster",
4
+ :code => "ring",
5
+ :main => "Ring.x10",
6
+ :output => "/baz",
7
+ :copy_to => "ring-compiled"
8
+ )
9
+
10
+ puts "out = #{result[:out]}"
11
+ puts "err = #{result[:err]}"
12
+
@@ -0,0 +1,8 @@
1
+
2
+ result = neptune (
3
+ :type => "output",
4
+ #:keyname => "neptune",
5
+ :output => "/baz.txt"
6
+ )
7
+
8
+ puts result
@@ -0,0 +1,3 @@
1
+ all:
2
+ /usr/local/x10/x10.dist/bin/x10c++ -x10rt mpi -o Ring Ring.x10
3
+
@@ -0,0 +1,28 @@
1
+ import x10.lang.Math;
2
+ import x10.util.Timer;
3
+
4
+ public class Ring {
5
+
6
+ static val NUM_MESSAGES = 10;
7
+
8
+ // A global datastructure with one integer cell per place
9
+ static A = PlaceLocalHandle.make[Cell[Long]](Dist.makeUnique(), ()=>new Cell[Long](-1));
10
+
11
+ public static def send (msg:Long, depth:Int) {
12
+ A()() = msg;
13
+ if (depth==0) return;
14
+ async at (here.next()) send(msg, depth-1);
15
+ }
16
+
17
+ public static def main(args:Array[String](1)) {
18
+
19
+ val startTime = Timer.milliTime();
20
+ finish send(42L, NUM_MESSAGES * Place.MAX_PLACES);
21
+ val endTime = Timer.milliTime();
22
+
23
+ val totalTime = (endTime - startTime) / 1000.0;
24
+
25
+ Console.OUT.printf("It took %f seconds\n", totalTime);
26
+ }
27
+ }
28
+
@@ -0,0 +1,68 @@
1
+ import x10.lang.Math;
2
+ import x10.util.Timer;
3
+
4
+ public class Ring {
5
+
6
+ static val NUM_MESSAGES = 2;
7
+ static val P = Place.MAX_PLACES;
8
+
9
+ /* Can't do message passing directly - so we'll use shared memory
10
+ * to achieve the same goal. The basic idea is to give each processor
11
+ * a single integer and have them wait for it to change values */
12
+
13
+ static val R <: Region = (0..P-1);
14
+ static val D <: Dist = Dist.makeBlock(R);
15
+ static val F <: (Point(1)) => Int = ([i]:Point(1)) => -1;
16
+ static val A <: DistArray[Int] = DistArray.make[Int](D, F);
17
+
18
+ public static def send(target:Int, value:Int) {
19
+ at (Place.place(target)) {
20
+ A(target) = value;
21
+ }
22
+
23
+ return;
24
+ }
25
+
26
+ public static def recv(from:Int, value:Int) {
27
+ while (A(here.id) != value) {
28
+ Activity.sleep(10l);
29
+ }
30
+
31
+ return;
32
+ }
33
+
34
+ public static def main(args:Array[String](1)) {
35
+ val startTime = Timer.milliTime();
36
+
37
+ for (var index : Int = 0; index < NUM_MESSAGES; index++) {
38
+ val i = index;
39
+ finish for (p in Place.places()) {
40
+ async at (p) {
41
+ if (p.id == 0) {
42
+ Console.OUT.printf("master is sending message to node 1\n");
43
+ Ring.send(1, i);
44
+
45
+ Console.OUT.printf("master is waiting for message from node %d\n", P - 1);
46
+ Ring.recv(P - 1, i);
47
+ } else {
48
+ Ring.recv(p.id - 1, i);
49
+
50
+ if (p.id + 1 == P) {
51
+ Console.OUT.printf("node %d is sending a message to node 0\n", p.id);
52
+ Ring.send(0, i);
53
+ } else {
54
+ Console.OUT.printf("node %d is sending a message to node %d\n", p.id, p.id + 1);
55
+ Ring.send(p.id + 1, i);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ val endTime = Timer.milliTime();
63
+ val totalTime = (endTime - startTime) / 1000.0;
64
+
65
+ Console.OUT.printf("It took %f seconds\n", totalTime);
66
+ }
67
+ }
68
+
@@ -0,0 +1,6 @@
1
+ neptune :type => "mpi",
2
+ #:keyname = "cluster",
3
+ :code => "NQueensCompiled",
4
+ :output => "/baz.txt",
5
+ :nodes_to_use => 1
6
+
@@ -0,0 +1,7 @@
1
+ neptune :type => "mpi",
2
+ :keyname => "cluster",
3
+ #:input => "baz",
4
+ #:output => "x10-output.txt",
5
+ :code => "MyPowerMethod",
6
+ :nodes_to_use => 16
7
+
@@ -0,0 +1,6 @@
1
+ neptune :type => "mpi",
2
+ #:keyname = "cluster",
3
+ :code => "ring-compiled/Ring",
4
+ :output => "/baz.txt",
5
+ :nodes_to_use => 4
6
+
@@ -1,8 +1,8 @@
1
1
 
2
- $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
3
3
  require 'neptune'
4
4
 
5
- $:.unshift File.join(File.dirname(__FILE__), "..", "test")
5
+ $:.unshift File.join(File.dirname(__FILE__), "..", "test", "integration")
6
6
  require 'test_helper'
7
7
 
8
8
  require 'test/unit'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
1
  STORAGE_TYPES = ["appdb", "gstorage", "s3", "walrus"] - ["appdb"]
2
2
 
3
- $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
4
4
  require 'neptune'
5
5
 
6
- $:.unshift File.join(File.dirname(__FILE__), "..", "test")
6
+ $:.unshift File.join(File.dirname(__FILE__), "..", "test", "integration")
7
7
  require 'test_helper'
8
8
 
9
9
  REQUIRED_CREDS = %w{ APPSCALE_HEAD_NODE
@@ -0,0 +1,106 @@
1
+ # Programmer: Chris Bunch (cgb@cs.ucsb.edu)
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
4
+ require 'app_controller_client'
5
+
6
+ require 'test/unit'
7
+
8
+ class FakeConnection
9
+ # Since all the methods we're faking take the same arguments and have
10
+ # the same semantics (return true or abort), just cover it all in one place.
11
+ def method_missing(id, *args, &block)
12
+ method_names = ["neptune_start_job", "neptune_put_input"] +
13
+ ["neptune_get_output", "neptune_get_acl", "neptune_set_acl"] +
14
+ ["neptune_compile_code"]
15
+
16
+ if method_names.include?(id.to_s)
17
+ job_data = args[0]
18
+ if job_data.include?("OK")
19
+ return true
20
+ else
21
+ return "Error:"
22
+ end
23
+ else
24
+ super
25
+ end
26
+ end
27
+ end
28
+
29
+ class TestAppControllerClient < Test::Unit::TestCase
30
+ def setup
31
+ @client = AppControllerClient.new("localhost", "secret")
32
+ @client.conn = FakeConnection.new()
33
+
34
+ @job_data_ok = ["OK"]
35
+ @job_data_err = ["ERR"]
36
+ end
37
+
38
+ def test_make_call
39
+ no_timeout = -1
40
+ retry_on_exception = true
41
+ no_retry_on_exception = false
42
+
43
+ call_number = 0
44
+ assert_nothing_raised(SystemExit) {
45
+ @client.make_call(no_timeout, retry_on_exception) {
46
+ call_number += 1
47
+ case call_number
48
+ when 1
49
+ raise Errno::ECONNREFUSED
50
+ when 2
51
+ raise OpenSSL::SSL::SSLError
52
+ when 3
53
+ raise Exception
54
+ else
55
+ 0
56
+ end
57
+ }
58
+ }
59
+
60
+ assert_raise(SystemExit) {
61
+ @client.make_call(no_timeout, no_retry_on_exception) {
62
+ raise Errno::ECONNREFUSED
63
+ }
64
+ }
65
+
66
+ assert_raise(SystemExit) {
67
+ @client.make_call(no_timeout, no_retry_on_exception) {
68
+ raise Exception
69
+ }
70
+ }
71
+
72
+ end
73
+
74
+ # The remaining tests are identical since the implemented methods are all
75
+ # extremely similar - these methods all make a SOAP call and return the
76
+ # result unless it has 'Error:' in it. If it does, it aborts execution.
77
+ def test_start_neptune_job
78
+ assert(@client.start_neptune_job(@job_data_ok))
79
+ assert_raise(SystemExit) { @client.start_neptune_job(@job_data_err) }
80
+ end
81
+
82
+ def test_put_input
83
+ assert(@client.put_input(@job_data_ok))
84
+ assert_raise(SystemExit) { @client.put_input(@job_data_err) }
85
+ end
86
+
87
+ def test_get_output
88
+ assert(@client.get_output(@job_data_ok))
89
+ assert_raise(SystemExit) { @client.get_output(@job_data_err) }
90
+ end
91
+
92
+ def test_get_acl
93
+ assert(@client.get_acl(@job_data_ok))
94
+ assert_raise(SystemExit) { @client.get_acl(@job_data_err) }
95
+ end
96
+
97
+ def test_set_acl
98
+ assert(@client.set_acl(@job_data_ok))
99
+ assert_raise(SystemExit) { @client.set_acl(@job_data_err) }
100
+ end
101
+
102
+ def test_compile_code
103
+ assert(@client.compile_code(@job_data_ok))
104
+ assert_raise(SystemExit) { @client.compile_code(@job_data_err) }
105
+ end
106
+ end
@@ -0,0 +1,106 @@
1
+ # Programmer: Chris Bunch (cgb@cs.ucsb.edu)
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
4
+ require 'common_functions'
5
+
6
+ require 'test/unit'
7
+
8
+ SECRET = "hey-its-a-secret"
9
+
10
+ module FakeCommonFunctions
11
+ def self.get_from_yaml(a, b, c)
12
+ end
13
+
14
+ def self.scp_file(a, b, c, d, e)
15
+ end
16
+ end
17
+
18
+ class FakeFile
19
+ @@exists_checks_done = 0
20
+
21
+ def self.expand_path(path)
22
+ return path
23
+ end
24
+
25
+ def self.exists?(name)
26
+ if name.include?("OK") or name.include?("BAD-TAG") or name.include?("FAIL")
27
+ return true
28
+ elsif name.include?("retval")
29
+ # The first time around, tell the caller that the file didn't exist so
30
+ # that it sleeps. The next time around, tell it that the file does exist.
31
+ if @@exists_checks_done.zero?
32
+ @@exists_checks_done += 1
33
+ return false
34
+ else
35
+ return true
36
+ end
37
+ else
38
+ return false
39
+ end
40
+ end
41
+
42
+ def self.open(name, mode=nil)
43
+ if name.include?("retval")
44
+ return "0\n"
45
+ else
46
+ return "1\n"
47
+ end
48
+ end
49
+ end
50
+
51
+ module FakeFileUtils
52
+ def self.rm_f(name)
53
+ # Do nothing - faking out File means we don't have extra files to clean up
54
+ end
55
+ end
56
+
57
+ module FakeKernel
58
+ def `(command)
59
+ # Do nothing - we don't need the side-effects from exec'ing a command
60
+ end
61
+
62
+ #def sleep(time)
63
+ # Do nothing - we don't actually need to sleep the current thread
64
+ #end
65
+ end
66
+
67
+ module FakeYAML
68
+ def self.load_file(filename)
69
+ if filename.include?("OK")
70
+ return { :secret => SECRET, :shadow => SECRET }
71
+ elsif filename.include?("BAD-TAG")
72
+ return {}
73
+ else
74
+ raise ArgumentError
75
+ end
76
+ end
77
+ end
78
+
79
+ class TestCommonFunctions < Test::Unit::TestCase
80
+ def test_scp_to_shadow
81
+ assert_nothing_raised(Exception) {
82
+ CommonFunctions.scp_to_shadow("OK", "OK", "OK", "OK",
83
+ file=FakeFile,
84
+ get_from_yaml=FakeCommonFunctions.method(:get_from_yaml),
85
+ scp_file=FakeCommonFunctions.method(:scp_file))
86
+ }
87
+ end
88
+
89
+ def test_get_secret_key
90
+ assert_equal(SECRET, CommonFunctions.get_secret_key("OK", required=true,
91
+ FakeFile, FakeYAML))
92
+
93
+ assert_raise(SystemExit) { CommonFunctions.get_secret_key("BAD-TAG",
94
+ required=true, FakeFile, FakeYAML) }
95
+
96
+ assert_raise(SystemExit) { CommonFunctions.get_secret_key("FAIL",
97
+ required=true, FakeFile, FakeYAML) }
98
+ assert_nil(CommonFunctions.get_secret_key("FAIL", required=false, FakeFile,
99
+ FakeYAML))
100
+
101
+ assert_raise(SystemExit) {
102
+ CommonFunctions.get_secret_key("NOT-EXISTANT", required=true, FakeFile,
103
+ FakeYAML)
104
+ }
105
+ end
106
+ end