blackstart 0.2.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of blackstart might be problematic. Click here for more details.
- data/LICENSE.txt +1 -1
- data/README.txt +90 -66
- data/lib/blackstart.rb +44 -61
- data/test/blackstart_test.rb +267 -205
- metadata +6 -7
- data/blackstart.gemspec +0 -11
    
        data/LICENSE.txt
    CHANGED
    
    
    
        data/README.txt
    CHANGED
    
    | @@ -2,82 +2,99 @@ Blackstart | |
| 2 2 |  | 
| 3 3 | 
             
            Blackstart is a small, subdued library for automated testing in Ruby. It
         | 
| 4 4 | 
             
            doesn't depend on anything beyond the Ruby platform and doesn't modify the
         | 
| 5 | 
            -
            environment  | 
| 6 | 
            -
            program, not via  | 
| 5 | 
            +
            environment beyond its conventional namespace. It's tested with a primitive
         | 
| 6 | 
            +
            program, not via an automated-testing library.
         | 
| 7 7 |  | 
| 8 | 
            -
            The blackstart is a small, subdued bird that eats bugs. A black start is when | 
| 9 | 
            -
            power plant  | 
| 8 | 
            +
            The blackstart is a small, subdued bird that eats bugs. A black start is when
         | 
| 9 | 
            +
            an inactive power plant restarts by means of an independent power source rather
         | 
| 10 | 
            +
            than the electrical grid.
         | 
| 10 11 |  | 
| 11 12 | 
             
            Here's an example of a test program that uses Blackstart:
         | 
| 12 13 |  | 
| 13 14 | 
             
              require "blackstart"
         | 
| 14 15 |  | 
| 15 | 
            -
              exit Blackstart.run  | 
| 16 | 
            -
                 | 
