=======
CHANGES
=======


Release 0.11.0
--------------

* [change] 'spec()' is now NOT obsoleted.

* [change] 'spec()' is now available as function decorator.
  ex::

    class FooTest(unittest.TestCase):
      def test_method1(self)
        @spec("1+1 should be 2")
	def _():
	  ok (1+1) == 2
        @spec("1-1 should be 0")
	def _():
	  ok (1-1) == 0

* [enhance] New assertions: not_file(), not_dir() and not_exist().
  ex::

    ok (".").not_file()         # same as NG (".").is_file()
    ok (__file__).not_dir()     # same as NG (__file__).is_dir()
    ok ("foobar").not_exist()   # same as NG ("foobar").exists()

* [enhance] New assertion: not_match().
  ex::

    ok ("SOS").not_match(r"\d+")  # same as NG ("SOS").matches(r"\d+")

* [enhance] Global provider/releaser functions can take 'self' argument.
  ex::

    def provide_logname(self):
        self._LOGNAME = os.getenv('LOGNAME')
	os.environ['LOGNAME'] = "Haruhi"
	return os.environ['LOGNAME']

    def release_logname(self, value):
        os.environ['LOGNAME'] = self._LOGNAME

* [change] Change not to ignore test classes which name starts with '_'.

* [change] (internal) Move some utility functions to 'util' module.

* [change] (internal) Move '_Context' and '_RunnableContext' classes into 'util' module.

* [change] (internal) Move 'Color' class into 'util' module

* [change] (internal) Remove 'OUT' variable in 'Reporter' class

* [change] (internal) Move 'TARGET_PATTERN' variable to 'config'

* [bugfix] Fix to clear ImportError after trying to import unittest2


Release 0.10.0
--------------

* [change] 'oktest.spec()' is obsoleted completely.
  It will print warning message if you use it.

* [change] 'oktest.helper' module is renamed to 'oktest.util'.
  ('oktest.helper' is still available for backward compabibility.)

* [enhance] Add 'oktest.main()' which is a replacement of 'oktest.run()'.
  Using 'oktest.main()' instead of 'oktest.run()', command options are available.
  ex::

    ## for example:
    $ python test/foobar_test.py -sp -f test='*keyword*'
    ## is almost same as:
    $ python -m oktest test/foobar_test.py -sp -f test='*keyword*'

* [enhance] Add 'oktest.fail(message)' which is same as 'unittest.fail(message)'.
  ex::

    from oktest import fail
    fail("not impelmented yet")    # will raise AssertionError

* [enhance] (Experimental) Add '@todo' decorator which is equivarent to
  '@unittest.expectedFailure'.
  ex::

    from oktest import ok, test, todo

    def add(x, y):
        return 0      # not implemented yet!

    class AddTest(unittest.TestCase):
        @test("returns sum of arguments.")
	@todo         # equivarent to @unittest.expectedFailure
        def _(self):
	    ok (10, 20) == 30   ## will be failed expectedly
	                        ## (because not implemented yet)

  Expected failure of assertion is reported as '[TODO]', not '[Failed]'.

* [enhance] (Experimental) Test context supported.
  It helps you to describe specification in structured style.
  ex::

    from oktest import ok, test
    from oktest import subject, situation

    class SampleTestCase(unittest.TestCase):
      SUBJECT = "class 'Sample'"

      with subject("method1()"):

        with situation("when condition:"):

          @test("spec1")
            def _(self):
            ...

          @test("spec2")
          def _(self):
            ...

        with situation("else:"):

          @test("spec3")
          def _(self):
            ...

    if __name__ == '__main__':
        import oktest
        oktest.main()

  Output exmple::

    $ python test/example_test.py
    * class 'Sample'
      + method1()
        + when condition:
	  - [passed] spec1
	  - [passed] spec2
        + else:
	  - [passed] spec3
    ## total:3, passed:3, failed:0, error:0, skipped:0, todo:0  (0.000 sec)

