Commits

Olof Bjarnason committed 5328018

Emptied repository and added moved message

  • Participants
  • Parent commits e1cb3f8

Comments (0)

Files changed (51)

File License.txt

-Copyright (c) 2009-2012
-Olof Bjarnason
-Fredrik Wendt
-Krunoslav Saho
-Samuel Ytterbrink
-Rafael Capucho
-Ilian Iliev
-Henrik Bohre
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-

File Moved to github.txt

+Hi there!
+
+The objarni/pytddmon repository has moved to github.
+
+It is also using git instead of hg.
+
+https://github.com/objarni/pytddmon
+
+See you there!
+
+/Olof

File Readme.txt

-** pytddmon - continous unit testing in Python **
-
-
-Latest news
------------
-Read the pytddmon blog & more documentation at
-
-http://pytddmon.org
-
-
-
-How to use
-----------
-
-Copy src/pytddmon.py to your projects' root folder. From there, type:
-
-python pytddmon.py
-
-
-
-License
--------
-
-See License.txt.
-
-
-
-Folder structure
-----------------
-src/       contains pytddmon.py
-src/tests  unit tests for pytddmon.py
-logo/      pytddmon logo
-systest/   contains systest.py, lots of folders and a Readme.txt
-           (used for end-to-end regression testing pytddmon.py)

File logo/pytddmon.svg