| 16 | 
            +
              exit Blackstart.run [
         | 
| 17 | 
            +
                proc {
         | 
| 17 18 | 
             
                  unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
         | 
| 18 19 | 
             
                    raise "join did not work as expected"
         | 
| 19 20 | 
             
                  end
         | 
| 20 | 
            -
                 | 
| 21 | 
            +
                },
         | 
| 21 22 |  | 
| 22 | 
            -
                 | 
| 23 | 
            +
                proc {
         | 
| 23 24 | 
             
                  unless "sample" == "simple".gsub(/i/, "a")
         | 
| 24 25 | 
             
                    raise "gsub did not work as expected"
         | 
| 25 26 | 
             
                  end
         | 
| 26 | 
            -
                 | 
| 27 | 
            -
               | 
| 28 | 
            -
             | 
| 29 | 
            -
            Blackstart.add adds procs to a collection and returns that collection. In the
         | 
| 30 | 
            -
            example, each proc is designed to run a test and, if it fails, raise an error
         | 
| 31 | 
            -
            to signal this.
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
              ]
         | 
| 32 29 |  | 
| 33 30 | 
             
            Blackstart.run runs a sequence of tests and reports information about any that
         | 
| 34 | 
            -
            fail  | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 31 | 
            +
            fail. It returns false if there were failures, true otherwise.
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            An array of procs is an easy way to represent a sequence of tests. In general,
         | 
| 34 | 
            +
            the sequence can be any object that responds to an each message in the
         | 
| 35 | 
            +
            conventional way and each test object in the sequence can be any object that
         | 
| 36 | 
            +
            (1) is a proc or converts to one via to_proc and (2) converts to a string via
         | 
| 37 | 
            +
            to_s.
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            Each test is run by converting the test object to a proc if necessary and then
         | 
| 40 | 
            +
            calling it in the context of a new instance of Blackstart::Scratchpad; if and
         | 
| 41 | 
            +
            only if this raises an error (any exception that's a kind of StandardError),
         | 
| 42 | 
            +
            Blackstart.run interprets it as a failed test.
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            After each test failure, Blackstart.run sends a puts message with failure
         | 
| 45 | 
            +
            information to its second parameter, which is $stdout by default. The failure
         | 
| 46 | 
            +
            information includes a description of the test (the test object converted to a
         | 
| 47 | 
            +
            string) and a description of the error raised (its class, message, and
         | 
| 48 | 
            +
            backtrace).
         | 
| 37 49 |  | 
| 38 | 
            -
            The library provides little beyond this, but it's easy to do  | 
| 39 | 
            -
             | 
| 50 | 
            +
            The library provides little beyond this, but it's easy to do sophisticated
         | 
| 51 | 
            +
            things with it. Some examples follow.
         | 
| 40 52 |  | 
| 41 53 |  | 
| 42 54 | 
             
            - Exiting with the appropriate status
         | 
| 43 55 |  | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 56 | 
            +
            You may want your test program to exit with a successful status only if there
         | 
| 57 | 
            +
            were no failures, perhaps so it can work as part of a testing script. You can
         | 
| 58 | 
            +
            do this by using the object returned by Blackstart.run as the argument to
         | 
| 59 | 
            +
            Kernel#exit. Blackstart.run returns false if there were failures, true
         | 
| 60 | 
            +
            otherwise.
         | 
| 47 61 |  | 
| 48 62 |  | 
| 49 63 | 
             
            - Defining helpers
         | 
| 50 64 |  | 
| 51 | 
            -
            You may want all your tests to  | 
| 65 | 
            +
            You may want to enable all your tests to assert something, generate test data,
         | 
| 52 66 | 
             
            or perform some other task by sending a message. You could implement this
         | 
| 53 67 | 
             
            statelessly in a singleton object or all instances of Object, for example, but
         | 
| 54 | 
            -
            there's another option that may be more convenient. Blackstart.run calls  | 
| 55 | 
            -
            test proc  | 
| 56 | 
            -
            instance methods in that class  | 
| 68 | 
            +
            there's another option that may be more convenient. When Blackstart.run calls a
         | 
| 69 | 
            +
            test proc, it sets the self object to a new instance of Blackstart::Scratchpad.
         | 
| 70 | 
            +
            You can define instance methods in that class. For example:
         | 
| 57 71 |  | 
| 58 | 
            -
              class Blackstart:: | 
| 72 | 
            +
              class Blackstart::Scratchpad
         | 
| 59 73 | 
             
                def assert boolean
         | 
| 60 74 | 
             
                  raise "assertion failed" unless boolean
         | 
| 61 75 | 
             
                end
         | 
| 62 76 |  | 
| 63 77 | 
             
                def make_products
         | 
| 64 78 | 
             
                  @cabbage = { :description => "head of cabbage", :price => 125 }
         | 
| 65 | 
            -
                  @orange = { :description => "Cara  | 
| 79 | 
            +
                  @orange = { :description => "Cara Cara navel orange", :price => 100 }
         | 
| 66 80 | 
             
                  nil
         | 
| 67 81 | 
             
                end
         | 
| 68 82 | 
             
              end
         | 
| 69 83 |  | 
| 70 | 
            -
            All  | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 84 | 
            +
            All your test procs can use them by sending messages to self. These methods,
         | 
| 85 | 
            +
            like the test proc itself, can see and modify instance variables and other
         | 
| 86 | 
            +
            elements of the scratchpad's state. The scratchpad is disposable:
         | 
| 87 | 
            +
            Blackstart.run discards it after the test is complete.
         | 
| 73 88 |  | 
| 74 89 |  | 
| 75 90 | 
             
            - Running code before and after each test
         | 
| 76 91 |  | 
| 77 | 
            -
            You  | 
| 78 | 
            -
             | 
| 92 | 
            +
            You may want to run code before and after each test -- for example, to create
         | 
| 93 | 
            +
            objects needed in tests or clean up resources without requiring every test to
         | 
| 94 | 
            +
            do these explicitly. You can do this by defining a custom
         | 
| 95 | 
            +
            Blackstart::Scratchpad#instance_exec. For example:
         | 
| 79 96 |  | 
| 80 | 
            -
              class Blackstart:: | 
| 97 | 
            +
              class Blackstart::Scratchpad
         | 
| 81 98 | 
             
                def instance_exec(*)
         | 
| 82 99 | 
             
                  puts "before test"
         | 
| 83 100 | 
             
                  @variable = "example"
         | 
| @@ -87,66 +104,73 @@ Blackstart::Context#instance_exec. For example: | |
| 87 104 | 
             
                end
         | 
| 88 105 | 
             
              end
         | 
| 89 106 |  | 
| 107 | 
            +
            Use this hack with care because the test proc could send instance_exec messages
         | 
| 108 | 
            +
            to self.
         | 
| 109 | 
            +
             | 
| 90 110 |  | 
| 91 111 | 
             
            - Building a test collection in stages
         | 
| 92 112 |  | 
| 93 | 
            -
            You  | 
| 94 | 
            -
             | 
| 113 | 
            +
            You may want to build a test collection in stages rather than all at once. This
         | 
| 114 | 
            +
            can be done straightforwardly:
         | 
| 95 115 |  | 
| 96 116 | 
             
              require "blackstart"
         | 
| 97 117 |  | 
| 98 | 
            -
               | 
| 118 | 
            +
              TESTS = []
         | 
| 99 119 |  | 
| 100 | 
            -
               | 
| 101 | 
            -
                 | 
| 120 | 
            +
              TESTS.concat [
         | 
| 121 | 
            +
                proc {
         | 
| 102 122 | 
             
                  unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
         | 
| 103 123 | 
             
                    raise "join did not work as expected"
         | 
| 104 124 | 
             
                  end
         | 
| 105 | 
            -
                 | 
| 106 | 
            -
               | 
| 125 | 
            +
                }
         | 
| 126 | 
            +
              ]
         | 
| 107 127 |  | 
| 108 | 
            -
               | 
| 109 | 
            -
                 | 
| 128 | 
            +
              TESTS.concat [
         | 
| 129 | 
            +
                proc {
         | 
| 110 130 | 
             
                  unless "sample" == "simple".gsub(/i/, "a")
         | 
| 111 131 | 
             
                    raise "gsub did not work as expected"
         | 
| 112 132 | 
             
                  end
         | 
| 113 | 
            -
                 | 
| 114 | 
            -
               | 
| 133 | 
            +
                }
         | 
| 134 | 
            +
              ]
         | 
| 115 135 |  | 
| 116 | 
            -
              exit Blackstart.run  | 
| 136 | 
            +
              exit Blackstart.run TESTS
         | 
| 117 137 |  | 
| 118 | 
            -
            This  | 
| 119 | 
            -
            load  | 
| 138 | 
            +
            This pattern is useful if you want to define your tests in multiple files: you
         | 
| 139 | 
            +
            create a collection with an agreed-upon name, load multiple files, each of
         | 
| 140 | 
            +
            which adds test objects to that collection, and then run all the tests.
         | 
| 120 141 |  | 
| 121 142 |  | 
| 122 143 | 
             
            - Running tests in random order
         | 
| 123 144 |  | 
| 145 | 
            +
            To check if you have any tests that depend on other tests having run, or not
         | 
| 146 | 
            +
            having run, earlier, you may want to run your tests in random order.
         | 
| 124 147 | 
             
            Blackstart.run runs a sequence of tests in order, but you can pass it a
         | 
| 125 | 
            -
            randomly-ordered sequence. Array#shuffle may be helpful for this. If you  | 
| 126 | 
            -
             | 
| 127 | 
            -
            so you can  | 
| 148 | 
            +
            randomly-ordered sequence. Array#shuffle may be helpful for this. If you use
         | 
| 149 | 
            +
            Array#shuffle or something similar, you may also want to print the random seed
         | 
| 150 | 
            +
            just before the tests are shuffled so you can rerun your tests in the same
         | 
| 151 | 
            +
            order by setting the random seed.
         | 
| 128 152 |  | 
| 129 153 |  | 
| 130 154 | 
             
            - Reporting detailed test descriptions
         | 
| 131 155 |  | 
| 132 | 
            -
            After a test fails, Blackstart.run writes a  | 
| 133 | 
            -
            output stream. It gets this  | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
            clear what was being tested.
         | 
| 156 | 
            +
            After a test fails, Blackstart.run writes a string describing the test to the
         | 
| 157 | 
            +
            output stream. It gets this string by sending a to_s message to the test
         | 
| 158 | 
            +
            object. When the test object is an instance of Proc, the returned string
         | 
| 159 | 
            +
            typically includes the file path and line number where it was defined: helpful,
         | 
| 160 | 
            +
            but it won't be immediately clear what was being tested.
         | 
| 138 161 |  | 
| 139 | 
            -
            You can improve this by  | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 162 | 
            +
            You can improve this by designing your own test objects. Blackstart.run does
         | 
| 163 | 
            +
            not strictly need a sequence of procs; it also works with a sequence of objects
         | 
| 164 | 
            +
            that convert to procs in response to to_proc messages. Your custom test objects
         | 
| 165 | 
            +
            can respond to to_s with detailed descriptions instead of mere file paths and
         | 
| 166 | 
            +
            line numbers.
         | 
| 144 167 |  | 
| 145 168 |  | 
| 146 169 | 
             
            - Handling failures differently
         | 
| 147 170 |  | 
| 148 171 | 
             
            Blackstart.run handles each failure by sending a puts message to the output
         | 
| 149 | 
            -
            stream, one of its parameters. By default,  | 
| 150 | 
            -
             | 
| 151 | 
            -
            can specify any object as the output | 
| 152 | 
            -
            -- allowing you to handle failure | 
| 172 | 
            +
            stream, one of its parameters, with failure information. By default, the output
         | 
| 173 | 
            +
            stream is $stdout, so the default behavior is to print unadorned failure
         | 
| 174 | 
            +
            information to standard output. But you can specify any object as the output
         | 
| 175 | 
            +
            stream -- even if it's not really a stream -- allowing you to handle failure
         | 
| 176 | 
            +
            information however you want.
         | 
    
        data/lib/blackstart.rb
    CHANGED
    
    | @@ -1,87 +1,70 @@ | |
| 1 1 | 
             
            ##
         | 
| 2 | 
            -
            # This module provides facilities for  | 
| 2 | 
            +
            # This module provides facilities for running automated tests.
         | 
| 3 3 |  | 
| 4 4 | 
             
            module Blackstart
         | 
| 5 5 |  | 
| 6 6 | 
             
              ##
         | 
| 7 | 
            -
              #  | 
| 7 | 
            +
              # Yields to the block and returns a string describing the error raised, if
         | 
| 8 | 
            +
              # any.
         | 
| 8 9 | 
             
              #
         | 
| 9 | 
            -
              # In detail: This method  | 
| 10 | 
            -
              #  | 
| 11 | 
            -
              #  | 
| 12 | 
            -
              #  | 
| 13 | 
            -
              #  | 
| 14 | 
            -
              #  | 
| 15 | 
            -
              #  | 
| 16 | 
            -
              #  | 
| 10 | 
            +
              # In detail: This method yields to the block with no arguments. If that
         | 
| 11 | 
            +
              # raises an error -- meaning an exception whose class is StandardError or a
         | 
| 12 | 
            +
              # descendant of StandardError -- this method attempts to make and return a
         | 
| 13 | 
            +
              # string describing that error; the description consists of the error's
         | 
| 14 | 
            +
              # class, message, and backtrace (if any). If an exception is raised while
         | 
| 15 | 
            +
              # making an error description, this method raises that exception. If
         | 
| 16 | 
            +
              # yielding to the block raises a non-error exception, this method raises that
         | 
| 17 | 
            +
              # exception. If yielding to the block does not raise an exception, this
         | 
| 18 | 
            +
              # method returns nil.
         | 
| 17 19 |  | 
| 18 | 
            -
              def self.vet | 
| 19 | 
            -
                 | 
| 20 | 
            +
              def self.vet
         | 
| 21 | 
            +
                yield
         | 
| 20 22 | 
             
                nil
         | 
| 21 | 
            -
              rescue ::StandardError
         | 
| 23 | 
            +
              rescue ::StandardError => e
         | 
| 22 24 | 
             
                # Like IO#puts, separate lines with a line feed character rather than $\.
         | 
| 23 | 
            -
                ["#{ | 
| 25 | 
            +
                ["#{e.class}: #{e.message}"].concat(e.backtrace || []).join "\n"
         | 
| 24 26 | 
             
              end
         | 
| 25 27 |  | 
| 26 28 | 
             
              ##
         | 
| 27 | 
            -
              #  | 
| 28 | 
            -
              #
         | 
| 29 | 
            -
              # In detail: This method sends call with one positional argument, an adder
         | 
| 30 | 
            -
              # object, to the object representing the block, director. This block can send
         | 
| 31 | 
            -
              # call messages to the adder to add objects representing the respective
         | 
| 32 | 
            -
              # blocks to the collection, sink. With each sending of call, the object to be
         | 
| 33 | 
            -
              # added will be either a proc (if call is sent with a block) or nil (if call
         | 
| 34 | 
            -
              # is sent without a block). If any non-block arguments are sent with the call
         | 
| 35 | 
            -
              # message, an exception is raised. To add the object, the adder sends a <<
         | 
| 36 | 
            -
              # message to sink with the object as the positional argument and no other
         | 
| 37 | 
            -
              # arguments. The adder returns nil in response to a call message if it
         | 
| 38 | 
            -
              # returns at all. This method returns sink.
         | 
| 39 | 
            -
              #
         | 
| 40 | 
            -
              # By default, sink is a new empty array.
         | 
| 41 | 
            -
             | 
| 42 | 
            -
              def self.add sink = [], &director
         | 
| 43 | 
            -
                director.call ::Kernel.lambda { |&prc|
         | 
| 44 | 
            -
                  sink << prc
         | 
| 45 | 
            -
                  nil
         | 
| 46 | 
            -
                }
         | 
| 47 | 
            -
                sink
         | 
| 48 | 
            -
              end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
              ##
         | 
| 51 | 
            -
              # A class for the contexts (the self objects) of tests.
         | 
| 29 | 
            +
              # A class for tests' disposable helper objects.
         | 
| 52 30 |  | 
| 53 | 
            -
              class  | 
| 31 | 
            +
              class Scratchpad
         | 
| 54 32 | 
             
              end
         | 
| 55 33 |  | 
| 56 34 | 
             
              ##
         | 
| 57 | 
            -
              # Runs  | 
| 35 | 
            +
              # Runs tests, writes any failure information to the output stream, and
         | 
| 58 36 | 
             
              # returns a boolean indicating whether there were no failures.
         | 
| 59 37 | 
             
              #
         | 
| 60 | 
            -
              # In detail: This method  | 
| 61 | 
            -
              #  | 
| 62 | 
            -
              #  | 
| 63 | 
            -
              #  | 
| 64 | 
            -
              #  | 
| 65 | 
            -
              #  | 
| 66 | 
            -
              #  | 
| 67 | 
            -
              # arguments  | 
| 68 | 
            -
              #  | 
| 69 | 
            -
              #  | 
| 70 | 
            -
              #  | 
| 71 | 
            -
              #  | 
| 72 | 
            -
              #  | 
| 38 | 
            +
              # In detail: This method sends an each message with a block to the sequence
         | 
| 39 | 
            +
              # of test objects, source, expecting it to yield to the block once for each
         | 
| 40 | 
            +
              # test object in the sequence with that test object as the first argument.
         | 
| 41 | 
            +
              # Each test involves creating a new scratchpad -- an instance of
         | 
| 42 | 
            +
              # Blackstart::Scratchpad -- and sending an instance_exec message to it with
         | 
| 43 | 
            +
              # the test object as a block argument, which converts the test object to a
         | 
| 44 | 
            +
              # proc via to_proc if it's not already a proc. The resulting proc is called
         | 
| 45 | 
            +
              # with no arguments; the self object is set to the scratchpad. A failure is
         | 
| 46 | 
            +
              # when the test (which includes any initial conversion to a proc) raises an
         | 
| 47 | 
            +
              # error: an exception whose class is StandardError or a descendant of
         | 
| 48 | 
            +
              # StandardError. After a failure, this method sends a puts message to the
         | 
| 49 | 
            +
              # output stream, ostream, with these arguments (positional, in order): a
         | 
| 50 | 
            +
              # string "FAILED TEST:"; the test object converted to a string via to_s; a
         | 
| 51 | 
            +
              # string "...ERROR:"; a string combining the class, message, and backtrace of
         | 
| 52 | 
            +
              # the error; and an empty string. If a test raises a non-error exception,
         | 
| 53 | 
            +
              # this method immediately raises that exception. After it has run all the
         | 
| 54 | 
            +
              # tests and handled any failures, this method returns false if there were
         | 
| 55 | 
            +
              # failures, true otherwise. This method immediately raises any exception
         | 
| 56 | 
            +
              # raised outside of a test.
         | 
| 73 57 | 
             
              #
         | 
| 74 58 | 
             
              # By default, ostream is $stdout.
         | 
| 75 59 |  | 
| 76 60 | 
             
              def self.run source, ostream = $stdout
         | 
| 77 61 | 
             
                success = true
         | 
| 78 | 
            -
                 | 
| 79 | 
            -
                  #  | 
| 80 | 
            -
                  #  | 
| 81 | 
            -
                  #  | 
| 82 | 
            -
                   | 
| 83 | 
            -
                   | 
| 84 | 
            -
                  if err_desc = vet { context.instance_exec(&prc) }
         | 
| 62 | 
            +
                source.each do |tst|
         | 
| 63 | 
            +
                  # Create the scratchpad outside the vet block so that this method will
         | 
| 64 | 
            +
                  # raise any exception that results instead of potentially treating it as
         | 
| 65 | 
            +
                  # a test failure.
         | 
| 66 | 
            +
                  scratchpad = Scratchpad.new
         | 
| 67 | 
            +
                  if err_desc = vet { scratchpad.instance_exec(&tst) }
         | 
| 85 68 | 
             
                    success = false
         | 
| 86 69 | 
             
                    ostream.puts "FAILED TEST:", tst.to_s, "...ERROR:", err_desc.to_s, ""
         | 
| 87 70 | 
             
                  end
         | 
    
        data/test/blackstart_test.rb
    CHANGED
    
    | @@ -6,9 +6,8 @@ require "blackstart" | |
| 6 6 | 
             
            # Blackstart should be a module.
         | 
| 7 7 | 
             
            fail "" unless Module.equal? Blackstart.class
         | 
| 8 8 |  | 
| 9 | 
            -
            # Blackstart should say it responds to vet | 
| 9 | 
            +
            # Blackstart should say it responds to vet and run.
         | 
| 10 10 | 
             
            fail "" unless Blackstart.respond_to? :vet
         | 
| 11 | 
            -
            fail "" unless Blackstart.respond_to? :add
         | 
| 12 11 | 
             
            fail "" unless Blackstart.respond_to? :run
         | 
| 13 12 |  | 
| 14 13 | 
             
            # Test Blackstart.vet:
         | 
| @@ -27,10 +26,14 @@ else | |
| 27 26 | 
             
              fail ""
         | 
| 28 27 | 
             
            end
         | 
| 29 28 |  | 
| 30 | 
            -
            # If  | 
| 31 | 
            -
            #  | 
| 29 | 
            +
            # If yielding to the block raises an error, Blackstart.vet should return a
         | 
| 30 | 
            +
            # string describing that error.
         | 
| 32 31 | 
             
            class ::Object
         | 
| 33 | 
            -
               | 
| 32 | 
            +
              # Should return an error description when no block is given.
         | 
| 33 | 
            +
              fail "" unless String.equal? Blackstart.vet.class
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              # Use frozen strings and backtrace arrays so that an exception will be raised
         | 
| 36 | 
            +
              # if they're modified.
         | 
| 34 37 |  | 
| 35 38 | 
             
              e_class = Class.new StandardError
         | 
| 36 39 | 
             
              def e_class.to_s
         | 
| @@ -41,158 +44,73 @@ class ::Object | |
| 41 44 | 
             
              def e_nil.backtrace
         | 
| 42 45 | 
             
                nil
         | 
| 43 46 | 
             
              end
         | 
| 44 | 
            -
               | 
| 47 | 
            +
              error = Blackstart.vet { ::Kernel.raise e_nil }
         | 
| 48 | 
            +
              fail "" unless String.equal? error.class
         | 
| 49 | 
            +
              fail "" unless "FakeError: a" == error
         | 
| 45 50 |  | 
| 46 51 | 
             
              e_empty = e_class.new "b".freeze
         | 
| 47 52 | 
             
              def e_empty.backtrace
         | 
| 48 53 | 
             
                [].freeze
         | 
| 49 54 | 
             
              end
         | 
| 50 | 
            -
               | 
| 55 | 
            +
              error = Blackstart.vet { ::Kernel.raise e_empty }
         | 
| 56 | 
            +
              fail "" unless String.equal? error.class
         | 
| 57 | 
            +
              fail "" unless "FakeError: b" == error
         | 
| 51 58 |  | 
| 52 59 | 
             
              e_full = e_class.new "c".freeze
         | 
| 53 60 | 
             
              def e_full.backtrace
         | 
| 54 61 | 
             
                ["d".freeze, "e".freeze].freeze
         | 
| 55 62 | 
             
              end
         | 
| 56 | 
            -
               | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
            # If sending call with no arguments to the block raises a non-StandardError
         | 
| 61 | 
            -
            # exception, Blackstart.vet should not rescue it.
         | 
| 62 | 
            -
            class ::Object
         | 
| 63 | 
            -
              non_standard_error = Class.new Exception
         | 
| 64 | 
            -
              begin
         | 
| 65 | 
            -
                Blackstart.vet { ::Kernel.raise non_standard_error }
         | 
| 66 | 
            -
              rescue non_standard_error
         | 
| 67 | 
            -
              else
         | 
| 68 | 
            -
                fail ""
         | 
| 69 | 
            -
              end
         | 
| 70 | 
            -
            end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
            # If sending call with no arguments to the block does not raise an exception,
         | 
| 73 | 
            -
            # Blackstart.vet should return nil.
         | 
| 74 | 
            -
            fail "" unless nil.equal? Blackstart.vet { nil }
         | 
| 75 | 
            -
            fail "" unless nil.equal? Blackstart.vet { true }
         | 
| 76 | 
            -
             | 
| 77 | 
            -
            # Test Blackstart.add:
         | 
| 78 | 
            -
             | 
| 79 | 
            -
            # When there is no block, Blackstart.add should raise an exception.
         | 
| 80 | 
            -
            begin
         | 
| 81 | 
            -
              Blackstart.add
         | 
| 82 | 
            -
            rescue NoMethodError
         | 
| 83 | 
            -
            else
         | 
| 84 | 
            -
              fail ""
         | 
| 85 | 
            -
            end
         | 
| 86 | 
            -
            begin
         | 
| 87 | 
            -
              Blackstart.add []
         | 
| 88 | 
            -
            rescue NoMethodError
         | 
| 89 | 
            -
            else
         | 
| 90 | 
            -
              fail ""
         | 
| 91 | 
            -
            end
         | 
| 92 | 
            -
             | 
| 93 | 
            -
            # Blackstart.add should raise an exception if superfluous arguments are sent.
         | 
| 94 | 
            -
            begin
         | 
| 95 | 
            -
              Blackstart.add [], "invalid"
         | 
| 96 | 
            -
            rescue ArgumentError
         | 
| 97 | 
            -
            else
         | 
| 98 | 
            -
              fail ""
         | 
| 99 | 
            -
            end
         | 
| 100 | 
            -
            begin
         | 
| 101 | 
            -
              Blackstart.add([], "invalid") {}
         | 
| 102 | 
            -
            rescue ArgumentError
         | 
| 103 | 
            -
            else
         | 
| 104 | 
            -
              fail ""
         | 
| 105 | 
            -
            end
         | 
| 106 | 
            -
             | 
| 107 | 
            -
            # When no collection is specified and the block does nothing, Blackstart.add
         | 
| 108 | 
            -
            # should return an empty array.
         | 
| 109 | 
            -
            fail "" unless [] == Blackstart.add {}
         | 
| 110 | 
            -
             | 
| 111 | 
            -
            # When a collection is specified and the block tries to add to the collection,
         | 
| 112 | 
            -
            # Blackstart.add should add corresponding procs or nil to the collection by
         | 
| 113 | 
            -
            # sending <<.
         | 
| 114 | 
            -
            class ::Object
         | 
| 115 | 
            -
              sink = [42]
         | 
| 116 | 
            -
              prc1 = proc {}
         | 
| 117 | 
            -
              prc2 = proc {}
         | 
| 118 | 
            -
              retval = Blackstart.add sink do |adder|
         | 
| 119 | 
            -
                adder.call(&prc1)
         | 
| 120 | 
            -
                adder.call(&prc2)
         | 
| 121 | 
            -
                adder.call
         | 
| 122 | 
            -
              end
         | 
| 123 | 
            -
              fail "" unless sink.equal? retval
         | 
| 124 | 
            -
              fail "" unless [42, prc1, prc2, nil] == sink
         | 
| 63 | 
            +
              error = Blackstart.vet { ::Kernel.raise e_full }
         | 
| 64 | 
            +
              fail "" unless String.equal? error.class
         | 
| 65 | 
            +
              fail "" unless "FakeError: c\nd\ne" == error
         | 
| 125 66 | 
             
            end
         | 
| 126 67 |  | 
| 127 | 
            -
            #  | 
| 128 | 
            -
            #  | 
| 68 | 
            +
            # If yielding to the block raises an error and that error raises an exception
         | 
| 69 | 
            +
            # when queried, Blackstart.vet should raise that second exception.
         | 
| 129 70 | 
             
            class ::Object
         | 
| 130 | 
            -
               | 
| 131 | 
            -
               | 
| 132 | 
            -
             | 
| 133 | 
            -
               | 
| 134 | 
            -
             | 
| 135 | 
            -
                fail ""
         | 
| 71 | 
            +
              e1 = StandardError.new
         | 
| 72 | 
            +
              e2_class = Class.new StandardError
         | 
| 73 | 
            +
              e1.instance_variable_set :@next_exception_class, e2_class
         | 
| 74 | 
            +
              def e1.message
         | 
| 75 | 
            +
                ::Kernel.raise @next_exception_class
         | 
| 136 76 | 
             
              end
         | 
| 137 77 | 
             
              begin
         | 
| 138 | 
            -
                Blackstart. | 
| 139 | 
            -
              rescue  | 
| 78 | 
            +
                Blackstart.vet { ::Kernel.raise e1 }
         | 
| 79 | 
            +
              rescue e2_class
         | 
| 140 80 | 
             
              else
         | 
| 141 81 | 
             
                fail ""
         | 
| 142 82 | 
             
              end
         | 
| 143 | 
            -
              fail "" unless 0 == sink.length
         | 
| 144 | 
            -
            end
         | 
| 145 | 
            -
             | 
| 146 | 
            -
            # When Blackstart.add's block sends call to the adder and it returns, nil
         | 
| 147 | 
            -
            # should be the object it returns.
         | 
| 148 | 
            -
            class ::Object
         | 
| 149 | 
            -
              retval_block = retval_no_block = true
         | 
| 150 | 
            -
              Blackstart.add do |adder|
         | 
| 151 | 
            -
                retval_block = adder.call {}
         | 
| 152 | 
            -
                retval_no_block = adder.call
         | 
| 153 | 
            -
              end
         | 
| 154 | 
            -
              fail "" unless nil.equal? retval_block
         | 
| 155 | 
            -
              fail "" unless nil.equal? retval_no_block
         | 
| 156 83 | 
             
            end
         | 
| 157 84 |  | 
| 158 | 
            -
            #  | 
| 159 | 
            -
            # should  | 
| 85 | 
            +
            # If yielding to the block raises a non-StandardError exception, Blackstart.vet
         | 
| 86 | 
            +
            # should raise it.
         | 
| 160 87 | 
             
            class ::Object
         | 
| 161 | 
            -
               | 
| 88 | 
            +
              non_standard_error = Class.new Exception
         | 
| 162 89 | 
             
              begin
         | 
| 163 | 
            -
                Blackstart. | 
| 164 | 
            -
              rescue
         | 
| 165 | 
            -
                fail "" unless e.equal? $!
         | 
| 90 | 
            +
                Blackstart.vet { ::Kernel.raise non_standard_error }
         | 
| 91 | 
            +
              rescue non_standard_error
         | 
| 166 92 | 
             
              else
         | 
| 167 93 | 
             
                fail ""
         | 
| 168 94 | 
             
              end
         | 
| 169 95 | 
             
            end
         | 
| 170 96 |  | 
| 171 | 
            -
            #  | 
| 172 | 
            -
            #  | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
              Blackstart.add(nil) { |adder| adder.call {} }
         | 
| 176 | 
            -
            rescue NoMethodError
         | 
| 177 | 
            -
            else
         | 
| 178 | 
            -
              fail ""
         | 
| 179 | 
            -
            end
         | 
| 97 | 
            +
            # If yielding to the block does not raise an exception, Blackstart.vet should
         | 
| 98 | 
            +
            # return nil. The object returned shouldn't matter.
         | 
| 99 | 
            +
            fail "" unless nil.equal? Blackstart.vet { nil }
         | 
| 100 | 
            +
            fail "" unless nil.equal? Blackstart.vet { true }
         | 
| 180 101 |  | 
| 181 | 
            -
            #  | 
| 182 | 
            -
            # Blackstart.add should append to a new array that gets returned.
         | 
| 183 | 
            -
            class ::Object
         | 
| 184 | 
            -
              prc = proc {}
         | 
| 185 | 
            -
              fail "" unless [prc] == Blackstart.add { |adder| adder.call(&prc) }
         | 
| 186 | 
            -
            end
         | 
| 102 | 
            +
            # Test Blackstart::Scratchpad:
         | 
| 187 103 |  | 
| 188 | 
            -
            #  | 
| 104 | 
            +
            # Blackstart::Scratchpad.new should return an instance of
         | 
| 105 | 
            +
            # Blackstart::Scratchpad, which implies that Blackstart::Scratchpad should be a
         | 
| 106 | 
            +
            # class.
         | 
| 107 | 
            +
            fail "" unless Blackstart::Scratchpad.equal? Blackstart::Scratchpad.new.class
         | 
| 189 108 |  | 
| 190 | 
            -
            # Blackstart:: | 
| 191 | 
            -
             | 
| 192 | 
            -
            fail "" unless Blackstart::Context.equal? Blackstart::Context.new.class
         | 
| 109 | 
            +
            # Blackstart::Scratchpad should not be frozen.
         | 
| 110 | 
            +
            fail "" if Blackstart::Scratchpad.frozen?
         | 
| 193 111 |  | 
| 194 | 
            -
            # A new  | 
| 195 | 
            -
            fail "" unless 0 == Blackstart:: | 
| 112 | 
            +
            # A new scratchpad should have no instance variables.
         | 
| 113 | 
            +
            fail "" unless 0 == Blackstart::Scratchpad.new.instance_variables.length
         | 
| 196 114 |  | 
| 197 115 | 
             
            # Test Blackstart.run:
         | 
| 198 116 |  | 
| @@ -200,7 +118,7 @@ fail "" unless 0 == Blackstart::Context.new.instance_variables.length | |
| 200 118 | 
             
            # return the appropriate object based on the behavior of the tests.
         | 
| 201 119 | 
             
            class ::Object
         | 
| 202 120 | 
             
              begin
         | 
| 203 | 
            -
                # Set $stdout to a test spy and  | 
| 121 | 
            +
                # Set $stdout to a test spy and restore it in the ensure clause.
         | 
| 204 122 | 
             
                original_stdout = $stdout
         | 
| 205 123 | 
             
                spy_stdout = $stdout.clone
         | 
| 206 124 | 
             
                spy_stdout.instance_variable_set :@_test_calls, stdout_calls = []
         | 
| @@ -210,155 +128,235 @@ class ::Object | |
| 210 128 | 
             
                end
         | 
| 211 129 | 
             
                $stdout = spy_stdout
         | 
| 212 130 |  | 
| 213 | 
            -
                 | 
| 214 | 
            -
                 | 
| 215 | 
            -
             | 
| 131 | 
            +
                invalid_ostream = Object.new
         | 
| 132 | 
            +
                def invalid_ostream.puts(*)
         | 
| 133 | 
            +
                  ::Kernel.raise ::NoMethodError
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                # When there are no tests, Blackstart.run should not notify the output
         | 
| 137 | 
            +
                # stream of any failures.
         | 
| 138 | 
            +
                source = []
         | 
| 139 | 
            +
                fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
         | 
| 216 140 | 
             
                fail "" unless true.equal? Blackstart.run(source, spy_stdout)
         | 
| 217 141 | 
             
                fail "" unless true.equal? Blackstart.run(source)
         | 
| 218 142 | 
             
                fail "" unless 0 == stdout_calls.length
         | 
| 219 143 |  | 
| 220 | 
            -
                #  | 
| 221 | 
            -
                 | 
| 222 | 
            -
                 | 
| 144 | 
            +
                # When there are tests but none raise exceptions, Blackstart.run should not
         | 
| 145 | 
            +
                # notify the output stream of any failures. The objects returned by the
         | 
| 146 | 
            +
                # test procs shouldn't affect Blackstart.run's behavior.
         | 
| 147 | 
            +
                source = [proc {}, proc { 42 }]
         | 
| 148 | 
            +
                fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
         | 
| 223 149 | 
             
                fail "" unless true.equal? Blackstart.run(source, spy_stdout)
         | 
| 224 150 | 
             
                fail "" unless true.equal? Blackstart.run(source)
         | 
| 225 151 | 
             
                fail "" unless 0 == stdout_calls.length
         | 
| 226 152 |  | 
| 227 | 
            -
                # Define some  | 
| 228 | 
            -
                 | 
| 153 | 
            +
                # Define some test objects. Freeze them, the strings to which they convert,
         | 
| 154 | 
            +
                # some exception details, and the strings to which the exception classes
         | 
| 155 | 
            +
                # convert to check that they don't get modified.
         | 
| 156 | 
            +
                e_class = Class.new StandardError do
         | 
| 157 | 
            +
                  def backtrace
         | 
| 158 | 
            +
                    ["line1".freeze, "line2".freeze].freeze
         | 
| 159 | 
            +
                  end
         | 
| 160 | 
            +
                end
         | 
| 229 161 | 
             
                def e_class.to_s
         | 
| 230 | 
            -
                  "FakeError"
         | 
| 162 | 
            +
                  "FakeError".freeze
         | 
| 231 163 | 
             
                end
         | 
| 232 | 
            -
                fail_prc1 = proc { ::Kernel.raise e_class, "fake message 1" }
         | 
| 164 | 
            +
                fail_prc1 = proc { ::Kernel.raise e_class, "fake message 1".freeze }
         | 
| 233 165 | 
             
                def fail_prc1.to_s
         | 
| 234 | 
            -
                  "to_s 1"
         | 
| 166 | 
            +
                  "to_s 1".freeze
         | 
| 235 167 | 
             
                end
         | 
| 168 | 
            +
                fail_prc1.freeze
         | 
| 169 | 
            +
                pass_prc = proc {}.freeze
         | 
| 236 170 | 
             
                fail_prc2 = Object.new # Not a Proc instance, but converts to one.
         | 
| 237 171 | 
             
                fail_prc2.instance_variable_set :@_test_e_class, e_class
         | 
| 238 172 | 
             
                def fail_prc2.to_proc
         | 
| 239 173 | 
             
                  e_class = @_test_e_class
         | 
| 240 | 
            -
                  ::Proc.new { ::Kernel.raise e_class, "fake message 2" }
         | 
| 174 | 
            +
                  ::Proc.new { ::Kernel.raise e_class, "fake message 2".freeze }.freeze
         | 
| 241 175 | 
             
                end
         | 
| 242 176 | 
             
                def fail_prc2.to_s
         | 
| 243 | 
            -
                  "to_s 2"
         | 
| 177 | 
            +
                  "to_s 2".freeze
         | 
| 244 178 | 
             
                end
         | 
| 245 | 
            -
                 | 
| 246 | 
            -
             | 
| 247 | 
            -
                 | 
| 248 | 
            -
             | 
| 249 | 
            -
             | 
| 250 | 
            -
                # Output stream that does not conform to the expected interface. (And in
         | 
| 251 | 
            -
                # general, the only exceptions Blackstart.run should rescue are
         | 
| 252 | 
            -
                # StandardError exceptions raised by tests.)
         | 
| 253 | 
            -
                begin
         | 
| 254 | 
            -
                  Blackstart.run source, Object.new
         | 
| 255 | 
            -
                rescue NoMethodError
         | 
| 256 | 
            -
                else
         | 
| 257 | 
            -
                  fail ""
         | 
| 179 | 
            +
                fail_prc2.freeze
         | 
| 180 | 
            +
                non_prc = Object.new # Does not convert to a proc.
         | 
| 181 | 
            +
                def non_prc.to_proc
         | 
| 182 | 
            +
                  ::Kernel.raise ::NoMethodError
         | 
| 258 183 | 
             
                end
         | 
| 259 | 
            -
                 | 
| 184 | 
            +
                def non_prc.to_s
         | 
| 185 | 
            +
                  "to_s 3".freeze
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
                non_prc.freeze
         | 
| 188 | 
            +
                bad_prc = Object.new # Pretends to convert to a proc but doesn't.
         | 
| 189 | 
            +
                def bad_prc.to_proc
         | 
| 190 | 
            +
                  0
         | 
| 191 | 
            +
                end
         | 
| 192 | 
            +
                def bad_prc.to_s
         | 
| 193 | 
            +
                  "to_s 4".freeze
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
                bad_prc.freeze
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                # A mix of tests that raise errors and tests that return. Freeze the array
         | 
| 198 | 
            +
                # to check that it's not being modified.
         | 
| 199 | 
            +
                source = [fail_prc1, pass_prc, fail_prc2, non_prc, bad_prc].freeze
         | 
| 260 200 |  | 
| 261 | 
            -
                #  | 
| 201 | 
            +
                # An output stream that conforms to the expected interface and does not
         | 
| 202 | 
            +
                # raise an exception should receive all failure information.
         | 
| 262 203 | 
             
                spy_ostream = Object.new
         | 
| 263 204 | 
             
                spy_ostream.instance_variable_set :@_test_calls, calls = []
         | 
| 264 205 | 
             
                def spy_ostream.puts *args
         | 
| 265 206 | 
             
                  @_test_calls << args
         | 
| 266 | 
            -
                  nil
         | 
| 207 | 
            +
                  # A real standard output stream would return nil here (and spy_stdout
         | 
| 208 | 
            +
                  # does that), but Blackstart.run's behavior should not be affected by the
         | 
| 209 | 
            +
                  # returned object. Returning this unusual object tests that.
         | 
| 210 | 
            +
                  ::Object.new
         | 
| 267 211 | 
             
                end
         | 
| 212 | 
            +
                # Freeze the output stream to check that it's not being modified.
         | 
| 213 | 
            +
                spy_ostream.freeze
         | 
| 268 214 | 
             
                fail "" unless false.equal? Blackstart.run(source, spy_ostream)
         | 
| 269 215 | 
             
                fail "" unless 0 == stdout_calls.length
         | 
| 270 | 
            -
                fail "" unless  | 
| 216 | 
            +
                fail "" unless 4 == calls.length
         | 
| 271 217 | 
             
                fail "" unless 5 == calls[0].length
         | 
| 218 | 
            +
                fail "" unless calls[0].all? { |o| ::String.equal? o.class }
         | 
| 272 219 | 
             
                fail "" unless "FAILED TEST:" == calls[0][0]
         | 
| 273 220 | 
             
                fail "" unless "to_s 1" == calls[0][1]
         | 
| 274 221 | 
             
                fail "" unless "...ERROR:" == calls[0][2]
         | 
| 275 | 
            -
                fail "" unless  | 
| 222 | 
            +
                fail "" unless "FakeError: fake message 1\nline1\nline2" == calls[0][3]
         | 
| 276 223 | 
             
                fail "" unless "" == calls[0][4]
         | 
| 277 224 | 
             
                fail "" unless 5 == calls[1].length
         | 
| 225 | 
            +
                fail "" unless calls[1].all? { |o| ::String.equal? o.class }
         | 
| 278 226 | 
             
                fail "" unless "FAILED TEST:" == calls[1][0]
         | 
| 279 227 | 
             
                fail "" unless "to_s 2" == calls[1][1]
         | 
| 280 228 | 
             
                fail "" unless "...ERROR:" == calls[1][2]
         | 
| 281 | 
            -
                fail "" unless  | 
| 229 | 
            +
                fail "" unless "FakeError: fake message 2\nline1\nline2" == calls[1][3]
         | 
| 282 230 | 
             
                fail "" unless "" == calls[1][4]
         | 
| 283 | 
            -
                 | 
| 284 | 
            -
             | 
| 285 | 
            -
                 | 
| 231 | 
            +
                fail "" unless 5 == calls[2].length
         | 
| 232 | 
            +
                fail "" unless calls[2].all? { |o| ::String.equal? o.class }
         | 
| 233 | 
            +
                fail "" unless "FAILED TEST:" == calls[2][0]
         | 
| 234 | 
            +
                fail "" unless "to_s 3" == calls[2][1]
         | 
| 235 | 
            +
                fail "" unless "...ERROR:" == calls[2][2]
         | 
| 236 | 
            +
                # (No need to check the error description.)
         | 
| 237 | 
            +
                fail "" unless "" == calls[2][4]
         | 
| 238 | 
            +
                fail "" unless 5 == calls[3].length
         | 
| 239 | 
            +
                fail "" unless calls[3].all? { |o| ::String.equal? o.class }
         | 
| 240 | 
            +
                fail "" unless "FAILED TEST:" == calls[3][0]
         | 
| 241 | 
            +
                fail "" unless "to_s 4" == calls[3][1]
         | 
| 242 | 
            +
                fail "" unless "...ERROR:" == calls[3][2]
         | 
| 243 | 
            +
                # (No need to check the error description.)
         | 
| 244 | 
            +
                fail "" unless "" == calls[3][4]
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                # The faked standard output stream.
         | 
| 286 247 | 
             
                stdout_calls.clear
         | 
| 287 248 | 
             
                fail "" unless false.equal? Blackstart.run(source)
         | 
| 288 | 
            -
                fail "" unless  | 
| 249 | 
            +
                fail "" unless 4 == stdout_calls.length
         | 
| 289 250 | 
             
                fail "" unless 5 == stdout_calls[0].length
         | 
| 251 | 
            +
                fail "" unless stdout_calls[0].all? { |o| ::String.equal? o.class }
         | 
| 290 252 | 
             
                fail "" unless "FAILED TEST:" == stdout_calls[0][0]
         | 
| 291 253 | 
             
                fail "" unless "to_s 1" == stdout_calls[0][1]
         | 
| 292 254 | 
             
                fail "" unless "...ERROR:" == stdout_calls[0][2]
         | 
| 293 | 
            -
                fail "" unless  | 
| 255 | 
            +
                fail "" unless "FakeError: fake message 1\nline1\nline2" ==
         | 
| 256 | 
            +
                  stdout_calls[0][3]
         | 
| 294 257 | 
             
                fail "" unless "" == stdout_calls[0][4]
         | 
| 295 258 | 
             
                fail "" unless 5 == stdout_calls[1].length
         | 
| 259 | 
            +
                fail "" unless stdout_calls[1].all? { |o| ::String.equal? o.class }
         | 
| 296 260 | 
             
                fail "" unless "FAILED TEST:" == stdout_calls[1][0]
         | 
| 297 261 | 
             
                fail "" unless "to_s 2" == stdout_calls[1][1]
         | 
| 298 262 | 
             
                fail "" unless "...ERROR:" == stdout_calls[1][2]
         | 
| 299 | 
            -
                fail "" unless  | 
| 263 | 
            +
                fail "" unless "FakeError: fake message 2\nline1\nline2" ==
         | 
| 264 | 
            +
                  stdout_calls[1][3]
         | 
| 300 265 | 
             
                fail "" unless "" == stdout_calls[1][4]
         | 
| 266 | 
            +
                fail "" unless 5 == stdout_calls[2].length
         | 
| 267 | 
            +
                fail "" unless stdout_calls[2].all? { |o| ::String.equal? o.class }
         | 
| 268 | 
            +
                fail "" unless "FAILED TEST:" == stdout_calls[2][0]
         | 
| 269 | 
            +
                fail "" unless "to_s 3" == stdout_calls[2][1]
         | 
| 270 | 
            +
                fail "" unless "...ERROR:" == stdout_calls[2][2]
         | 
| 271 | 
            +
                # (No need to check the error description.)
         | 
| 272 | 
            +
                fail "" unless "" == stdout_calls[2][4]
         | 
| 273 | 
            +
                fail "" unless 5 == stdout_calls[3].length
         | 
| 274 | 
            +
                fail "" unless stdout_calls[3].all? { |o| ::String.equal? o.class }
         | 
| 275 | 
            +
                fail "" unless "FAILED TEST:" == stdout_calls[3][0]
         | 
| 276 | 
            +
                fail "" unless "to_s 4" == stdout_calls[3][1]
         | 
| 277 | 
            +
                fail "" unless "...ERROR:" == stdout_calls[3][2]
         | 
| 278 | 
            +
                # (No need to check the error description.)
         | 
| 279 | 
            +
                fail "" unless "" == stdout_calls[3][4]
         | 
| 301 280 | 
             
                stdout_calls.clear
         | 
| 302 281 |  | 
| 303 | 
            -
                #  | 
| 304 | 
            -
                #  | 
| 305 | 
            -
                #  | 
| 306 | 
            -
                 | 
| 307 | 
            -
                 | 
| 282 | 
            +
                # An output stream that raises an exception when notified of a failure
         | 
| 283 | 
            +
                # should cause Blackstart.run to raise that exception and stop processing
         | 
| 284 | 
            +
                # tests. (In general, the only exceptions Blackstart.run should rescue are
         | 
| 285 | 
            +
                # StandardError exceptions raised by tests.)
         | 
| 286 | 
            +
                second_test_run = false
         | 
| 287 | 
            +
                source = [proc { ::Kernel.raise "" }, proc { second_test_run = true }]
         | 
| 308 288 | 
             
                begin
         | 
| 309 | 
            -
                  Blackstart.run source,  | 
| 310 | 
            -
                rescue  | 
| 289 | 
            +
                  Blackstart.run source, invalid_ostream
         | 
| 290 | 
            +
                rescue NoMethodError
         | 
| 311 291 | 
             
                else
         | 
| 312 292 | 
             
                  fail ""
         | 
| 313 293 | 
             
                end
         | 
| 294 | 
            +
                fail "" if second_test_run
         | 
| 314 295 | 
             
                fail "" unless 0 == stdout_calls.length
         | 
| 315 296 |  | 
| 316 | 
            -
                #  | 
| 317 | 
            -
                #  | 
| 318 | 
            -
                # exceptions  | 
| 297 | 
            +
                # When a test raises a non-StandardError exception, Blackstart.run should
         | 
| 298 | 
            +
                # raise that exception and stop processing tests. (In general, the only
         | 
| 299 | 
            +
                # exceptions Blackstart.run should rescue are StandardError exceptions
         | 
| 300 | 
            +
                # raised by tests.)
         | 
| 301 | 
            +
                second_test_run = false
         | 
| 302 | 
            +
                non_standard_error = Class.new Exception
         | 
| 303 | 
            +
                source = [proc { ::Kernel.raise non_standard_error },
         | 
| 304 | 
            +
                  proc { second_test_run = true }]
         | 
| 319 305 | 
             
                begin
         | 
| 320 | 
            -
                  Blackstart.run  | 
| 321 | 
            -
                rescue  | 
| 306 | 
            +
                  Blackstart.run source, spy_stdout
         | 
| 307 | 
            +
                rescue non_standard_error
         | 
| 322 308 | 
             
                else
         | 
| 323 309 | 
             
                  fail ""
         | 
| 324 310 | 
             
                end
         | 
| 311 | 
            +
                fail "" if second_test_run
         | 
| 325 312 | 
             
                fail "" unless 0 == stdout_calls.length
         | 
| 326 313 |  | 
| 327 | 
            -
                #  | 
| 328 | 
            -
                #  | 
| 329 | 
            -
                # ( | 
| 314 | 
            +
                # When the test sequence does not conform to the interface and raises an
         | 
| 315 | 
            +
                # exception during iteration, Blackstart.run should raise that exception.
         | 
| 316 | 
            +
                # (In general, the only exceptions Blackstart.run should rescue are
         | 
| 330 317 | 
             
                # StandardError exceptions raised by tests.)
         | 
| 331 | 
            -
                source =  | 
| 332 | 
            -
                 | 
| 333 | 
            -
                   | 
| 334 | 
            -
                rescue ArgumentError
         | 
| 335 | 
            -
                else
         | 
| 336 | 
            -
                  fail ""
         | 
| 318 | 
            +
                source = Object.new
         | 
| 319 | 
            +
                def source.each(*)
         | 
| 320 | 
            +
                  ::Kernel.raise ::NoMethodError
         | 
| 337 321 | 
             
                end
         | 
| 338 | 
            -
                source = [0]
         | 
| 339 322 | 
             
                begin
         | 
| 340 323 | 
             
                  Blackstart.run source, spy_stdout
         | 
| 341 | 
            -
                rescue  | 
| 324 | 
            +
                rescue NoMethodError
         | 
| 342 325 | 
             
                else
         | 
| 343 326 | 
             
                  fail ""
         | 
| 344 327 | 
             
                end
         | 
| 345 | 
            -
                 | 
| 346 | 
            -
             | 
| 347 | 
            -
             | 
| 328 | 
            +
                fail "" unless 0 == stdout_calls.length
         | 
| 329 | 
            +
             | 
| 330 | 
            +
                # When a test fails and converting the test object to a string raises an
         | 
| 331 | 
            +
                # exception, Blackstart.run should raise that exception and stop processing
         | 
| 332 | 
            +
                # tests. (In general, the only exceptions Blackstart.run should rescue are
         | 
| 333 | 
            +
                # StandardError exceptions raised by tests.)
         | 
| 334 | 
            +
                second_test_run = false
         | 
| 335 | 
            +
                no_desc = proc { ::Kernel.raise "" }
         | 
| 336 | 
            +
                def no_desc.to_s
         | 
| 337 | 
            +
                  ::Kernel.raise ::NoMethodError
         | 
| 348 338 | 
             
                end
         | 
| 349 | 
            -
                source = [ | 
| 339 | 
            +
                source = [no_desc, proc { second_test_run = true }]
         | 
| 350 340 | 
             
                begin
         | 
| 351 341 | 
             
                  Blackstart.run source, spy_stdout
         | 
| 352 | 
            -
                rescue  | 
| 342 | 
            +
                rescue NoMethodError
         | 
| 353 343 | 
             
                else
         | 
| 354 344 | 
             
                  fail ""
         | 
| 355 345 | 
             
                end
         | 
| 346 | 
            +
                fail "" if second_test_run
         | 
| 356 347 | 
             
                fail "" unless 0 == stdout_calls.length
         | 
| 357 348 | 
             
              ensure
         | 
| 358 349 | 
             
                $stdout = original_stdout
         | 
| 359 350 | 
             
              end
         | 
| 360 351 | 
             
            end
         | 
| 361 352 |  | 
| 353 | 
            +
            # Blackstart.run should run tests in order.
         | 
| 354 | 
            +
            class ::Object
         | 
| 355 | 
            +
              data = []
         | 
| 356 | 
            +
              Blackstart.run [proc { data << 1 }, proc { data << 2 }], nil
         | 
| 357 | 
            +
              fail "" unless [1, 2] == data
         | 
| 358 | 
            +
            end
         | 
| 359 | 
            +
             | 
| 362 360 | 
             
            # Blackstart.run should call each test proc with no arguments.
         | 
| 363 361 | 
             
            class ::Object
         | 
| 364 362 | 
             
              args = nil
         | 
| @@ -369,14 +367,55 @@ class ::Object | |
| 369 367 | 
             
              fail "" unless [] == args
         | 
| 370 368 | 
             
            end
         | 
| 371 369 |  | 
| 372 | 
            -
            # Blackstart.run should  | 
| 373 | 
            -
            #  | 
| 370 | 
            +
            # Blackstart.run should work with any source that responds to each. The object
         | 
| 371 | 
            +
            # returned in response to the each message should not affect Blackstart.run's
         | 
| 372 | 
            +
            # behavior.
         | 
| 374 373 | 
             
            class ::Object
         | 
| 375 | 
            -
               | 
| 376 | 
            -
               | 
| 377 | 
            -
             | 
| 378 | 
            -
             | 
| 379 | 
            -
             | 
| 374 | 
            +
              source = Object.new
         | 
| 375 | 
            +
              def source.each
         | 
| 376 | 
            +
                ran = false
         | 
| 377 | 
            +
                yield proc { ran = true }
         | 
| 378 | 
            +
                @ran = ran
         | 
| 379 | 
            +
                self
         | 
| 380 | 
            +
              end
         | 
| 381 | 
            +
              fail "" unless true.equal? Blackstart.run(source, nil)
         | 
| 382 | 
            +
              fail "" unless source.instance_variable_get :@ran
         | 
| 383 | 
            +
             | 
| 384 | 
            +
              source = Object.new
         | 
| 385 | 
            +
              def source.each
         | 
| 386 | 
            +
                yield proc {}
         | 
| 387 | 
            +
                nil
         | 
| 388 | 
            +
              end
         | 
| 389 | 
            +
              fail "" unless true.equal? Blackstart.run(source, nil)
         | 
| 390 | 
            +
            end
         | 
| 391 | 
            +
             | 
| 392 | 
            +
            # Blackstart.run should send exactly one each message to the source.
         | 
| 393 | 
            +
            class ::Object
         | 
| 394 | 
            +
              source = Object.new
         | 
| 395 | 
            +
              source.instance_variable_set :@count, 0
         | 
| 396 | 
            +
              def source.each
         | 
| 397 | 
            +
                @count += 1
         | 
| 398 | 
            +
                self
         | 
| 399 | 
            +
              end
         | 
| 400 | 
            +
              Blackstart.run source, nil
         | 
| 401 | 
            +
              fail "" unless 1 == source.instance_variable_get(:@count)
         | 
| 402 | 
            +
            end
         | 
| 403 | 
            +
             | 
| 404 | 
            +
            # When Blackstart.run calls a test proc, the self object should be a unique
         | 
| 405 | 
            +
            # instance of Blackstart::Scratchpad.
         | 
| 406 | 
            +
            class ::Object
         | 
| 407 | 
            +
              sp1 = sp2 = nil
         | 
| 408 | 
            +
              Blackstart.run [proc { sp1 = self }, proc { sp2 = self }], nil
         | 
| 409 | 
            +
              fail "" unless Blackstart::Scratchpad.equal? sp1.class
         | 
| 410 | 
            +
              fail "" unless Blackstart::Scratchpad.equal? sp2.class
         | 
| 411 | 
            +
              fail "" if sp1.equal? sp2
         | 
| 412 | 
            +
            end
         | 
| 413 | 
            +
             | 
| 414 | 
            +
            # When Blackstart.run runs a test, the scratchpad should not be frozen.
         | 
| 415 | 
            +
            class ::Object
         | 
| 416 | 
            +
              frozen = true
         | 
| 417 | 
            +
              Blackstart.run [proc { frozen = frozen? }], nil
         | 
| 418 | 
            +
              fail "" if frozen
         | 
| 380 419 | 
             
            end
         | 
| 381 420 |  | 
| 382 421 | 
             
            # Blackstart.run should raise an exception if too few or too many non-block
         | 
| @@ -387,11 +426,34 @@ rescue ArgumentError | |
| 387 426 | 
             
            else
         | 
| 388 427 | 
             
              fail ""
         | 
| 389 428 | 
             
            end
         | 
| 390 | 
            -
             | 
| 391 | 
            -
               | 
| 392 | 
            -
             | 
| 393 | 
            -
             | 
| 394 | 
            -
               | 
| 429 | 
            +
            class ::Object
         | 
| 430 | 
            +
              source = []
         | 
| 431 | 
            +
              begin
         | 
| 432 | 
            +
                Blackstart.run source, nil, nil
         | 
| 433 | 
            +
              rescue ArgumentError
         | 
| 434 | 
            +
              else
         | 
| 435 | 
            +
                fail ""
         | 
| 436 | 
            +
              end
         | 
| 437 | 
            +
            end
         | 
| 438 | 
            +
             | 
| 439 | 
            +
            # The following section modifies the state of the library, so it must be run
         | 
| 440 | 
            +
            # after all the ordinary tests.
         | 
| 441 | 
            +
             | 
| 442 | 
            +
            # Blackstart.run should run each test by sending instance_exec to a scratchpad.
         | 
| 443 | 
            +
            class Blackstart::Scratchpad
         | 
| 444 | 
            +
              def foo
         | 
| 445 | 
            +
                2
         | 
| 446 | 
            +
              end
         | 
| 447 | 
            +
             | 
| 448 | 
            +
              def instance_exec(*)
         | 
| 449 | 
            +
                @ivar = 1
         | 
| 450 | 
            +
                super
         | 
| 451 | 
            +
              end
         | 
| 452 | 
            +
            end
         | 
| 453 | 
            +
            class ::Object
         | 
| 454 | 
            +
              result = nil
         | 
| 455 | 
            +
              Blackstart.run [proc { result = [@ivar, foo()] }], nil
         | 
| 456 | 
            +
              fail "" unless [1, 2] == result
         | 
| 395 457 | 
             
            end
         | 
| 396 458 |  | 
| 397 459 | 
             
            # If this message doesn't get written, there was a problem.
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: blackstart
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 4 | 
            +
              hash: 7
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 0
         | 
| 8 | 
            -
              -  | 
| 8 | 
            +
              - 6
         | 
| 9 9 | 
             
              - 0
         | 
| 10 | 
            -
              version: 0. | 
| 10 | 
            +
              version: 0.6.0
         | 
| 11 11 | 
             
            platform: ruby
         | 
| 12 12 | 
             
            authors: 
         | 
| 13 13 | 
             
            - Aaron Beckerman
         | 
| @@ -15,7 +15,7 @@ autorequire: | |
| 15 15 | 
             
            bindir: bin
         | 
| 16 16 | 
             
            cert_chain: []
         | 
| 17 17 |  | 
| 18 | 
            -
            date:  | 
| 18 | 
            +
            date: 2025-05-16 00:00:00 -07:00
         | 
| 19 19 | 
             
            default_executable: 
         | 
| 20 20 | 
             
            dependencies: []
         | 
| 21 21 |  | 
| @@ -30,7 +30,6 @@ extra_rdoc_files: [] | |
| 30 30 | 
             
            files: 
         | 
| 31 31 | 
             
            - LICENSE.txt
         | 
| 32 32 | 
             
            - README.txt
         | 
| 33 | 
            -
            - blackstart.gemspec
         | 
| 34 33 | 
             
            - lib/blackstart.rb
         | 
| 35 34 | 
             
            - test/blackstart_test.rb
         | 
| 36 35 | 
             
            has_rdoc: true
         | 
| @@ -69,5 +68,5 @@ rubygems_version: 1.6.2 | |
| 69 68 | 
             
            signing_key: 
         | 
| 70 69 | 
             
            specification_version: 3
         | 
| 71 70 | 
             
            summary: A small, subdued library for automated testing.
         | 
| 72 | 
            -
            test_files: 
         | 
| 73 | 
            -
             | 
| 71 | 
            +
            test_files: []
         | 
| 72 | 
            +
             | 
    
        data/blackstart.gemspec
    DELETED
    
    | @@ -1,11 +0,0 @@ | |
| 1 | 
            -
            Gem::Specification.new do |s|
         | 
| 2 | 
            -
              s.name = "blackstart"
         | 
| 3 | 
            -
              s.version = "0.2.0"
         | 
| 4 | 
            -
              s.authors = ["Aaron Beckerman"]
         | 
| 5 | 
            -
              s.summary = "A small, subdued library for automated testing."
         | 
| 6 | 
            -
              s.licenses = ["MIT"]
         | 
| 7 | 
            -
              s.required_ruby_version = ">= 1.8.7"
         | 
| 8 | 
            -
              s.files = ["LICENSE.txt", "README.txt", "blackstart.gemspec",
         | 
| 9 | 
            -
                "lib/blackstart.rb", "test/blackstart_test.rb"]
         | 
| 10 | 
            -
              s.test_files = ["test/blackstart_test.rb"]
         | 
| 11 | 
            -
            end
         |