* [change] Output is changed.

    ###
    ### previous version
    ###
    $ python test/foo_test.py
    * FooTest
      - [ok] test1
      - [ok] test2
      - [skipped] test3
    ## total:3, passed:2, failed:0, error:0, skipped:1   (elapsed 0.000)

    ###
    ### in this release
    ###
    $ python test/foo_test.py
    * FooTest
      - [passed] test1
      - [passed] test2
      - [skipped] test3 (reason: REASON)
    ## total:3, passed:2, failed:0, error:0, skipped:1, todo:0  (0.000 sec)


Release 0.9.0
-------------

* New '@test' decorator provided. It is simple but very powerful.
  Using @test decorator, you can write test description in free text
  instead of test method.
  ex::

    class FooTest(unittest.TestCase):

        def test_1_plus_1_should_be_2(self):  # not cool...
            self.assertEqual(2, 1+1)

        @test("1 + 1 should be 2")    # cool! easy to read & write!
        def _(self):
            self.assertEqual(2, 1+1)

* Fixture injection support by '@test' decorator.
  Arguments of test method are regarded as fixture names and
  they are injected by @test decorator automatically.
  Instance methods or global functions which name is 'provide_xxxx' are
  regarded as fixture provider (or builder) for fixture 'xxxx'.
  ex::

    class SosTest(unittest.TestCase):

        ##
        ## fixture providers.
        ##
        def provide_member1(self):
            return {"name": "Haruhi"}

        def provide_member2(self):
            return {"name": "Kyon"}

        ##
        ## fixture releaser (optional)
        ##
        def release_member1(self, value):
            assert value == {"name": "Haruhi"}

        ##
        ## testcase which requires 'member1' and 'member2' fixtures.
        ##
        @test("validate member's names")
        def _(self, member1, member2):
            assert member1["name"] == "Haruhi"
            assert member2["name"] == "Kyon"

  Dependencies between fixtures are resolved automatically.
  ex::

    class BarTest(unittest.TestCase):

        ##
        ## for example:
        ## - Fixture 'a' depends on 'b' and 'c'.
        ## - Fixture 'c' depends on 'd'.
        ##
        def provide_a(b, c):  return b + c + ["A"]
        def provide_b():      return ["B"]
        def provide_c(d):     return d + ["C"]
        def provide_d():      reutrn ["D"]

        ##
        ## Dependencies between fixtures are solved automatically.
        ##
        @test("dependency test")
        def _(self, a):
            assert a == ["B", "D", "C", "A"]

  If loop exists in dependency then @test reports error.

  If you want to integrate with other fixture library, see the following
  example::

      class MyFixtureManager(object):
          def __init__(self):
              self.values = { "x": 100, "y": 200 }
          def provide(self, name):
              return self.values[name]
          def release(self, name, value):
              pass

      oktest.fixure_manager = MyFixtureResolver()