-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="200"
-   height="200"
-   id="svg2"
-   sodipodi:version="0.32"
-   inkscape:version="0.48.1 "
-   version="1.0"
-   sodipodi:docname="pytddmon.svg"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape"
-   inkscape:export-filename="C:\Users\Olof.Bjarnason\Desktop\pytddmon.png"
-   inkscape:export-xdpi="21.6"
-   inkscape:export-ydpi="21.6">
-  <defs
-     id="defs4">
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3382">
-      <stop
-         style="stop-color:#800000;stop-opacity:1"
-         offset="0"
-         id="stop3384" />
-      <stop
-         style="stop-color:#cccccc;stop-opacity:1"
-         offset="1"
-         id="stop3386" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3374">
-      <stop
-         style="stop-color:#1c561e;stop-opacity:1"
-         offset="0"
-         id="stop3376" />
-      <stop
-         style="stop-color:#b3b3b3;stop-opacity:1"
-         offset="1"
-         id="stop3378" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3346">
-      <stop
-         style="stop-color:#666666;stop-opacity:1;"
-         offset="0"
-         id="stop3348" />
-      <stop
-         style="stop-color:#666666;stop-opacity:0;"
-         offset="1"
-         id="stop3350" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3338">
-      <stop
-         style="stop-color:#1c1c1c;stop-opacity:1"
-         offset="0"
-         id="stop3340" />
-      <stop
-         style="stop-color:#ffffff;stop-opacity:1"
-         offset="1"
-         id="stop3342" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3178">
-      <stop
-         style="stop-color:#008000;stop-opacity:1"
-         offset="0"
-         id="stop3180" />
-      <stop
-         style="stop-color:#00ff00;stop-opacity:1"
-         offset="1"
-         id="stop3182" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3170">
-      <stop
-         style="stop-color:#d40000;stop-opacity:1;"
-         offset="0"
-         id="stop3172" />
-      <stop
-         style="stop-color:#ffaaaa;stop-opacity:1"
-         offset="1"
-         id="stop3174" />
-    </linearGradient>
-    <inkscape:perspective
-       sodipodi:type="inkscape:persp3d"
-       inkscape:vp_x="0 : 526.18109 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_z="744.09448 : 526.18109 : 1"
-       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
-       id="perspective10" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3170"
-       id="linearGradient3176"
-       x1="263.23544"
-       y1="454.2395"
-       x2="228.89024"
-       y2="393.63034"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(0,4)" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3178"
-       id="linearGradient3184"
-       x1="328.89536"
-       y1="452.21921"
-       x2="282.42831"
-       y2="390.59988"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(3.0304576,5.0101525)" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3338"
-       id="linearGradient3344"
-       x1="208.02345"
-       y1="522.42053"
-       x2="299.79645"
-       y2="394.28171"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3346"
-       id="linearGradient3352"
-       x1="178.83714"
-       y1="486.41888"
-       x2="260.62125"
-       y2="371.02631"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3374"
-       id="linearGradient3380"
-       x1="316.77353"
-       y1="460.53082"
-       x2="279.39786"
-       y2="416.08411"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3382"
-       id="linearGradient3388"
-       x1="253.13391"
-       y1="478.71356"
-       x2="215.75826"
-       y2="416.08411"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3170"
-       id="linearGradient3030"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(280,4)"
-       x1="263.23544"
-       y1="454.2395"
-       x2="228.89024"
-       y2="393.63034" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3382"
-       id="linearGradient3032"
-       gradientUnits="userSpaceOnUse"
-       x1="253.13391"
-       y1="478.71356"
-       x2="215.75826"
-       y2="416.08411"
-       gradientTransform="translate(280,0)" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3178"
-       id="linearGradient3034"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(283.03046,5.0101525)"
-       x1="328.89536"
-       y1="452.21921"
-       x2="282.42831"
-       y2="390.59988" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3374"
-       id="linearGradient3036"
-       gradientUnits="userSpaceOnUse"
-       x1="316.77353"
-       y1="460.53082"
-       x2="279.39786"
-       y2="416.08411"
-       gradientTransform="translate(280,0)" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3170"
-       id="linearGradient3813"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(280,4)"
-       x1="263.23544"
-       y1="454.2395"
-       x2="228.89024"
-       y2="393.63034" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3382"
-       id="linearGradient3815"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(280,0)"
-       x1="253.13391"
-       y1="478.71356"
-       x2="215.75826"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3178"
-       id="linearGradient3817"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(283.03046,5.0101525)"
-       x1="328.89536"
-       y1="452.21921"
-       x2="282.42831"
-       y2="390.59988" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3374"
-       id="linearGradient3819"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(280,0)"
-       x1="316.77353"
-       y1="460.53082"
-       x2="279.39786"
-       y2="416.08411" />
-    <filter
-       inkscape:collect="always"
-       id="filter3810">
-      <feGaussianBlur
-         inkscape:collect="always"
-         stdDeviation="2.49449"
-         id="feGaussianBlur3812" />
-    </filter>
-    <filter
-       inkscape:collect="always"
-       id="filter3832">
-      <feGaussianBlur
-         inkscape:collect="always"
-         stdDeviation="1.6150575"
-         id="feGaussianBlur3834" />
-    </filter>
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3338"
-       id="linearGradient3855"
-       gradientUnits="userSpaceOnUse"
-       x1="208.02345"
-       y1="522.42053"
-       x2="299.79645"
-       y2="394.28171" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3346"
-       id="linearGradient3857"
-       gradientUnits="userSpaceOnUse"
-       x1="178.83714"
-       y1="486.41888"
-       x2="260.62125"
-       y2="371.02631" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3170"
-       id="linearGradient3859"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(0,4)"
-       x1="263.23544"
-       y1="454.2395"
-       x2="228.89024"
-       y2="393.63034" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3382"
-       id="linearGradient3861"
-       gradientUnits="userSpaceOnUse"
-       x1="253.13391"
-       y1="478.71356"
-       x2="215.75826"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3178"
-       id="linearGradient3863"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(3.0304576,5.0101525)"
-       x1="328.89536"
-       y1="452.21921"
-       x2="282.42831"
-       y2="390.59988" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3374"
-       id="linearGradient3865"
-       gradientUnits="userSpaceOnUse"
-       x1="316.77353"
-       y1="460.53082"
-       x2="279.39786"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3170"
-       id="linearGradient3911"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(279.0875,137.5547)"
-       x1="263.23544"
-       y1="454.2395"
-       x2="228.89024"
-       y2="393.63034" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3382"
-       id="linearGradient3913"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(279.0875,133.5547)"
-       x1="253.13391"
-       y1="478.71356"
-       x2="215.75826"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3178"
-       id="linearGradient3915"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(282.11796,138.56485)"
-       x1="328.89536"
-       y1="452.21921"
-       x2="282.42831"
-       y2="390.59988" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3374"
-       id="linearGradient3917"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(279.0875,133.5547)"
-       x1="316.77353"
-       y1="460.53082"
-       x2="279.39786"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3338"
-       id="linearGradient3952"
-       gradientUnits="userSpaceOnUse"
-       x1="208.02345"
-       y1="522.42053"
-       x2="299.79645"
-       y2="394.28171" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3346"
-       id="linearGradient3954"
-       gradientUnits="userSpaceOnUse"
-       x1="178.83714"
-       y1="486.41888"
-       x2="260.62125"
-       y2="371.02631" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3170"
-       id="linearGradient3956"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(0,4)"
-       x1="263.23544"
-       y1="454.2395"
-       x2="228.89024"
-       y2="393.63034" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3382"
-       id="linearGradient3958"
-       gradientUnits="userSpaceOnUse"
-       x1="253.13391"
-       y1="478.71356"
-       x2="215.75826"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3178"
-       id="linearGradient3960"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(3.0304576,5.0101525)"
-       x1="328.89536"
-       y1="452.21921"
-       x2="282.42831"
-       y2="390.59988" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3374"
-       id="linearGradient3962"
-       gradientUnits="userSpaceOnUse"
-       x1="316.77353"
-       y1="460.53082"
-       x2="279.39786"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3170"
-       id="linearGradient3094"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(279.0875,137.5547)"
-       x1="263.23544"
-       y1="454.2395"
-       x2="228.89024"
-       y2="393.63034" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3382"
-       id="linearGradient3096"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(279.0875,133.5547)"
-       x1="253.13391"
-       y1="478.71356"
-       x2="215.75826"
-       y2="416.08411" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3178"
-       id="linearGradient3098"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(282.11796,138.56485)"
-       x1="328.89536"
-       y1="452.21921"
-       x2="282.42831"
-       y2="390.59988" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3374"
-       id="linearGradient3100"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(279.0875,133.5547)"
-       x1="316.77353"
-       y1="460.53082"
-       x2="279.39786"
-       y2="416.08411" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     gridtolerance="10000"
-     guidetolerance="10"
-     objecttolerance="10"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.35355339"
-     inkscape:cx="840.5751"
-     inkscape:cy="224.91547"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer3"
-     showgrid="false"
-     inkscape:window-width="1366"
-     inkscape:window-height="706"
-     inkscape:window-x="-8"
-     inkscape:window-y="-8"
-     inkscape:window-maximized="1" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:groupmode="layer"
-     id="layer3"
-     inkscape:label="fakepages"
-     style="opacity:0">
-    <rect
-       style="fill:#0000ff;fill-rule:evenodd;stroke:none"
-       id="rect3038"
-       width="256"
-       height="256"
-       x="570.73621"
-       y="-170.72598"
-       inkscape:export-filename="C:\Users\Olof.Bjarnason\Desktop\pytddmon.png"
-       inkscape:export-xdpi="90"
-       inkscape:export-ydpi="90" />
-    <rect
-       transform="translate(-156.15926,-324.17043)"
-       style="fill:#0000ff;fill-rule:evenodd;stroke:none"
-       id="rect3056"
-       width="35"
-       height="35"
-       x="451.15927"
-       y="476.17044"
-       inkscape:export-filename="C:\Windows\Temp\bitbucket_logotype.png"
-       inkscape:export-xdpi="90"
-       inkscape:export-ydpi="90" />
-    <rect
-       transform="translate(-156.15926,-324.17043)"
-       style="color:#000000;fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       id="rect3834"
-       width="47"
-       height="47"
-       x="449.15927"
-       y="601.17041" />
-    <rect
-       style="color:#000000;fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       id="rect3084"
-       width="16"
-       height="16"
-       x="282.84271"
-       y="45.40707"
-       inkscape:export-filename="C:\Users\Olof\Desktop\pytddmon_favicon.png"
-       inkscape:export-xdpi="90"
-       inkscape:export-ydpi="90" />
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer2"
-     inkscape:label="shades" />
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-156.15926,-324.17043)">
-    <path
-       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       d="M 105.50328,293.28742 C 98.13435,213.87697 163.34821,166.20262 223.93612,196.68961 C 234.11008,201.80901 239.21678,208.20765 249.03042,218.15436"
-       id="path2395"
-       sodipodi:nodetypes="css"
-       inkscape:transform-center-y="-62.708549"
-       inkscape:transform-center-x="5.6108416" />
-    <g
-       id="g3840"
-       transform="translate(2.0875024,4.5547021)">
-      <path
-         sodipodi:type="arc"
-         style="opacity:0.90265491;fill:url(#linearGradient3855);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3857);stroke-width:10.38545609;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3832);enable-background:accumulate"
-         id="path3296"
-         sodipodi:cx="268.57144"
-         sodipodi:cy="429.50504"
-         sodipodi:rx="74.285713"
-         sodipodi:ry="74.285713"
-         d="m 342.85715,429.50504 c 0,41.02686 -33.25884,74.28571 -74.28571,74.28571 -41.02686,0 -74.28571,-33.25885 -74.28571,-74.28571 0,-41.02687 33.25885,-74.28572 74.28571,-74.28572 41.02687,0 74.28571,33.25885 74.28571,74.28572 z"
-         transform="matrix(0.79345758,-0.54550208,0.54550208,0.79345758,-177.8042,237.22744)" />
-      <text
-         xml:space="preserve"
-         style="font-size:40px;font-style:normal;font-weight:normal;opacity:0.6460177;fill:#2f769d;fill-opacity:1;stroke:none;filter:url(#filter3810);font-family:Bitstream Vera Sans"
-         id="text3186"
-         transform="translate(82.418415,168.25455)"><textPath
-           xlink:href="#path2395"
-           id="textPath3188"><tspan
-   style="fill:#2f769d;fill-opacity:1"
-   id="tspan3190">pytddmon</tspan></textPath></text>
-      <text
-         xml:space="preserve"
-         style="font-size:40px;font-style:normal;font-weight:normal;opacity:0.38053098;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
-         id="text3814"
-         transform="translate(83.428572,170.26469)"><textPath
-           xlink:href="#path2395"
-           id="textPath3816"><tspan
-   id="tspan3818">pytddmon</tspan></textPath></text>
-      <path
-         id="path2387"
-         d="m 245.6921,498.44568 c -19.17939,-6.97328 -34.32039,-21.74585 -41.39823,-40.39083 -10.40419,-27.40741 -1.98074,-59.42486 20.37433,-77.44288 13.16779,-10.61316 27.17689,-15.65671 44.11432,-15.88202 6.58083,-0.0876 6.76725,0.52744 1.32747,4.37896 -3.98705,2.82295 -10.05528,9.87003 -12.29484,14.27806 -3.57446,7.03544 -3.66002,16.83885 -0.22597,25.88637 2.92536,7.70729 8.82986,18.26695 17.03746,30.46995 7.91297,11.76492 8.62535,13.03928 9.27582,16.59327 2.12196,11.59366 -5.40774,22.80561 -26.0215,38.74681 l -6.8554,5.30144 -5.33346,-1.93913 z"
-         style="fill:url(#linearGradient3859);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3861);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-      <path
-         id="path2389"
-         d="m 305.26571,494.44689 c -2.94296,1.70929 -7.81981,3.97721 -10.83747,5.03982 -7.22659,2.54471 -19.82889,4.26494 -27.22994,3.71693 l -5.93643,-0.43956 7.43388,-6.20801 c 9.31304,-7.77726 13.78666,-12.40302 18.13222,-18.74888 4.55815,-6.65633 6.43334,-14.25609 5.20403,-21.09107 -0.85852,-4.77331 -1.06615,-5.16771 -10.15889,-19.29459 -15.65006,-24.31463 -19.28832,-33.24593 -17.98042,-44.13858 0.78309,-6.52183 4.61378,-12.21719 12.9158,-19.20289 2.11417,-1.77896 4.33973,-4.14094 4.9457,-5.24882 1.0651,-1.94737 1.23021,-1.99212 4.96658,-1.34609 4.36922,0.75545 13.63827,4.15238 18.3551,6.72683 12.10377,6.60625 23.62952,19.14682 29.17862,31.74777 6.01725,13.66407 7.45472,25.39531 4.94229,40.33454 -3.45355,20.53524 -15.28163,37.32079 -33.93109,48.1526 l 2e-5,0 z"
-         style="fill:url(#linearGradient3863);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3865);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-      <text
-         transform="translate(81.428572,168.26469)"
-         id="text2391"
-         style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
-         xml:space="preserve"><textPath
-           id="textPath2397"
-           xlink:href="#path2395"><tspan
-   id="tspan2393">pytddmon</tspan></textPath></text>
-      <path
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="csscc"
-         id="path3335"
-         d="m 220.97017,477.56052 c -10.43076,-11.03935 -15.31344,-29.4645 -15.31344,-45.83935 0,-34.01386 27.60545,-61.61931 61.61931,-61.61931 11.93415,0 23.0794,3.39834 32.5202,9.27945 16.56643,-2.44444 -113.10919,64.32605 -78.82607,98.17921 z"
-         style="opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
-    </g>
-    <g
-       id="g3808"
-       transform="matrix(1.7275645,0,0,1.7275645,-95.479553,-468.34267)">
-      <path
-         inkscape:connector-curvature="0"
-         style="fill:url(#linearGradient3813);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3815);stroke-width:2.60482287;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 525.6921,498.44568 c -19.17939,-6.97328 -34.32039,-21.74585 -41.39823,-40.39083 -10.40419,-27.40741 -1.98074,-59.42486 20.37433,-77.44288 13.16779,-10.61316 27.17689,-15.65671 44.11432,-15.88202 6.58083,-0.0876 6.76725,0.52744 1.32747,4.37896 -3.98705,2.82295 -10.05528,9.87003 -12.29484,14.27806 -3.57446,7.03544 -3.66002,16.83885 -0.22597,25.88637 2.92536,7.70729 8.82986,18.26695 17.03746,30.46995 7.91297,11.76492 8.62535,13.03928 9.27582,16.59327 2.12196,11.59366 -5.40774,22.80561 -26.0215,38.74681 l -6.8554,5.30144 -5.33346,-1.93913 z"
-         id="path3024" />
-      <path
-         inkscape:connector-curvature="0"
-         style="fill:url(#linearGradient3817);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3819);stroke-width:2.60482287;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 585.26571,494.44689 c -2.94296,1.70929 -7.81981,3.97721 -10.83747,5.03982 -7.22659,2.54471 -19.82889,4.26494 -27.22994,3.71693 l -5.93643,-0.43956 7.43388,-6.20801 c 9.31304,-7.77726 13.78666,-12.40302 18.13222,-18.74888 4.55815,-6.65633 6.43334,-14.25609 5.20403,-21.09107 -0.85852,-4.77331 -1.06615,-5.16771 -10.15889,-19.29459 -15.65006,-24.31463 -19.28832,-33.24593 -17.98042,-44.13858 0.78309,-6.52183 4.61378,-12.21719 12.9158,-19.20289 2.11417,-1.77896 4.33973,-4.14094 4.9457,-5.24882 1.0651,-1.94737 1.23021,-1.99212 4.96658,-1.34609 4.36922,0.75545 13.63827,4.15238 18.3551,6.72683 12.10377,6.60625 23.62952,19.14682 29.17862,31.74777 6.01725,13.66407 7.45472,25.39531 4.94229,40.33454 -3.45355,20.53524 -15.28163,37.32079 -33.93109,48.1526 l 2e-5,0 z"
-         id="path3026" />
-      <path
-         inkscape:connector-curvature="0"
-         style="opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 499.45494,476.0656 c -10.43076,-11.03935 -13.79821,-29.96958 -13.79821,-46.34443 0,-34.01386 27.60545,-61.61931 61.61931,-61.61931 11.93415,0 23.0794,3.39834 32.5202,9.27945 16.56643,-2.44444 -114.62442,64.83113 -80.3413,98.68429 z"
-         id="path3028"
-         sodipodi:nodetypes="csscc" />
-    </g>
-    <text
-       xml:space="preserve"
-       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="399.15927"
-       y="541.17041"
-       id="text3826"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3828"
-         x="399.15927"
-         y="541.17041">bitbucket logotype</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="372.15924"
-       y="681.17041"
-       id="text3830"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3832"
-         x="372.15924"
-         y="681.17041">pytddmon.org logotype</tspan></text>
-    <text
-       xml:space="preserve"
-       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
-       x="207.15926"
-       y="556.17041"
-       id="text3836"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan3838"
-         x="207.15926"
-         y="556.17041">wiki logotype</tspan></text>
-    <rect
-       style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       id="rect3966"
-       width="88.5"
-       height="74"
-       x="425.65927"
-       y="448.17044" />
-    <g
-       id="g3906"
-       transform="matrix(0.23794816,0,0,0.23794816,337.97536,358.61852)">
-      <path
-         inkscape:connector-curvature="0"
-         style="fill:url(#linearGradient3911);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3913);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 524.7796,632.00038 c -19.17939,-6.97328 -34.32039,-21.74585 -41.39823,-40.39083 -10.40419,-27.40741 -1.98074,-59.42486 20.37433,-77.44288 13.16779,-10.61316 27.17689,-15.65671 44.11432,-15.88202 6.58083,-0.0876 6.76725,0.52744 1.32747,4.37896 -3.98705,2.82295 -10.05528,9.87003 -12.29484,14.27806 -3.57446,7.03544 -3.66002,16.83885 -0.22597,25.88637 2.92536,7.70729 8.82986,18.26695 17.03746,30.46995 7.91297,11.76492 8.62535,13.03928 9.27582,16.59327 2.12196,11.59366 -5.40774,22.80561 -26.0215,38.74681 l -6.8554,5.30144 -5.33346,-1.93913 z"
-         id="path3857" />
-      <path
-         inkscape:connector-curvature="0"
-         style="fill:url(#linearGradient3915);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3917);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 584.35321,628.00159 c -2.94296,1.70929 -7.81981,3.97721 -10.83747,5.03982 -7.22659,2.54471 -19.82889,4.26494 -27.22994,3.71693 l -5.93643,-0.43956 7.43388,-6.20801 c 9.31304,-7.77726 13.78666,-12.40302 18.13222,-18.74888 4.55815,-6.65633 6.43334,-14.25609 5.20403,-21.09107 -0.85852,-4.77331 -1.06615,-5.16771 -10.15889,-19.29459 -15.65006,-24.31463 -19.28832,-33.24593 -17.98042,-44.13858 0.78309,-6.52183 4.61378,-12.21719 12.9158,-19.20289 2.11417,-1.77896 4.33973,-4.14094 4.9457,-5.24882 1.0651,-1.94737 1.23021,-1.99212 4.96658,-1.34609 4.36922,0.75545 13.63827,4.15238 18.3551,6.72683 12.10377,6.60625 23.62952,19.14682 29.17862,31.74777 6.01725,13.66407 7.45472,25.39531 4.94229,40.33454 -3.45355,20.53524 -15.28163,37.32079 -33.93109,48.1526 l 2e-5,0 z"
-         id="path3859" />
-      <path
-         style="opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 500.05767,611.11522 c -10.43076,-11.03935 -15.31344,-29.4645 -15.31344,-45.83935 0,-34.01386 27.60545,-61.61931 61.61931,-61.61931 11.93415,0 23.0794,3.39834 32.5202,9.27945 16.56643,-2.44444 -113.10919,64.32605 -78.82607,98.17921 z"
-         id="path3867"
-         sodipodi:nodetypes="csscc"
-         inkscape:connector-curvature="0" />
-    </g>
-    <g
-       transform="matrix(0.23603344,0,0,0.23603344,412.68984,525.62707)"
-       id="g3924">
-      <path
-         transform="matrix(0.79345758,-0.54550208,0.54550208,0.79345758,-177.8042,237.22744)"
-         d="m 342.85715,429.50504 c 0,41.02686 -33.25884,74.28571 -74.28571,74.28571 -41.02686,0 -74.28571,-33.25885 -74.28571,-74.28571 0,-41.02687 33.25885,-74.28572 74.28571,-74.28572 41.02687,0 74.28571,33.25885 74.28571,74.28572 z"
-         sodipodi:ry="74.285713"
-         sodipodi:rx="74.285713"
-         sodipodi:cy="429.50504"
-         sodipodi:cx="268.57144"
-         id="path3926"
-         style="opacity:0.90265491;fill:url(#linearGradient3952);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3954);stroke-width:10.38545609;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3832);enable-background:accumulate"
-         sodipodi:type="arc" />
-      <text
-         transform="translate(82.418415,168.25455)"
-         id="text3928"
-         style="font-size:40px;font-style:normal;font-weight:normal;opacity:0.6460177;fill:#2f769d;fill-opacity:1;stroke:none;filter:url(#filter3810);font-family:Bitstream Vera Sans"
-         xml:space="preserve"><textPath
-           id="textPath3930"
-           xlink:href="#path2395"><tspan
-   id="tspan3932"
-   style="fill:#2f769d;fill-opacity:1">pytddmon</tspan></textPath></text>
-      <text
-         transform="translate(83.428572,170.26469)"
-         id="text3934"
-         style="font-size:40px;font-style:normal;font-weight:normal;opacity:0.38053098;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
-         xml:space="preserve"><textPath
-           id="textPath3936"
-           xlink:href="#path2395"><tspan
-   id="tspan3938">pytddmon</tspan></textPath></text>
-      <path
-         inkscape:connector-curvature="0"
-         style="fill:url(#linearGradient3956);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3958);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 245.6921,498.44568 c -19.17939,-6.97328 -34.32039,-21.74585 -41.39823,-40.39083 -10.40419,-27.40741 -1.98074,-59.42486 20.37433,-77.44288 13.16779,-10.61316 27.17689,-15.65671 44.11432,-15.88202 6.58083,-0.0876 6.76725,0.52744 1.32747,4.37896 -3.98705,2.82295 -10.05528,9.87003 -12.29484,14.27806 -3.57446,7.03544 -3.66002,16.83885 -0.22597,25.88637 2.92536,7.70729 8.82986,18.26695 17.03746,30.46995 7.91297,11.76492 8.62535,13.03928 9.27582,16.59327 2.12196,11.59366 -5.40774,22.80561 -26.0215,38.74681 l -6.8554,5.30144 -5.33346,-1.93913 z"
-         id="path3940" />
-      <path
-         inkscape:connector-curvature="0"
-         style="fill:url(#linearGradient3960);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3962);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 305.26571,494.44689 c -2.94296,1.70929 -7.81981,3.97721 -10.83747,5.03982 -7.22659,2.54471 -19.82889,4.26494 -27.22994,3.71693 l -5.93643,-0.43956 7.43388,-6.20801 c 9.31304,-7.77726 13.78666,-12.40302 18.13222,-18.74888 4.55815,-6.65633 6.43334,-14.25609 5.20403,-21.09107 -0.85852,-4.77331 -1.06615,-5.16771 -10.15889,-19.29459 -15.65006,-24.31463 -19.28832,-33.24593 -17.98042,-44.13858 0.78309,-6.52183 4.61378,-12.21719 12.9158,-19.20289 2.11417,-1.77896 4.33973,-4.14094 4.9457,-5.24882 1.0651,-1.94737 1.23021,-1.99212 4.96658,-1.34609 4.36922,0.75545 13.63827,4.15238 18.3551,6.72683 12.10377,6.60625 23.62952,19.14682 29.17862,31.74777 6.01725,13.66407 7.45472,25.39531 4.94229,40.33454 -3.45355,20.53524 -15.28163,37.32079 -33.93109,48.1526 l 2e-5,0 z"
-         id="path3942" />
-      <text
-         xml:space="preserve"
-         style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
-         id="text3944"
-         transform="translate(81.428572,168.26469)"><textPath
-           xlink:href="#path2395"
-           id="textPath3946"><tspan
-   id="tspan3948">pytddmon</tspan></textPath></text>
-      <path
-         style="opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         d="m 220.97017,477.56052 c -10.43076,-11.03935 -15.31344,-29.4645 -15.31344,-45.83935 0,-34.01386 27.60545,-61.61931 61.61931,-61.61931 11.93415,0 23.0794,3.39834 32.5202,9.27945 16.56643,-2.44444 -113.10919,64.32605 -78.82607,98.17921 z"
-         id="path3950"
-         sodipodi:nodetypes="csscc"
-         inkscape:connector-curvature="0" />
-    </g>
-    <g
-       transform="matrix(0.10933077,0,0,0.10933077,386.95624,315.52478)"
-       id="g3086">
-      <path
-         id="path3088"
-         d="m 524.7796,632.00038 c -19.17939,-6.97328 -34.32039,-21.74585 -41.39823,-40.39083 -10.40419,-27.40741 -1.98074,-59.42486 20.37433,-77.44288 13.16779,-10.61316 27.17689,-15.65671 44.11432,-15.88202 6.58083,-0.0876 6.76725,0.52744 1.32747,4.37896 -3.98705,2.82295 -10.05528,9.87003 -12.29484,14.27806 -3.57446,7.03544 -3.66002,16.83885 -0.22597,25.88637 2.92536,7.70729 8.82986,18.26695 17.03746,30.46995 7.91297,11.76492 8.62535,13.03928 9.27582,16.59327 2.12196,11.59366 -5.40774,22.80561 -26.0215,38.74681 l -6.8554,5.30144 -5.33346,-1.93913 z"
-         style="fill:url(#linearGradient3094);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3096);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-      <path
-         id="path3090"
-         d="m 584.35321,628.00159 c -2.94296,1.70929 -7.81981,3.97721 -10.83747,5.03982 -7.22659,2.54471 -19.82889,4.26494 -27.22994,3.71693 l -5.93643,-0.43956 7.43388,-6.20801 c 9.31304,-7.77726 13.78666,-12.40302 18.13222,-18.74888 4.55815,-6.65633 6.43334,-14.25609 5.20403,-21.09107 -0.85852,-4.77331 -1.06615,-5.16771 -10.15889,-19.29459 -15.65006,-24.31463 -19.28832,-33.24593 -17.98042,-44.13858 0.78309,-6.52183 4.61378,-12.21719 12.9158,-19.20289 2.11417,-1.77896 4.33973,-4.14094 4.9457,-5.24882 1.0651,-1.94737 1.23021,-1.99212 4.96658,-1.34609 4.36922,0.75545 13.63827,4.15238 18.3551,6.72683 12.10377,6.60625 23.62952,19.14682 29.17862,31.74777 6.01725,13.66407 7.45472,25.39531 4.94229,40.33454 -3.45355,20.53524 -15.28163,37.32079 -33.93109,48.1526 l 2e-5,0 z"
-         style="fill:url(#linearGradient3098);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3100);stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-      <path
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="csscc"
-         id="path3092"
-         d="m 500.05767,611.11522 c -10.43076,-11.03935 -15.31344,-29.4645 -15.31344,-45.83935 0,-34.01386 27.60545,-61.61931 61.61931,-61.61931 11.93415,0 23.0794,3.39834 32.5202,9.27945 16.56643,-2.44444 -113.10919,64.32605 -78.82607,98.17921 z"
-         style="opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
-    </g>
-  </g>
-</svg>