* Supports command-line interface to execute test scripts.
  ex::

    ## run test scripts except foo_*.py in plain style
    $ python -m oktest -x 'foo_*.py' -sp tests/*_test.py
    ## run test scripts in 'tests' dir with pattern '*_test.py'
    $ python -m oktest -p '*_test.py' tests
    ## filter by class name
    $ python -m oktest -f class='ClassName*' tests
    ## filter by test method name
    $ python -m oktest -f test='*keyword*' tests
    ## filter by user-defined option added by @test decorator
    $ python -m oktest -f tag='*value*' tests

  Try ``python -m oktest -h`` for details about command-line options.

* Reporting style is changed. Oktest now provides three reporing styles.
  - plain (similar to unittest)
  - simple
  - verbose (default)
  All of these styles are colored to emphasize errors.
  If you want change reporting style, specify ``-r`` option in command-line.

* New assertion method ``ok(x).attr(name, value)`` to check attribute.
  ex::

    d = datetime.date(2000, 12, 31)
    ok (d).attr('year', 2000).attr('month', 12).attr('date', 31)

* New assertion method ``ok(x).length(n)``. This is same as
  ``ok(len(x)) == n``, but it is useful when chaining assertion methods.
  ex::

    ok (func()).is_a(tuple).length(2)

* New feature``ok().should`` helps you to check boolean method.
  ex::

    ## same as ok ("Haruhi".startswith("Haru")) == True
    ok ("Haruhi").should.startswith("Haru")

    ## same as ok ("Haruhi".isupper()) == False
    ok ("Haruhi").should_not.isupper()

* 'ok(str1) == str2' displays diff if text1 != text2, even when using
  with unittest module. In previous version, text diff is displayed
  only when using oktest.run().
  If you are unittest user and using Python < 2.7, use 'ok(str1) == str2'
  instead of 'self.assertEqual(str2, str1)' to display text diff.

* Assertion ``raises()`` supports regular expression to check error message.

    def fn(): raise ValueError("ERROR-123")
    ok (fn).raises(ValueError, re.compile(r'^[A-Z]+-\d+$'))

* Helper functions in oktest.dummy module are now available as decorator.
  This is useful when you must use Python 2.4::

    from oktest.dummy import dummy_io
    ## for Python 2.4
    @dummy_io("SOS")
    def d_io():
        assert sys.stdin.read() == "SOS"
        print("Haruhi")
    sout, serr = d_io
    assert sout == "Haruhi\n"
    ## for Python 2.5 or later
    with dummy_io("SOS") as d_io:
        assert sys.stdin.read() == "SOS"
        print("Haruhi")
    sout, serr = d_io
    assert sout == "Haruhi\n"

* 'AssertionObject.expected' is renamed to 'AssertionObject.boolean'.
  You should update your custom assertion definition.
  ex::

    import oktest
    @oktest.assertion
    def startswith(self, arg):
        boolean = self.target.startswith(arg)
        #if boolean == self.expected:      # obsolete
        if boolean == self.boolean:
            return True
        self.failed("%r.startswith(%r) : failed." % (self.target, arg))

* ``oktest.run()`` is changed to return number of failures and errors
  of tests.::

    sys.exit(oktest.run())   # status code == number of failures and errors

* ``before_each()`` and ``after_each()`` are now non-supported.
  Use ``before()`` and ``after()`` intead.

* (Experimental) New function ``NOT()`` provided which is same as ``NG()``.

* (Experimental) ``skip()`` and ``@skip.when()`` are provided to skip tests::

    from oktest import skip

    class FooTest(unittest.TestCase):

        def test_1(self):
            if sys.version.startswith('2.'):
                reason = "not available on Python 2.x"
                skip(reason)    # raises SkipTest
            ...

        @skip.when(sys.version.startswith('2.'), "not available on Python 2.x")
        def test_2(self):
            ...

  If you want to use @skip.when with @test decorator, see the folloing::

     ## OK
     @test("description")
     @skip.when(condition, "reason")
     def _(self):
         ...

     ## NG
     @skip.when(condition, "reason")
     @test("description")
     def _(self):
         ...


Release 0.8.0
-------------

* add ``NG()`` which is same as not_ok().

* enhanced to proive egg files for Python 3.

* enhanced to support assertion method chaining. ::

    ok ("sos".upper()).is_a(str).matches(r'^[A-Z]+$') == "SOS"

* ``ok().matches()`` can take flag parameter which is passed to re.compile().

    ok ("\nSOS\n").matches(r'^[A-Z]+$', re.M)
    ## same as:
    #ok("\nSOS\n").matches(r.compile(r'^[A-Z]$', re.M))

* enhance helper methods to be available without with-statement.
  (this is necessary for Python 2.4 which is default version on CentOS.)

    from oktest.helper import chdir

    def fn():
      ok (os.getcwd()) == "/tmp"
    chdir("/tmp").run(fn)
    ## this is same as:
    #with chdir("/tmp"):
    #  ok (os.getcwd()) == "/tmp"

    from oktest.dummy import dummy_file

    def fn():
      ok ("A.txt").is_file()
      ok (open("A.txt").read()) == "SOS"
    dummy_file("A.txt", "SOS").run(fun)
    ## this is same as:
    #with dummy_file("A.txt", "SOS"):
    #  ok (open("A.txt").read()) == "SOS"

* ``spec()`` now checks environment variable $SPEC.
  This is useful to filter test cases.

    ## test script
    from oktest import oktest, run
    class StrTest(object):
      def test_upper(self):
        if spec("returns upper case string"):
          ok ("sos".upper()) == "SOS"
        if spec("doesn't change non-alphabetics"):
          ok ("sos123<>".upper()) == "SOS123<>"
    if __name__ == "__main__":
      run()

    ## terminal
    $ SPEC="returns upper case string" python test1.py

* fix ``oktest.run()`` to print correct traceback if ok() is called from
  nested function.

* fix content of README.txt.



Release 0.7.0
-------------

* enhanced to allow users to define custom assertion functions. ::

    import oktest
    from oktest import ok
    #
    @oktest.assertion
    def startswith(self, arg):
        boolean = self.target.startswith(arg)
        if boolean == self.expected:
            return True
        self.failed("%r.startswith(%r) : failed." % (self.target, arg))
    #
    ok ("Sasaki").startswith("Sas")

* rename 'ok().hasattr()' to 'ok().has_attr()'.
  (but old name is also available for backward compatibility.)

* change 'chdir()' to take a function as 2nd argument. ::

    def f():
      ... do_something ...
    chdir('build', f)

    # The above is same as:
    with chdir('build'):
      ... do_something ...

* add document of 'oktest.helper.dummy_io()'. ::

    with dummy_io("SOS") as io:
        assert sys.stdin.read() == "SOS"
        print("Haruhi")
    assert io.stdout == "Haruhi\n"
    assert io.stderr == ""

* fix 'oktest.tracer.Call#__repr__()' to change output according to
  whether '==' is called or not. This is aimed to make output of
  'ok(tr[0]) == [...]' to be more readable.

* change 'Runner#run()' to skip AssertionError if it is raised by
  'assert ....' and not 'ok() == ...'.


Release 0.6.0
-------------

* enhanced to add 'oktest.tracer.Tracer' class. see README for details.
* change 'run()' to sort classes order by lineno in source file.
* change default argument of 'run()' from '.*Test(Case)$' to
  '.*(Test|TestCase|_TC)$'.


Release 0.5.0
-------------

* change default argument of 'run()' to '.*Test(Case)$'.
* enhanced to report untested AssertionObject.
* new helper function 'spec()' which describes test specification.
* new helper function 'dummy_values()' which changes dictionary temporarily.
* new helper function 'dummy_attrs()' which changes object's attributes temporarily.
* 'TestCaseRunner' class is renamed to 'TestRunner'.
* (undocumented) new helper function 'dummy_environ_vars()'.
* (undocumented) new helper function 'using()'.
* (uncodumented) add rm_rf()


Release 0.4.0
-------------

* enhanced to support 'ok (x).in_delta(y, d)' which raises assertion exception
  unless y-d < x < y+d.
* change test script to support Python 2.7 and 3.x.
* fixed several bugs.


Release 0.3.0
-------------

* enhanced 'ok (s1) == s2' to display unified diff (diff -u)
* changed to call 'before()/after()' instead of 'before_each()/after_each()'
  (currently 'before_each()/after_each()' is also enabled but they will be
   disabled in the future)
* improved compatibility with unittest module
* (internal) 'ValueObject' class is renamed to 'AssertionObject'
* (internal) 'Reporter#before_each()' and '#after_each()' are renamed into
  '#before()' and '#after()'


Release 0.2.2
-------------

* enhanced to set 'f.exception' after 'ok (f).raises(Exception)'
  to get raised exception object
* changed to flush output after '.'/'f'/'E' printed
* change to get exception message by 'str(ex)' instead of 'ex.message'


Release 0.2.1
-------------

* fix 'REAMDE.txt'
* fix 'setup.py'


Release 0.2.0
-------------

* public release