File src/pytddmon.py

-#! /usr/bin/env python
-#coding: utf-8
-
-'''
-COPYRIGHT (c) 2009, 2010, 2011, 2012
-.. in order of first contribution
-Olof Bjarnason
-    Initial proof-of-concept pygame implementation.
-Fredrik Wendt
-    Help with Tkinter implementation (replacing the pygame dependency)
-Krunoslav Saho
-    Added always-on-top to the pytddmon window
-Samuel Ytterbrink
-    Print(".") will not screw up test-counting (it did before)
-    Docstring support
-    Recursive discovery of tests
-    Refactoring to increase Pylint score from 6 to 9.5 out of 10 (!)
-    Numerous refactorings & other improvements
-Rafael Capucho
-    Python shebang at start of script, enabling "./pytddmon.py" on unix systems
-Ilian Iliev
-    Use integers instead of floats in file modified time (checksum calc)
-    Auto-update of text in Details window when the log changes
-Henrik Bohre
-    Status bar in pytddmon window, showing either last time tests were
-    run, or "Testing..." during a test run
-    
-
-LICENSE
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-'''
-
-import os
-import sys
-import platform
-import optparse
-import re
-import unittest
-import doctest
-import time
-import multiprocessing
-import fnmatch
-import functools
-
-ON_PYTHON3 = sys.version_info[0] == 3
-ON_WINDOWS = platform.system() == "Windows"
-
-####
-## Core
-####
-
-class Pytddmon:
-    "The core class, all functionality is combined into this class"
-    def __init__(
-        self,
-        file_finder,
-        monitor,
-        project_name = "<pytddmon>"
-    ):
-        self.file_finder = file_finder
-        self.project_name = project_name
-        self.monitor = monitor
-        self.change_detected = False
-
-        self.total_tests_run = 0
-        self.total_tests_passed = 0
-        self.last_testrun_time = -1
-        self.log = ""
-        self.status_message = 'n/a'
-
-        self.run_tests()
-
-    def run_tests(self):
-        """Runs all tests and updates state variables with results."""
-        
-        file_paths = self.file_finder()
-        
-        # We need to run the tests in a separate process, since
-        # Python caches loaded modules, and unittest/doctest
-        # imports modules to run them.
-        # However, we do not want to assume users' unit tests
-        # are thread-safe, so we only run one test module at a
-        # time, using processes = 1.
-        start = time.time()
-        if file_paths:
-            pool = multiprocessing.Pool(processes = 1)
-            results = pool.map(run_tests_in_file, file_paths)
-            pool.close()
-            pool.join()
-        else:
-            results = []
-        self.last_testrun_time = time.time() - start
-        
-        now = time.strftime("%H:%M:%S", time.localtime())
-        self.log = ""
-        self.log += "Monitoring folder %s.\n" % self.project_name
-        self.log += "Found <TOTALTESTS> tests in %i files.\n" % len(results)
-        self.log += "Last change detected at %s.\n" % now
-        self.log += "Test run took %.2f seconds.\n" % self.last_testrun_time
-        self.log += "\n"
-        self.total_tests_passed = 0
-        self.total_tests_run = 0
-        module_logs = []  # Summary for each module with errors first
-        for packed in results:
-            (module, green, total, logtext) = packed
-            self.total_tests_passed += green
-            self.total_tests_run += total
-            module_log = "\nLog from " + module + ":\n" + logtext
-            if not isinstance(total, int) or total - green > 0:
-                module_logs.insert(0, module_log)
-            else:
-                module_logs.append(module_log)
-        self.log += ''.join(module_logs)
-        self.log = self.log.replace('<TOTALTESTS>', 
-                str(int(self.total_tests_run.real)))
-        self.status_message = now
-
-    def get_and_set_change_detected(self):
-        self.change_detected = self.monitor.look_for_changes()
-        return self.change_detected
-
-    def main(self):
-        """This is the main loop body"""
-        self.change_detected = self.monitor.look_for_changes()
-        if self.change_detected:
-            self.run_tests()
-
-    def get_log(self):
-        """Access the log string created during test run"""
-        return self.log
-
-    def get_status_message(self):
-        """Return message in status bar"""
-        return self.status_message
-
-class Monitor:
-    'Looks for file changes when prompted to'
-    
-    def __init__(self, file_finder, get_file_size, get_file_modtime):
-        self.file_finder = file_finder
-        self.get_file_size = get_file_size
-        self.get_file_modtime = get_file_modtime
-        self.snapshot = self.get_snapshot()
-
-    def get_snapshot(self):
-        snapshot = {}
-        for file in self.file_finder():
-            file_size = self.get_file_size(file)
-            file_modtime = self.get_file_modtime(file)
-            snapshot[file] = (file_size, file_modtime)
-        return snapshot
-
-    def look_for_changes(self):
-        new_snapshot = self.get_snapshot()
-        change_detected = new_snapshot != self.snapshot
-        self.snapshot = new_snapshot
-        return change_detected
-
-
-####
-## Finding files
-####
-
-class FileFinder:
-    "Returns all files matching given regular expression from root downwards"
-    
-    def __init__(self, root, regexp):
-        self.root = os.path.abspath(root)
-        self.regexp = regexp
-        
-    def __call__(self):
-        return self.find_files()
-
-    def find_files(self):
-        "recursively finds files matching regexp"
-        file_paths = set()
-        for path, _folder, filenames in os.walk(self.root):
-            for filename in filenames:
-                if self.re_complete_match(filename):
-                    file_paths.add(
-                        os.path.abspath(os.path.join(path, filename))
-                    )
-        return file_paths
-        
-    def re_complete_match(self, string_to_match):
-        "full string regexp check"
-        return bool(re.match(self.regexp + "$", string_to_match))
-
-wildcard_to_regex = fnmatch.translate
-
-####
-## Finding & running tests
-####
-
-def log_exceptions(func):
-    """Decorator that forwards the error message from an exception to the log
-    slot of the return value, and also returns a complexnumber to signal that
-    the result is an error."""
-    wraps = functools.wraps
-
-    @wraps(func)
-    def wrapper(*a, **k):
-        "Docstring"
-        try:
-            return func(*a, **k)
-        except:
-            import traceback
-            return ('Exception(%s)' % a[0] , 0, 1j, traceback.format_exc())
-    return wrapper
-
-@log_exceptions
-def run_tests_in_file(file_path):
-    module = file_name_to_module("", file_path)
-    return run_module(module)
-
-def run_module(module):
-    suite = find_tests_in_module(module)
-    (green, total, log) = run_suite(suite)
-    return (module, green, total, log)
-
-def file_name_to_module(base_path, file_name):
-    r"""Converts filenames of files in packages to import friendly dot
-    separated paths.
-
-    Examples:
-    >>> print(file_name_to_module("","pytddmon.pyw"))
-    pytddmon
-    >>> print(file_name_to_module("","pytddmon.py"))
-    pytddmon
-    >>> print(file_name_to_module("","tests/pytddmon.py"))
-    tests.pytddmon
-    >>> print(file_name_to_module("","./tests/pytddmon.py"))
-    tests.pytddmon
-    >>> print(file_name_to_module("",".\\tests\\pytddmon.py"))
-    tests.pytddmon
-    >>> print(
-    ...     file_name_to_module(
-    ...         "/User/pytddmon\\ geek/pytddmon/",
-    ...         "/User/pytddmon\\ geek/pytddmon/tests/pytddmon.py"
-    ...     )
-    ... )
-    tests.pytddmon
-    """
-    symbol_stripped = os.path.relpath(file_name, base_path)
-    for symbol in r"/\.":
-        symbol_stripped = symbol_stripped.replace(symbol, " ")
-    words = symbol_stripped.split()
-    # remove .py/.pyw
-    module_words = words[:-1]
-    module_name = '.'.join(module_words)
-    return module_name
-
-def find_tests_in_module(module):
-    suite = unittest.TestSuite()
-    suite.addTests(find_unittests_in_module(module))
-    suite.addTests(find_doctests_in_module(module))
-    return suite
-
-def find_unittests_in_module(module):
-    test_loader = unittest.TestLoader()
-    return test_loader.loadTestsFromName(module)
-
-def find_doctests_in_module(module):
-    try:
-        return doctest.DocTestSuite(module, optionflags = doctest.ELLIPSIS)
-    except ValueError:
-        return unittest.TestSuite()
-
-def run_suite(suite):
-    def StringIO():
-        if ON_PYTHON3:
-            import io as StringIO
-        else:
-            import StringIO 
-        return StringIO.StringIO()
-    err_log = StringIO()
-    text_test_runner = unittest.TextTestRunner(stream = err_log, verbosity = 1)
-    result = text_test_runner.run(suite)
-    green = result.testsRun - len(result.failures) - len(result.errors)
-    total = result.testsRun
-    log = err_log.getvalue() if green<total else "All %i tests passed\n" % green
-    return (green, total, log)
-
-
-####
-## GUI
-####
-
-def import_tkinter():
-    "imports tkinter from python 3.x or python 2.x"
-    if not ON_PYTHON3:
-        import Tkinter as tkinter
-    else:
-        import tkinter
-    return tkinter
-
-def import_tkFont():
-    "imports tkFont from python 3.x or python 2.x"
-    if not ON_PYTHON3:
-        import tkFont
-    else:
-        from tkinter import font as tkFont 
-    return tkFont
-    
-class TKGUIButton(object):
-    """Encapsulate the button(label)"""
-    def __init__(self, tkinter, tkFont, toplevel, display_log_callback):
-        self.font = tkFont.Font(name="Helvetica", size=28)
-        self.label = tkinter.Label(
-            toplevel,
-            text="loading...",
-            relief='raised',
-            font=self.font,
-            justify=tkinter.CENTER,
-            anchor=tkinter.CENTER
-        )
-        self.bind_click(display_log_callback)
-        self.pack()
-
-    def bind_click(self, display_log_callback):
-        """Binds the left mous button click event to trigger the logg_windows\
-        diplay method"""
-        self.label.bind(
-            '<Button-1>',
-            display_log_callback
-        )
-
-    def pack(self):
-        "packs the lable"
-        self.label.pack(
-            expand=1,
-            fill='both'
-        )
-
-    def update(self, text, color):
-        "updates the collor and displayed text."
-        self.label.configure(
-            bg=color,
-            activebackground=color,
-            text=text
-        )
-
-class TkGUI(object):
-    """Connect pytddmon engine to Tkinter GUI toolkit"""
-    def __init__(self, pytddmon, tkinter, tkFont):
-        self.pytddmon = pytddmon
-        self.tkinter = tkinter
-        self.tkFont = tkFont
-        self.color_picker = ColorPicker()
-        self.root = None
-        self.building_root()
-        self.title_font = None
-        self.building_fonts()
-        self.frame = None
-        self.building_frame()
-        self.button = TKGUIButton(
-            tkinter,
-            tkFont,
-            self.frame,
-            self.display_log_message
-        )
-        self.status_bar = None
-        self.building_status_bar()
-        self.frame.grid()
-        self.message_window = None
-        self.text = None
-
-        if ON_WINDOWS:
-            buttons_width = 25
-        else:
-            buttons_width = 75
-        self.root.minsize(
-            width=self.title_font.measure(
-                self.pytddmon.project_name
-            ) + buttons_width, 
-            height=0
-        )
-        self.frame.pack(expand=1, fill="both")
-        self.create_text_window()
-        self.update_text_window()
-
-    def building_root(self):
-        """take hold of the tk root object as self.root"""
-        self.root = self.tkinter.Tk()
-        self.root.wm_attributes("-topmost", 1)
-        if ON_WINDOWS:
-            self.root.attributes("-toolwindow", 1)
-            print("Minimize me!")
-
-    def building_fonts(self):
-        "building fonts"
-        self.title_font = self.tkFont.nametofont("TkCaptionFont")
-
-    def building_frame(self):
-        """Creates a frame and assigns it to self.frame"""
-        # Calculate the width of the tilte + buttons
-        self.frame = self.tkinter.Frame(
-            self.root
-        )
-        # Sets the title of the gui
-        self.frame.master.title(self.pytddmon.project_name)
-        # Forces the window to not be resizeable
-        self.frame.master.resizable(False, False)
-        self.frame.pack(expand=1, fill="both")
-
-    def building_status_bar(self):
-        """Add status bar and assign it to self.status_bar"""
-        self.status_bar = self.tkinter.Label(
-            self.frame,
-            text="n/a"
-        )
-        self.status_bar.pack(expand=1, fill="both")
-
-    def _update_and_get_color(self):
-        "Calculate the current color and trigger pulse"
-        self.color_picker.set_result(
-            self.pytddmon.total_tests_passed,
-            self.pytddmon.total_tests_run,
-        )
-        light, color = self.color_picker.pick()
-        rgb = self.color_picker.translate_color(light, color)
-        self.color_picker.pulse()
-        return rgb
-
-    def _get_text(self):
-        "Calculates the text to show the user(passed/total or Error!)"
-        if self.pytddmon.total_tests_run.imag!=0:
-            text = "?ERROR"
-        else:
-            text = "%r/%r" % (
-                self.pytddmon.total_tests_passed,
-                self.pytddmon.total_tests_run
-            )
-        return text
-
-    def update(self):
-        """updates the tk gui"""
-        rgb = self._update_and_get_color()
-        text = self._get_text()
-        self.button.update(text, rgb)
-        self.root.configure(bg=rgb)
-        self.update_status(self.pytddmon.get_status_message())
-    
-        if self.pytddmon.change_detected:
-            self.update_text_window()
-
-    def update_status(self, message):
-        self.status_bar.configure(
-            text=message
-        )
-        self.status_bar.update_idletasks()
-
-    def get_text_message(self):
-        """returns the logmessage from pytddmon"""
-        message = self.pytddmon.get_log()
-        return message
-
-    def create_text_window(self):
-        """creates new window and text widget""" 
-        win = self.tkinter.Toplevel()
-        if ON_WINDOWS:
-            win.attributes("-toolwindow", 1)
-        win.title('Details')
-        self.message_window = win
-        self.text = self.tkinter.Text(win)
-        self.message_window.withdraw()
-
-    def update_text_window(self):
-        """inserts/replaces the log message in the text widget"""
-        text = self.text
-        text['state'] = self.tkinter.NORMAL
-        text.delete(1.0, self.tkinter.END)
-        text.insert(self.tkinter.INSERT, self.get_text_message())
-        text['state'] = self.tkinter.DISABLED
-        text.pack(expand=1, fill='both')
-        text.focus_set()
-
-    def display_log_message(self, _arg):
-        """displays/close the logmessage from pytddmon in a window"""
-        if self.message_window.state() == 'normal':
-            self.message_window.state('iconic')
-        else:
-            self.message_window.state('normal')
-
-    def loop(self):
-        """the main loop"""
-        if self.pytddmon.get_and_set_change_detected():
-            self.update_status('Testing...')
-            self.pytddmon.run_tests()
-        self.update()
-        self.frame.after(750, self.loop)
-
-    def run(self):
-        """starts the main loop and goes into sleep"""
-        self.loop()
-        self.root.mainloop()
-
-class ColorPicker:
-    """
-    ColorPicker decides the background color the pytddmon window,
-    based on the number of green tests, and the total number of
-    tests. Also, there is a "pulse" (light color, dark color),
-    to increase the feeling of continous testing.
-    """
-    color_table = {
-        (True, 'green'): '0f0',
-        (False, 'green'): '0c0',
-        (True, 'red'): 'f00',
-        (False, 'red'): 'c00',
-        (True, 'orange'): 'fc0',
-        (False, 'orange'): 'ca0',
-        (True, 'gray'): '999',
-        (False, 'gray'): '555'
-    }
-
-    def __init__(self):
-        self.color = 'green'
-        self.light = True
-
-    def pick(self):
-        "returns the tuple (light, color) with the types(bool ,str)"
-        return (self.light, self.color)
-
-    def pulse(self):
-        "updates the light state"
-        self.light = not self.light
-
-    def reset_pulse(self):
-        "resets the light state"
-        self.light = True
-
-    def set_result(self, green, total):
-        "calculates what color should be used and may reset the lightness"
-        old_color = self.color
-        self.color = 'green'
-        if green.imag or total.imag:
-            self.color = "orange"
-        elif green == total - 1:
-            self.color = 'red'
-        elif green < total - 1:
-            self.color = 'gray'
-        if self.color != old_color:
-            self.reset_pulse()
-
-    @classmethod
-    def translate_color(cls, light, color):
-        """helper method to create a rgb string"""
-        return "#" + cls.color_table[(light, color)]
-
-
-def parse_commandline():
-    """
-    returns (files, test_mode) created from the command line arguments
-    passed to pytddmon.
-    """
-    parser = optparse.OptionParser()
-    parser.add_option(
-        "--log-and-exit",
-        action="store_true",
-        default=False,
-        help='Run all tests, write the results to "pytddmon.log" and exit.')
-    (options, args) = parser.parse_args()
-    return (args, options.log_and_exit)
-
-def build_monitor(file_finder):
-    os.stat_float_times(False)
-    def get_file_size(file_path):
-        stat = os.stat(file_path)
-        return stat.st_size
-    def get_file_modtime(file_path):
-        stat = os.stat(file_path)
-        return stat.st_mtime
-    return Monitor(file_finder, get_file_size, get_file_modtime)
-
-def run():
-    """
-    The main function: basic initialization and program start
-    """
-    cwd = os.getcwd()
-    
-    # Include current work directory in Python path
-    sys.path[:0] = [cwd]
-    
-    # Command line argument handling
-    (static_file_set, test_mode) = parse_commandline()
-    
-    # What files to monitor?
-    if not static_file_set:
-        regex = wildcard_to_regex("*.py")
-    else:
-        regex = '|'.join(static_file_set)
-    file_finder = FileFinder(cwd, regex)
-    
-    # The change detector: Monitor
-    monitor = build_monitor(file_finder)
-    
-    # Python engine ready to be setup
-    pytddmon = Pytddmon(
-        file_finder,
-        monitor,
-        project_name = os.path.basename(cwd)
-    )
-    
-    # Start the engine!
-    if not test_mode:
-        TkGUI(pytddmon, import_tkinter(), import_tkFont()).run()
-    else:
-        pytddmon.main()
-        with open("pytddmon.log", "w") as log_file:
-            log_file.write(
-                "green=%r\ntotal=%r\n" % (
-                    pytddmon.total_tests_passed,
-                    pytddmon.total_tests_run
-                )
-            )
-
-if __name__ == '__main__':
-    run()

File src/tests/__init__.py

Empty file removed.

File src/tests/test_color_picker.py

-import unittest
-from pytddmon import ColorPicker
-
-class test_pulse(unittest.TestCase):
-
-	def setUp(self):
-		self.color_picker = ColorPicker()
-
-	def test_starts_with_light_color(self):
-		(light, color) = self.color_picker.pick()
-		self.assertTrue(light)
-		
-	def test_dark_after_pulse(self):
-		self.color_picker.pulse()
-		(light, color) = self.color_picker.pick()
-		self.assertFalse(light)
-		
-	def test_no_failing_test_picks_green(self):
-		self.color_picker.set_result(1, 1)
-		(light, color) = self.color_picker.pick()
-		self.assertEqual(color, 'green')
-		
-	def test_one_failing_test_picks_red(self):
-		self.color_picker.set_result(1, 2)
-		(light, color) = self.color_picker.pick()
-		self.assertEqual(color, 'red')
-		
-	def test_two_failing_tests_picks_gray(self):
-		self.color_picker.set_result(1, 3)
-		(light, color) = self.color_picker.pick()
-		self.assertEqual(color, 'gray')
-		
-	def test_changing_color_resets_pulse(self):
-		self.color_picker.set_result(1,1)
-		self.color_picker.pulse()
-		self.color_picker.set_result(1,2)
-		(light, color) = self.color_picker.pick()
-		self.assertTrue(light)
-		
-	def test_default_color_is_green(self):
-		self.color_picker = ColorPicker()
-		(light, color) = self.color_picker.pick()
-		self.assertEqual('green', color)
-		
-	def test_no_tests_means_green(self):
-		self.color_picker.set_result(0,0)
-		(light, color) = self.color_picker.pick()
-		self.assertEqual('green', color)
-		
-	def test_pulse_is_not_reset_if_colors_stays_same(self):
-		self.color_picker.pulse()
-		self.color_picker.set_result(1,1)
-		self.color_picker.set_result(1,1)
-		(light, color) = self.color_picker.pick()
-		self.assertFalse(light)
-

File src/tests/test_monitor.py

-# coding: utf-8
-import unittest
-from pytddmon import Monitor
-
-class test_change_detection(unittest.TestCase):
-
-    def dtest_modification_time_changed(self):
-        def file_finder():
-            return ['file']
-        def get_file_size(file):
-            return 1
-        modtime = [1]
-        def get_file_modification_time(file):
-            return modtime[0]
-        monitor = Monitor(file_finder, get_file_size, get_file_modification_time)
-        modtime[0] = 2
-        change_detected = monitor.look_for_changes()
-        assert change_detected
-
-    def test_nothing_changed(self):
-        def file_finder():
-            return ['file']
-        def get_file_size(file):
-            return 1
-        def get_file_modification_time(file):
-            return 1
-        monitor = Monitor(file_finder, get_file_size, get_file_modification_time)
-        change_detected = monitor.look_for_changes()
-        assert not change_detected
-
-    def test_adding_file(self):
-        files = ['file']
-        def file_finder():
-            return files
-        def get_file_size(file):
-            return 1
-        def get_file_modification_time(file):
-            return 1
-        monitor = Monitor(file_finder, get_file_size, get_file_modification_time)
-        files.append('file2')
-        change_detected = monitor.look_for_changes()
-        assert change_detected
-
-    def test_renaming_file(self):
-        files = ['file']
-        def file_finder():
-            return files
-        def get_file_size(file):
-            return 1
-        def get_file_modification_time(file):
-            return 1
-        monitor = Monitor(file_finder, get_file_size, get_file_modification_time)
-        files[0] = 'renamed'
-        change_detected = monitor.look_for_changes()
-        assert change_detected
-
-    def test_change_is_only_detected_once(self):
-        files = ['file']
-        def file_finder():
-            return files
-        def get_file_size(file):
-            return 1
-        def get_file_modification_time(file):
-            return 1
-        monitor = Monitor(file_finder, get_file_size, get_file_modification_time)
-        files[0] = 'changed'
-        change_detected = monitor.look_for_changes()
-        change_detected = monitor.look_for_changes()
-        assert not change_detected
-
-    def test_file_size_changed(self):
-        files = ['file']
-        filesize = [1]
-        def file_finder():
-            return files
-        def get_file_size(file):
-            return filesize[0]
-        def get_file_modification_time(file):
-            return 1
-        monitor = Monitor(file_finder, get_file_size, get_file_modification_time)
-        filesize[0] = 5
-        change_detected = monitor.look_for_changes()
-        assert change_detected
-
-    def test_file_order_does_not_matter(self):
-        files = ['file', 'file2']
-        def file_finder():
-            return files
-        def get_file_size(file):
-            return 1
-        def get_file_modification_time(file):
-            return 1
-        monitor = Monitor(file_finder, get_file_size, get_file_modification_time)
-        files[:] = ['file2', 'file']
-        change_detected = monitor.look_for_changes()
-        assert not change_detected
-
-if __name__ == '__main__':
-    unittest.main()

File src/tests/test_pytddmon.py

-# coding: utf-8
-import sys
-sys.path.append('..')
-
-import unittest
-from pytddmon import Pytddmon
-
-'''
-#- kör tester från början
-#- kör inte tester om ingen förändring
-#- kör tester om förändring
-- summa finns i .total_tests
-- gröna finns i .total_passed_tests
-'''
-
-class test_Pytddmon_monitor_communication(unittest.TestCase):
-    class FakeMonitor:
-        def __init__(self, look_for_changes_returns):
-            self.returns = list(look_for_changes_returns)
-            self.returns.reverse()
-        def look_for_changes(self):
-            return self.returns.pop()
-            
-    def setUp(self):
-        self.number_of_test_runs = 0
-
-    def fake_filefinder(self):
-        self.number_of_test_runs += 1
-        return []
-        
-    def test_runs_tests_at_boot(self):
-        fake_monitor = self.FakeMonitor(look_for_changes_returns = [False])
-        pytddmon = Pytddmon(self.fake_filefinder, fake_monitor)
-        pytddmon.main()
-        self.assertEqual(1, self.number_of_test_runs)
-
-    def test_runs_tests_when_change_detected(self):
-        fake_monitor = self.FakeMonitor(look_for_changes_returns = [True])
-        pytddmon = Pytddmon(self.fake_filefinder, fake_monitor)
-        pytddmon.main()
-        self.assertEqual(2, self.number_of_test_runs)
-
-    def test_doesnt_run_tests_when_no_change(self):
-        fake_monitor = self.FakeMonitor(look_for_changes_returns = [True, False])
-        pytddmon = Pytddmon(self.fake_filefinder, fake_monitor)
-        pytddmon.main()
-        pytddmon.main()
-        self.assertEqual(2, self.number_of_test_runs)
-
-    def test_runs_each_time_a_change_is_detected(self):
-        runs = 10
-        fake_monitor = self.FakeMonitor(look_for_changes_returns = [True]*runs)
-        pytddmon = Pytddmon(self.fake_filefinder, fake_monitor)
-        for _ in range(runs):
-            pytddmon.main()
-        self.assertEqual(runs + 1, self.number_of_test_runs)
-
-    def test_total_tests_is_zero_if_no_tests_are_run(self):
-        fake_monitor = self.FakeMonitor(look_for_changes_returns = [False])
-        pytddmon = Pytddmon(self.fake_filefinder, fake_monitor)
-        pytddmon.main()
-        self.assertEqual(0, pytddmon.total_tests_run)
-        
-    def test_total_tests_is_zero_if_no_tests_are_run(self):
-        fake_monitor = self.FakeMonitor(look_for_changes_returns = [False])
-        pytddmon = Pytddmon(self.fake_filefinder, fake_monitor)
-        pytddmon.main()
-        self.assertEqual(0, pytddmon.total_tests_run)
-
-if __name__ == '__main__':
-    unittest.main()

File src/tests/test_status_bar_feature.py

-# coding: utf-8
-import unittest
-
-import pytddmon
-
-
-class TestStatusBarFeature(unittest.TestCase):
-
-    def _test_gui_should_contain_a_status_bar(self):
-        # This test creates an X error in a running pytddmon session
-        import mock
-        gui = pytddmon.TkGUI(mock.Mock())
-        self.assertTrue(hasattr(gui, 'status_bar'))
-
-    def test_pytddmon_should_have_get_status_message_function(self):
-        self.assertTrue(hasattr(pytddmon.Pytddmon, 'get_status_message'))
-
-
-if __name__ == '__main__':
-    unittest.main()

File systest/Readme.txt

-Q: What is this directory?
-A: It contains so-called "system-level" tests for pytddmon.
-
-Q: What do you mean, "system level"?
-A: It means that they excercise pytddmon "as a whole", rather than testing it's parts, as the ordinary unit-tests do.
-
-Q: Why a 'systest' directory? Why not put them among the other test_*.py files?
-A: To clearly separate unit-level tests from system-level tests.
-
-Q: How are the system level tests run?
-A: The tests in this directory are not meant to be run by pytddmon. Run them with the
-systest.py script:
-
-python systest.py
-
-It will output which tests worked and which failed.
-
-Q: How is this organized?
-A: The systest.py script runs pytddmon.py in "test mode" (flag --log-and-exit) inside all
-subdirectories of systest. In each directory, there is an "expected.log" file. That file
-contains the correct output for pytddmon, for the directory in question. systest.py
-checks each and every expected.log, and compares it with the newly created pytddmon.log.
-For each broken expectation, an informative message is printed to stdout. Besides any
-test and/or code-files in the folder being tested, an optional "args.txt" may specify
-any arguments to pytddmon.

File systest/builtin_assert_works/expected.log

-green=1
-total=1

File systest/builtin_assert_works/test_user_builtin_assert.py

-# 
-
-import unittest
-
-class TestClass(unittest.TestCase):
-    def test1(self):
-        assert True
-

File systest/empty_folder/expected.log

-green=0
-total=0

File systest/file_with_name_of_stdlib_module/args.txt

-test.py

File systest/file_with_name_of_stdlib_module/expected.log

-green=1
-total=1

File systest/file_with_name_of_stdlib_module/test.py

-def fn():
-    '''
-    >>> fn()
-    5
-    '''
-    return 5

File systest/one_green_doctest/args.txt

-unit.py

File systest/one_green_doctest/expected.log

-green=1
-total=1

File systest/one_green_doctest/unit.py

-def fn():
-    '''
-    >>> fn()
-    5
-    '''
-    return 5

File systest/one_green_of_two_doctests/args.txt

-unit.py

File systest/one_green_of_two_doctests/expected.log

-green=1
-total=2

File systest/one_green_of_two_doctests/unit.py

-def fn1():
-    '''
-    >>> fn()
-    4
-    '''
-    return 5
-
-def fn2():
-    '''
-    >>> fn2()
-    5
-    '''
-    return 5

File systest/one_green_of_two_tests/expected.log

-green=1
-total=2

File systest/one_green_of_two_tests/test_code.py

-import unittest
-
-class TestCase(unittest.TestCase):
-    def test_something(self):
-        self.assertTrue(True)
-
-    def test_something_else(self):
-        self.assertTrue(False)

File systest/one_green_of_two_tests_no_args/expected.log

-green=1
-total=2

File systest/one_green_of_two_tests_no_args/test_code.py

-import unittest
-
-class TestCase(unittest.TestCase):
-    def test_something(self):
-        self.assertTrue(True)
-
-    def test_something_else(self):
-        self.assertTrue(False)

File systest/one_green_package_test/expected.log

-green=1
-total=1

File systest/one_green_package_test/package/__init__.py

Empty file removed.

File systest/one_green_package_test/package/test_code.py

-import unittest
-
-class TestCase(unittest.TestCase):
-    def test_something(self):
-        self.assertTrue(True)

File systest/one_green_test/expected.log

-green=1
-total=1

File systest/one_green_test/test_code.py

-import unittest
-
-class TestCase(unittest.TestCase):
-    def test_something(self):
-        self.assertTrue(True)

File systest/one_red_test/expected.log

-green=0
-total=1

File systest/one_red_test/test_code.py

-import unittest
-
-class TestCase(unittest.TestCase):
-    def test_something(self):
-        self.assertTrue(False)

File systest/print_dot_in_code_does_not_fool_counting/expected.log

-green=1
-total=1

File systest/print_dot_in_code_does_not_fool_counting/test_unit.py

-import unittest
-
-class TestCase(unittest.TestCase):
-    def test_something(self):
-        print('.')
-        self.assertTrue(True)

File systest/print_dot_in_code_does_not_fool_counting/unit.py

-def fn():
-    print('.')

File systest/print_dot_in_test_does_not_fool_counting/expected.log

-green=1
-total=1

File systest/print_dot_in_test_does_not_fool_counting/test_some.py

-import unittest
-
-class TestCase(unittest.TestCase):
-    def test_something(self):
-        print('.')
-        self.assertTrue(True)

File systest/rexursivescan_for_doctest/expected.log

-green=1
-total=1

File systest/rexursivescan_for_doctest/test_doctest.py

-def id_function(x):
-    """
-    >>> id_function(10)
-    10
-    """
-    return x
-

File systest/syntax_error/expected.log

-green=0
-total=1j

File systest/syntax_error/test_syntax_error.py

-# 
-
-import unittest
-
-class TestClass(unittest.TestCase):
-    def test_syntax_error(self):
-        assert True((
-

File systest/systest.py

-#! /usr/bin/env python
-#coding: utf-8
-import os
-import subprocess
-
-def get_log_as_dictionary(path):
-    f = open(path, 'r')
-    rows = f.readlines()
-    f.close()
-    dict = {}
-    for row in rows:
-        (name, splitter, value) = row.partition('=')
-        dict[name] = value.strip()
-    return dict
-    
-def get_log(testdir, logname):
-    fullpath = os.path.join(testdir, logname)
-    return get_log_as_dictionary(fullpath)
-
-def pretty_please(testdir):
-    testdir = testdir.replace('\\', '/')
-    testdir = testdir.split('/')[-1]
-    testdir = testdir.replace('_', ' ')
-    testdir = testdir.title()
-    return testdir
-
-def compare(testdir, what, gotdict, expdict):
-    got = gotdict[what]
-    exp = expdict[what]
-    pretty = pretty_please(testdir)
-    if got != exp:
-        print(pretty + ": expected " + exp + " " + what + " test(s), got " + got)
-
-def compare_logs(testdir, got, exp):
-    compare(testdir, 'green', got, exp)
-    compare(testdir, 'total', got, exp)
-
-def compare_logs_in_dir(testdir):
-    gotinfo = get_log(testdir, "pytddmon.log")
-    expinfo = get_log(testdir, "expected.log")
-    compare_logs(testdir, gotinfo, expinfo)
-    
-def get_args(path):
-    argspath = os.path.join(path, "args.txt")
-    if not os.path.exists(argspath):
-        return []
-    f = open(argspath, "r")
-    content = f.read().strip()
-    f.close()
-    return content.split()
-
-def run_all():
-    cwd = os.getcwd()
-    pytddmon_path = os.path.join(cwd, "../src/pytddmon.py")
-    names = os.listdir(cwd)
-    for name in names:
-        path = os.path.join(cwd, name)
-        if os.path.isdir(path):
-            os.chdir(path)
-            cmdline = ['python', pytddmon_path, "--log-and-exit"]
-            args = get_args(path)
-            cmdline.extend(args)
-            try: