This commit is contained in:
parent
be08a21dc7
commit
952b7d4394
@ -1,5 +1,5 @@
|
|||||||
<svg width="140" height="20" viewBox="0 0 140 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
<svg width="140" height="20" viewBox="0 0 140 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
<title>interrogate: 94.6%</title>
|
<title>interrogate: 92.5%</title>
|
||||||
<g transform="matrix(1,0,0,1,22,0)">
|
<g transform="matrix(1,0,0,1,22,0)">
|
||||||
<g id="backgrounds" transform="matrix(1.32789,0,0,1,-22.3892,0)">
|
<g id="backgrounds" transform="matrix(1.32789,0,0,1,-22.3892,0)">
|
||||||
<rect x="0" y="0" width="71" height="20" style="fill:rgb(85,85,85);"/>
|
<rect x="0" y="0" width="71" height="20" style="fill:rgb(85,85,85);"/>
|
||||||
@ -12,8 +12,8 @@
|
|||||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
|
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
|
||||||
<text x="590" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="610">interrogate</text>
|
<text x="590" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="610">interrogate</text>
|
||||||
<text x="590" y="140" transform="scale(.1)" textLength="610">interrogate</text>
|
<text x="590" y="140" transform="scale(.1)" textLength="610">interrogate</text>
|
||||||
<text x="1160" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370" data-interrogate="result">94.6%</text>
|
<text x="1160" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370" data-interrogate="result">92.5%</text>
|
||||||
<text x="1160" y="140" transform="scale(.1)" textLength="370" data-interrogate="result">94.6%</text>
|
<text x="1160" y="140" transform="scale(.1)" textLength="370" data-interrogate="result">92.5%</text>
|
||||||
</g>
|
</g>
|
||||||
<g id="logo-shadow" serif:id="logo shadow" transform="matrix(0.854876,0,0,0.854876,-6.73514,1.732)">
|
<g id="logo-shadow" serif:id="logo shadow" transform="matrix(0.854876,0,0,0.854876,-6.73514,1.732)">
|
||||||
<g transform="matrix(0.299012,0,0,0.299012,9.70229,-6.68582)">
|
<g transform="matrix(0.299012,0,0,0.299012,9.70229,-6.68582)">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -1 +1 @@
|
|||||||
35.0%
|
41.1%
|
||||||
File diff suppressed because one or more lines are too long
652
coverage.xml
652
coverage.xml
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" ?>
|
<?xml version="1.0" ?>
|
||||||
<coverage version="7.8.2" timestamp="1749313463565" lines-valid="3863" lines-covered="1591" line-rate="0.4119" branches-valid="1384" branches-covered="247" branch-rate="0.1785" complexity="0">
|
<coverage version="7.8.2" timestamp="1749313869935" lines-valid="3273" lines-covered="1564" line-rate="0.4778" branches-valid="1132" branches-covered="247" branch-rate="0.2182" complexity="0">
|
||||||
<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.8.2 -->
|
<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.8.2 -->
|
||||||
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
|
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
|
||||||
<sources>
|
<sources>
|
||||||
<source>/home/dtourolle/Development/pyWebLayout/pyWebLayout</source>
|
<source>/home/dtourolle/Development/pyWebLayout/pyWebLayout</source>
|
||||||
</sources>
|
</sources>
|
||||||
<packages>
|
<packages>
|
||||||
<package name="." line-rate="0.07566" branch-rate="0" complexity="0">
|
<package name="." line-rate="1" branch-rate="1" complexity="0">
|
||||||
<classes>
|
<classes>
|
||||||
<class name="__init__.py" filename="__init__.py" complexity="0" line-rate="1" branch-rate="1">
|
<class name="__init__.py" filename="__init__.py" complexity="0" line-rate="1" branch-rate="1">
|
||||||
<methods/>
|
<methods/>
|
||||||
@ -20,634 +20,7 @@
|
|||||||
<line number="31" hits="1"/>
|
<line number="31" hits="1"/>
|
||||||
<line number="32" hits="1"/>
|
<line number="32" hits="1"/>
|
||||||
<line number="35" hits="1"/>
|
<line number="35" hits="1"/>
|
||||||
<line number="38" hits="1"/>
|
<line number="39" hits="1"/>
|
||||||
<line number="41" hits="1"/>
|
|
||||||
</lines>
|
|
||||||
</class>
|
|
||||||
<class name="base.py" filename="base.py" complexity="0" line-rate="0.6842" branch-rate="0">
|
|
||||||
<methods/>
|
|
||||||
<lines>
|
|
||||||
<line number="2" hits="1"/>
|
|
||||||
<line number="3" hits="1"/>
|
|
||||||
<line number="5" hits="1"/>
|
|
||||||
<line number="8" hits="1"/>
|
|
||||||
<line number="13" hits="1"/>
|
|
||||||
<line number="20" hits="0"/>
|
|
||||||
<line number="22" hits="1"/>
|
|
||||||
<line number="27" hits="1"/>
|
|
||||||
<line number="34" hits="1"/>
|
|
||||||
<line number="36" hits="1"/>
|
|
||||||
<line number="46" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="47,48"/>
|
|
||||||
<line number="47" hits="0"/>
|
|
||||||
<line number="48" hits="0"/>
|
|
||||||
<line number="50" hits="1"/>
|
|
||||||
<line number="55" hits="1"/>
|
|
||||||
<line number="60" hits="0"/>
|
|
||||||
<line number="62" hits="1"/>
|
|
||||||
<line number="64" hits="1"/>
|
|
||||||
<line number="68" hits="0"/>
|
|
||||||
</lines>
|
|
||||||
</class>
|
|
||||||
<class name="html_parser.py" filename="html_parser.py" complexity="0" line-rate="0" branch-rate="0">
|
|
||||||
<methods/>
|
|
||||||
<lines>
|
|
||||||
<line number="1" hits="0"/>
|
|
||||||
<line number="2" hits="0"/>
|
|
||||||
<line number="3" hits="0"/>
|
|
||||||
<line number="4" hits="0"/>
|
|
||||||
<line number="5" hits="0"/>
|
|
||||||
<line number="7" hits="0"/>
|
|
||||||
<line number="8" hits="0"/>
|
|
||||||
<line number="9" hits="0"/>
|
|
||||||
<line number="13" hits="0"/>
|
|
||||||
<line number="14" hits="0"/>
|
|
||||||
<line number="15" hits="0"/>
|
|
||||||
<line number="16" hits="0"/>
|
|
||||||
<line number="19" hits="0"/>
|
|
||||||
<line number="25" hits="0"/>
|
|
||||||
<line number="32" hits="0"/>
|
|
||||||
<line number="35" hits="0"/>
|
|
||||||
<line number="38" hits="0"/>
|
|
||||||
<line number="39" hits="0"/>
|
|
||||||
<line number="42" hits="0"/>
|
|
||||||
<line number="43" hits="0"/>
|
|
||||||
<line number="44" hits="0"/>
|
|
||||||
<line number="47" hits="0"/>
|
|
||||||
<line number="48" hits="0"/>
|
|
||||||
<line number="59" hits="0"/>
|
|
||||||
<line number="60" hits="0"/>
|
|
||||||
<line number="61" hits="0"/>
|
|
||||||
<line number="64" hits="0"/>
|
|
||||||
<line number="65" hits="0"/>
|
|
||||||
<line number="66" hits="0"/>
|
|
||||||
<line number="69" hits="0"/>
|
|
||||||
<line number="70" hits="0"/>
|
|
||||||
<line number="71" hits="0"/>
|
|
||||||
<line number="72" hits="0"/>
|
|
||||||
<line number="73" hits="0"/>
|
|
||||||
<line number="74" hits="0"/>
|
|
||||||
<line number="75" hits="0"/>
|
|
||||||
<line number="77" hits="0"/>
|
|
||||||
<line number="85" hits="0"/>
|
|
||||||
<line number="86" hits="0"/>
|
|
||||||
<line number="89" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="90,91"/>
|
|
||||||
<line number="90" hits="0"/>
|
|
||||||
<line number="91" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="92,95"/>
|
|
||||||
<line number="92" hits="0"/>
|
|
||||||
<line number="95" hits="0"/>
|
|
||||||
<line number="96" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="97,100"/>
|
|
||||||
<line number="97" hits="0"/>
|
|
||||||
<line number="100" hits="0"/>
|
|
||||||
<line number="101" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="102,106"/>
|
|
||||||
<line number="102" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="101,103"/>
|
|
||||||
<line number="103" hits="0"/>
|
|
||||||
<line number="106" hits="0"/>
|
|
||||||
<line number="109" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="111,114"/>
|
|
||||||
<line number="111" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="exit,112"/>
|
|
||||||
<line number="112" hits="0"/>
|
|
||||||
<line number="114" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="115,117"/>
|
|
||||||
<line number="115" hits="0"/>
|
|
||||||
<line number="117" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="118,121"/>
|
|
||||||
<line number="118" hits="0"/>
|
|
||||||
<line number="119" hits="0"/>
|
|
||||||
<line number="121" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="122,124"/>
|
|
||||||
<line number="122" hits="0"/>
|
|
||||||
<line number="124" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="125,127"/>
|
|
||||||
<line number="125" hits="0"/>
|
|
||||||
<line number="127" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="128,131"/>
|
|
||||||
<line number="128" hits="0"/>
|
|
||||||
<line number="129" hits="0"/>
|
|
||||||
<line number="131" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="132,135"/>
|
|
||||||
<line number="132" hits="0"/>
|
|
||||||
<line number="133" hits="0"/>
|
|
||||||
<line number="135" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="137,139"/>
|
|
||||||
<line number="137" hits="0"/>
|
|
||||||
<line number="139" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="140,153"/>
|
|
||||||
<line number="140" hits="0"/>
|
|
||||||
<line number="141" hits="0"/>
|
|
||||||
<line number="144" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="145,147"/>
|
|
||||||
<line number="145" hits="0"/>
|
|
||||||
<line number="147" hits="0"/>
|
|
||||||
<line number="150" hits="0"/>
|
|
||||||
<line number="151" hits="0"/>
|
|
||||||
<line number="153" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="154,179"/>
|
|
||||||
<line number="154" hits="0"/>
|
|
||||||
<line number="157" hits="0"/>
|
|
||||||
<line number="166" hits="0"/>
|
|
||||||
<line number="169" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="170,172"/>
|
|
||||||
<line number="170" hits="0"/>
|
|
||||||
<line number="172" hits="0"/>
|
|
||||||
<line number="175" hits="0"/>
|
|
||||||
<line number="176" hits="0"/>
|
|
||||||
<line number="177" hits="0"/>
|
|
||||||
<line number="179" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="180,196"/>
|
|
||||||
<line number="180" hits="0"/>
|
|
||||||
<line number="183" hits="0"/>
|
|
||||||
<line number="186" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="187,189"/>
|
|
||||||
<line number="187" hits="0"/>
|
|
||||||
<line number="189" hits="0"/>
|
|
||||||
<line number="192" hits="0"/>
|
|
||||||
<line number="193" hits="0"/>
|
|
||||||
<line number="194" hits="0"/>
|
|
||||||
<line number="196" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="197,211"/>
|
|
||||||
<line number="197" hits="0"/>
|
|
||||||
<line number="199" hits="0"/>
|
|
||||||
<line number="202" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="203,205"/>
|
|
||||||
<line number="203" hits="0"/>
|
|
||||||
<line number="205" hits="0"/>
|
|
||||||
<line number="208" hits="0"/>
|
|
||||||
<line number="209" hits="0"/>
|
|
||||||
<line number="211" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="212,230"/>
|
|
||||||
<line number="212" hits="0"/>
|
|
||||||
<line number="217" hits="0"/>
|
|
||||||
<line number="220" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="221,223"/>
|
|
||||||
<line number="221" hits="0"/>
|
|
||||||
<line number="223" hits="0"/>
|
|
||||||
<line number="226" hits="0"/>
|
|
||||||
<line number="227" hits="0"/>
|
|
||||||
<line number="228" hits="0"/>
|
|
||||||
<line number="230" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="232,262"/>
|
|
||||||
<line number="232" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="233,260"/>
|
|
||||||
<line number="233" hits="0"/>
|
|
||||||
<line number="236" hits="0"/>
|
|
||||||
<line number="237" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="238,242"/>
|
|
||||||
<line number="238" hits="0"/>
|
|
||||||
<line number="239" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="240,242"/>
|
|
||||||
<line number="240" hits="0"/>
|
|
||||||
<line number="242" hits="0"/>
|
|
||||||
<line number="245" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="246,255"/>
|
|
||||||
<line number="246" hits="0"/>
|
|
||||||
<line number="247" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="249,255"/>
|
|
||||||
<line number="249" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="250,255"/>
|
|
||||||
<line number="250" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="249,251"/>
|
|
||||||
<line number="251" hits="0"/>
|
|
||||||
<line number="252" hits="0"/>
|
|
||||||
<line number="255" hits="0"/>
|
|
||||||
<line number="256" hits="0"/>
|
|
||||||
<line number="257" hits="0"/>
|
|
||||||
<line number="260" hits="0"/>
|
|
||||||
<line number="262" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="263,286"/>
|
|
||||||
<line number="263" hits="0"/>
|
|
||||||
<line number="266" hits="0"/>
|
|
||||||
<line number="272" hits="0"/>
|
|
||||||
<line number="275" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="276,278"/>
|
|
||||||
<line number="276" hits="0"/>
|
|
||||||
<line number="278" hits="0"/>
|
|
||||||
<line number="281" hits="0"/>
|
|
||||||
<line number="282" hits="0"/>
|
|
||||||
<line number="283" hits="0"/>
|
|
||||||
<line number="284" hits="0"/>
|
|
||||||
<line number="286" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="287,300"/>
|
|
||||||
<line number="287" hits="0"/>
|
|
||||||
<line number="289" hits="0"/>
|
|
||||||
<line number="292" hits="0"/>
|
|
||||||
<line number="293" hits="0"/>
|
|
||||||
<line number="296" hits="0"/>
|
|
||||||
<line number="297" hits="0"/>
|
|
||||||
<line number="298" hits="0"/>
|
|
||||||
<line number="300" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="301,319"/>
|
|
||||||
<line number="301" hits="0"/>
|
|
||||||
<line number="304" hits="0"/>
|
|
||||||
<line number="307" hits="0"/>
|
|
||||||
<line number="308" hits="0"/>
|
|
||||||
<line number="311" hits="0"/>
|
|
||||||
<line number="312" hits="0"/>
|
|
||||||
<line number="315" hits="0"/>
|
|
||||||
<line number="316" hits="0"/>
|
|
||||||
<line number="317" hits="0"/>
|
|
||||||
<line number="319" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="320,347"/>
|
|
||||||
<line number="320" hits="0"/>
|
|
||||||
<line number="323" hits="0"/>
|
|
||||||
<line number="324" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="325,335"/>
|
|
||||||
<line number="325" hits="0"/>
|
|
||||||
<line number="328" hits="0"/>
|
|
||||||
<line number="329" hits="0"/>
|
|
||||||
<line number="332" hits="0"/>
|
|
||||||
<line number="335" hits="0"/>
|
|
||||||
<line number="336" hits="0"/>
|
|
||||||
<line number="339" hits="0"/>
|
|
||||||
<line number="340" hits="0"/>
|
|
||||||
<line number="343" hits="0"/>
|
|
||||||
<line number="344" hits="0"/>
|
|
||||||
<line number="345" hits="0"/>
|
|
||||||
<line number="347" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="348,369"/>
|
|
||||||
<line number="348" hits="0"/>
|
|
||||||
<line number="351" hits="0"/>
|
|
||||||
<line number="352" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="353,355"/>
|
|
||||||
<line number="353" hits="0"/>
|
|
||||||
<line number="355" hits="0"/>
|
|
||||||
<line number="358" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="359,361"/>
|
|
||||||
<line number="359" hits="0"/>
|
|
||||||
<line number="361" hits="0"/>
|
|
||||||
<line number="364" hits="0"/>
|
|
||||||
<line number="365" hits="0"/>
|
|
||||||
<line number="366" hits="0"/>
|
|
||||||
<line number="367" hits="0"/>
|
|
||||||
<line number="369" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="371,373"/>
|
|
||||||
<line number="371" hits="0"/>
|
|
||||||
<line number="373" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="374,396"/>
|
|
||||||
<line number="374" hits="0"/>
|
|
||||||
<line number="377" hits="0"/>
|
|
||||||
<line number="380" hits="0"/>
|
|
||||||
<line number="383" hits="0"/>
|
|
||||||
<line number="384" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="385,390"/>
|
|
||||||
<line number="385" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="386,387"/>
|
|
||||||
<line number="386" hits="0"/>
|
|
||||||
<line number="387" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="388,390"/>
|
|
||||||
<line number="388" hits="0"/>
|
|
||||||
<line number="390" hits="0"/>
|
|
||||||
<line number="393" hits="0"/>
|
|
||||||
<line number="394" hits="0"/>
|
|
||||||
<line number="396" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="397,431"/>
|
|
||||||
<line number="397" hits="0"/>
|
|
||||||
<line number="400" hits="0"/>
|
|
||||||
<line number="401" hits="0"/>
|
|
||||||
<line number="403" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="404,409"/>
|
|
||||||
<line number="404" hits="0"/>
|
|
||||||
<line number="405" hits="0"/>
|
|
||||||
<line number="406" hits="0"/>
|
|
||||||
<line number="407" hits="0"/>
|
|
||||||
<line number="409" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="410,416"/>
|
|
||||||
<line number="410" hits="0"/>
|
|
||||||
<line number="411" hits="0"/>
|
|
||||||
<line number="412" hits="0"/>
|
|
||||||
<line number="413" hits="0"/>
|
|
||||||
<line number="416" hits="0"/>
|
|
||||||
<line number="417" hits="0"/>
|
|
||||||
<line number="420" hits="0"/>
|
|
||||||
<line number="423" hits="0"/>
|
|
||||||
<line number="424" hits="0"/>
|
|
||||||
<line number="427" hits="0"/>
|
|
||||||
<line number="428" hits="0"/>
|
|
||||||
<line number="429" hits="0"/>
|
|
||||||
<line number="431" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="432,465"/>
|
|
||||||
<line number="432" hits="0"/>
|
|
||||||
<line number="435" hits="0"/>
|
|
||||||
<line number="436" hits="0"/>
|
|
||||||
<line number="439" hits="0"/>
|
|
||||||
<line number="440" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="441,442"/>
|
|
||||||
<line number="441" hits="0"/>
|
|
||||||
<line number="442" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="443,444"/>
|
|
||||||
<line number="443" hits="0"/>
|
|
||||||
<line number="444" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="445,449"/>
|
|
||||||
<line number="445" hits="0"/>
|
|
||||||
<line number="446" hits="0"/>
|
|
||||||
<line number="449" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="450,453"/>
|
|
||||||
<line number="450" hits="0"/>
|
|
||||||
<line number="453" hits="0"/>
|
|
||||||
<line number="460" hits="0"/>
|
|
||||||
<line number="463" hits="0"/>
|
|
||||||
<line number="465" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="467,510"/>
|
|
||||||
<line number="467" hits="0"/>
|
|
||||||
<line number="468" hits="0"/>
|
|
||||||
<line number="471" hits="0"/>
|
|
||||||
<line number="472" hits="0"/>
|
|
||||||
<line number="473" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="474,479"/>
|
|
||||||
<line number="474" hits="0"/>
|
|
||||||
<line number="475" hits="0"/>
|
|
||||||
<line number="476" hits="0"/>
|
|
||||||
<line number="477" hits="0"/>
|
|
||||||
<line number="479" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="480,486"/>
|
|
||||||
<line number="480" hits="0"/>
|
|
||||||
<line number="481" hits="0"/>
|
|
||||||
<line number="482" hits="0"/>
|
|
||||||
<line number="483" hits="0"/>
|
|
||||||
<line number="486" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="487,490"/>
|
|
||||||
<line number="487" hits="0"/>
|
|
||||||
<line number="490" hits="0"/>
|
|
||||||
<line number="491" hits="0"/>
|
|
||||||
<line number="494" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="495,497"/>
|
|
||||||
<line number="495" hits="0"/>
|
|
||||||
<line number="497" hits="0"/>
|
|
||||||
<line number="500" hits="0"/>
|
|
||||||
<line number="501" hits="0"/>
|
|
||||||
<line number="510" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="513,521"/>
|
|
||||||
<line number="513" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="514,519"/>
|
|
||||||
<line number="514" hits="0"/>
|
|
||||||
<line number="515" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="516,519"/>
|
|
||||||
<line number="516" hits="0"/>
|
|
||||||
<line number="519" hits="0"/>
|
|
||||||
<line number="521" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="522,533"/>
|
|
||||||
<line number="522" hits="0"/>
|
|
||||||
<line number="525" hits="0"/>
|
|
||||||
<line number="528" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="529,531"/>
|
|
||||||
<line number="529" hits="0"/>
|
|
||||||
<line number="531" hits="0"/>
|
|
||||||
<line number="533" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="535,538"/>
|
|
||||||
<line number="535" hits="0"/>
|
|
||||||
<line number="536" hits="0"/>
|
|
||||||
<line number="538" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="540,543"/>
|
|
||||||
<line number="540" hits="0"/>
|
|
||||||
<line number="541" hits="0"/>
|
|
||||||
<line number="543" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="545,548"/>
|
|
||||||
<line number="545" hits="0"/>
|
|
||||||
<line number="546" hits="0"/>
|
|
||||||
<line number="548" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="550,552"/>
|
|
||||||
<line number="550" hits="0"/>
|
|
||||||
<line number="552" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="553,567"/>
|
|
||||||
<line number="553" hits="0"/>
|
|
||||||
<line number="556" hits="0"/>
|
|
||||||
<line number="557" hits="0"/>
|
|
||||||
<line number="560" hits="0"/>
|
|
||||||
<line number="563" hits="0"/>
|
|
||||||
<line number="567" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="569,603"/>
|
|
||||||
<line number="569" hits="0"/>
|
|
||||||
<line number="570" hits="0"/>
|
|
||||||
<line number="571" hits="0"/>
|
|
||||||
<line number="572" hits="0"/>
|
|
||||||
<line number="575" hits="0"/>
|
|
||||||
<line number="590" hits="0"/>
|
|
||||||
<line number="593" hits="0"/>
|
|
||||||
<line number="603" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="606,608"/>
|
|
||||||
<line number="606" hits="0"/>
|
|
||||||
<line number="608" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="611,613"/>
|
|
||||||
<line number="611" hits="0"/>
|
|
||||||
<line number="613" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="exit,615"/>
|
|
||||||
<line number="615" hits="0"/>
|
|
||||||
<line number="616" hits="0"/>
|
|
||||||
<line number="620" hits="0"/>
|
|
||||||
<line number="627" hits="0"/>
|
|
||||||
<line number="630" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="631,637"/>
|
|
||||||
<line number="631" hits="0"/>
|
|
||||||
<line number="632" hits="0"/>
|
|
||||||
<line number="633" hits="0"/>
|
|
||||||
<line number="634" hits="0"/>
|
|
||||||
<line number="635" hits="0"/>
|
|
||||||
<line number="637" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="638,647"/>
|
|
||||||
<line number="638" hits="0"/>
|
|
||||||
<line number="640" hits="0"/>
|
|
||||||
<line number="641" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="642,643"/>
|
|
||||||
<line number="642" hits="0"/>
|
|
||||||
<line number="643" hits="0"/>
|
|
||||||
<line number="644" hits="0"/>
|
|
||||||
<line number="645" hits="0"/>
|
|
||||||
<line number="647" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="648,654"/>
|
|
||||||
<line number="648" hits="0"/>
|
|
||||||
<line number="649" hits="0"/>
|
|
||||||
<line number="650" hits="0"/>
|
|
||||||
<line number="651" hits="0"/>
|
|
||||||
<line number="652" hits="0"/>
|
|
||||||
<line number="654" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="655,656"/>
|
|
||||||
<line number="655" hits="0"/>
|
|
||||||
<line number="656" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="657,660"/>
|
|
||||||
<line number="657" hits="0"/>
|
|
||||||
<line number="660" hits="0"/>
|
|
||||||
<line number="663" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="664,666"/>
|
|
||||||
<line number="664" hits="0"/>
|
|
||||||
<line number="666" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="667,669"/>
|
|
||||||
<line number="667" hits="0"/>
|
|
||||||
<line number="669" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="671,684"/>
|
|
||||||
<line number="671" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="672,675"/>
|
|
||||||
<line number="672" hits="0"/>
|
|
||||||
<line number="675" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="676,678"/>
|
|
||||||
<line number="676" hits="0"/>
|
|
||||||
<line number="678" hits="0"/>
|
|
||||||
<line number="681" hits="0"/>
|
|
||||||
<line number="682" hits="0"/>
|
|
||||||
<line number="684" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="686,688"/>
|
|
||||||
<line number="686" hits="0"/>
|
|
||||||
<line number="688" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="690,706"/>
|
|
||||||
<line number="690" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="691,693"/>
|
|
||||||
<line number="691" hits="0"/>
|
|
||||||
<line number="693" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="694,697"/>
|
|
||||||
<line number="694" hits="0"/>
|
|
||||||
<line number="697" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="698,700"/>
|
|
||||||
<line number="698" hits="0"/>
|
|
||||||
<line number="700" hits="0"/>
|
|
||||||
<line number="703" hits="0"/>
|
|
||||||
<line number="704" hits="0"/>
|
|
||||||
<line number="706" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="708,721"/>
|
|
||||||
<line number="708" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="709,712"/>
|
|
||||||
<line number="709" hits="0"/>
|
|
||||||
<line number="712" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="713,715"/>
|
|
||||||
<line number="713" hits="0"/>
|
|
||||||
<line number="715" hits="0"/>
|
|
||||||
<line number="718" hits="0"/>
|
|
||||||
<line number="719" hits="0"/>
|
|
||||||
<line number="721" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="723,742"/>
|
|
||||||
<line number="723" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="724,726"/>
|
|
||||||
<line number="724" hits="0"/>
|
|
||||||
<line number="726" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="727,730"/>
|
|
||||||
<line number="727" hits="0"/>
|
|
||||||
<line number="730" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="731,733"/>
|
|
||||||
<line number="731" hits="0"/>
|
|
||||||
<line number="733" hits="0"/>
|
|
||||||
<line number="736" hits="0"/>
|
|
||||||
<line number="737" hits="0"/>
|
|
||||||
<line number="738" hits="0"/>
|
|
||||||
<line number="739" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="740,776"/>
|
|
||||||
<line number="740" hits="0"/>
|
|
||||||
<line number="742" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="744,747"/>
|
|
||||||
<line number="744" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="745,776"/>
|
|
||||||
<line number="745" hits="0"/>
|
|
||||||
<line number="747" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="749,751"/>
|
|
||||||
<line number="749" hits="0"/>
|
|
||||||
<line number="751" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="753,766"/>
|
|
||||||
<line number="753" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="754,757"/>
|
|
||||||
<line number="754" hits="0"/>
|
|
||||||
<line number="757" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="758,760"/>
|
|
||||||
<line number="758" hits="0"/>
|
|
||||||
<line number="760" hits="0"/>
|
|
||||||
<line number="763" hits="0"/>
|
|
||||||
<line number="764" hits="0"/>
|
|
||||||
<line number="766" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="768,771"/>
|
|
||||||
<line number="768" hits="0"/>
|
|
||||||
<line number="769" hits="0"/>
|
|
||||||
<line number="771" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="773,776"/>
|
|
||||||
<line number="773" hits="0"/>
|
|
||||||
<line number="776" hits="0"/>
|
|
||||||
<line number="778" hits="0"/>
|
|
||||||
<line number="785" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="786,789"/>
|
|
||||||
<line number="786" hits="0"/>
|
|
||||||
<line number="787" hits="0"/>
|
|
||||||
<line number="789" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="790,793"/>
|
|
||||||
<line number="790" hits="0"/>
|
|
||||||
<line number="791" hits="0"/>
|
|
||||||
<line number="793" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="794,798"/>
|
|
||||||
<line number="794" hits="0"/>
|
|
||||||
<line number="795" hits="0"/>
|
|
||||||
<line number="798" hits="0"/>
|
|
||||||
<line number="800" hits="0"/>
|
|
||||||
<line number="808" hits="0"/>
|
|
||||||
<line number="820" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="821,823"/>
|
|
||||||
<line number="821" hits="0"/>
|
|
||||||
<line number="823" hits="0"/>
|
|
||||||
<line number="824" hits="0"/>
|
|
||||||
<line number="825" hits="0"/>
|
|
||||||
<line number="826" hits="0"/>
|
|
||||||
<line number="827" hits="0"/>
|
|
||||||
<line number="830" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="831,832"/>
|
|
||||||
<line number="831" hits="0"/>
|
|
||||||
<line number="832" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="833,834"/>
|
|
||||||
<line number="833" hits="0"/>
|
|
||||||
<line number="834" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="835,837"/>
|
|
||||||
<line number="835" hits="0"/>
|
|
||||||
<line number="837" hits="0"/>
|
|
||||||
<line number="839" hits="0"/>
|
|
||||||
<line number="847" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="849,852"/>
|
|
||||||
<line number="849" hits="0"/>
|
|
||||||
<line number="852" hits="0"/>
|
|
||||||
<line number="855" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="856,857"/>
|
|
||||||
<line number="856" hits="0"/>
|
|
||||||
<line number="857" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="858,859"/>
|
|
||||||
<line number="858" hits="0"/>
|
|
||||||
<line number="859" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="860,862"/>
|
|
||||||
<line number="860" hits="0"/>
|
|
||||||
<line number="862" hits="0"/>
|
|
||||||
<line number="864" hits="0"/>
|
|
||||||
<line number="872" hits="0"/>
|
|
||||||
<line number="875" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="exit,876"/>
|
|
||||||
<line number="876" hits="0"/>
|
|
||||||
<line number="878" hits="0"/>
|
|
||||||
<line number="880" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="exit,881"/>
|
|
||||||
<line number="881" hits="0"/>
|
|
||||||
<line number="883" hits="0"/>
|
|
||||||
<line number="894" hits="0"/>
|
|
||||||
<line number="911" hits="0"/>
|
|
||||||
<line number="913" hits="0"/>
|
|
||||||
</lines>
|
|
||||||
</class>
|
|
||||||
<class name="layout.py" filename="layout.py" complexity="0" line-rate="1" branch-rate="1">
|
|
||||||
<methods/>
|
|
||||||
<lines>
|
|
||||||
<line number="1" hits="1"/>
|
|
||||||
<line number="3" hits="1"/>
|
|
||||||
<line number="4" hits="1"/>
|
|
||||||
<line number="5" hits="1"/>
|
|
||||||
<line number="6" hits="1"/>
|
|
||||||
<line number="7" hits="1"/>
|
|
||||||
<line number="8" hits="1"/>
|
|
||||||
<line number="9" hits="1"/>
|
|
||||||
</lines>
|
|
||||||
</class>
|
|
||||||
<class name="localisation.py" filename="localisation.py" complexity="0" line-rate="1" branch-rate="1">
|
|
||||||
<methods/>
|
|
||||||
<lines/>
|
|
||||||
</class>
|
|
||||||
<class name="style.py" filename="style.py" complexity="0" line-rate="0" branch-rate="0">
|
|
||||||
<methods/>
|
|
||||||
<lines>
|
|
||||||
<line number="2" hits="0"/>
|
|
||||||
<line number="3" hits="0"/>
|
|
||||||
<line number="4" hits="0"/>
|
|
||||||
<line number="7" hits="0"/>
|
|
||||||
<line number="8" hits="0"/>
|
|
||||||
<line number="9" hits="0"/>
|
|
||||||
<line number="12" hits="0"/>
|
|
||||||
<line number="13" hits="0"/>
|
|
||||||
<line number="14" hits="0"/>
|
|
||||||
<line number="17" hits="0"/>
|
|
||||||
<line number="18" hits="0"/>
|
|
||||||
<line number="19" hits="0"/>
|
|
||||||
<line number="20" hits="0"/>
|
|
||||||
<line number="23" hits="0"/>
|
|
||||||
<line number="29" hits="0"/>
|
|
||||||
<line number="50" hits="0"/>
|
|
||||||
<line number="51" hits="0"/>
|
|
||||||
<line number="52" hits="0"/>
|
|
||||||
<line number="53" hits="0"/>
|
|
||||||
<line number="54" hits="0"/>
|
|
||||||
<line number="55" hits="0"/>
|
|
||||||
<line number="56" hits="0"/>
|
|
||||||
<line number="57" hits="0"/>
|
|
||||||
<line number="59" hits="0"/>
|
|
||||||
<line number="61" hits="0"/>
|
|
||||||
<line number="63" hits="0"/>
|
|
||||||
<line number="64" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="65,71"/>
|
|
||||||
<line number="65" hits="0"/>
|
|
||||||
<line number="71" hits="0"/>
|
|
||||||
<line number="72" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="exit,73"/>
|
|
||||||
<line number="73" hits="0"/>
|
|
||||||
<line number="74" hits="0"/>
|
|
||||||
<line number="75" hits="0"/>
|
|
||||||
<line number="76" hits="0"/>
|
|
||||||
<line number="78" hits="0"/>
|
|
||||||
<line number="79" hits="0"/>
|
|
||||||
<line number="81" hits="0"/>
|
|
||||||
<line number="83" hits="0"/>
|
|
||||||
<line number="84" hits="0"/>
|
|
||||||
<line number="86" hits="0"/>
|
|
||||||
<line number="88" hits="0"/>
|
|
||||||
<line number="89" hits="0"/>
|
|
||||||
<line number="91" hits="0"/>
|
|
||||||
<line number="93" hits="0"/>
|
|
||||||
<line number="94" hits="0"/>
|
|
||||||
<line number="96" hits="0"/>
|
|
||||||
<line number="98" hits="0"/>
|
|
||||||
<line number="99" hits="0"/>
|
|
||||||
<line number="101" hits="0"/>
|
|
||||||
<line number="103" hits="0"/>
|
|
||||||
<line number="104" hits="0"/>
|
|
||||||
<line number="106" hits="0"/>
|
|
||||||
<line number="108" hits="0"/>
|
|
||||||
<line number="109" hits="0"/>
|
|
||||||
<line number="111" hits="0"/>
|
|
||||||
<line number="113" hits="0"/>
|
|
||||||
<line number="114" hits="0"/>
|
|
||||||
<line number="116" hits="0"/>
|
|
||||||
<line number="118" hits="0"/>
|
|
||||||
<line number="120" hits="0"/>
|
|
||||||
<line number="130" hits="0"/>
|
|
||||||
<line number="132" hits="0"/>
|
|
||||||
<line number="142" hits="0"/>
|
|
||||||
<line number="144" hits="0"/>
|
|
||||||
<line number="154" hits="0"/>
|
|
||||||
<line number="156" hits="0"/>
|
|
||||||
<line number="166" hits="0"/>
|
|
||||||
<line number="168" hits="0"/>
|
|
||||||
</lines>
|
|
||||||
</class>
|
|
||||||
<class name="table.py" filename="table.py" complexity="0" line-rate="0.2917" branch-rate="0">
|
|
||||||
<methods/>
|
|
||||||
<lines>
|
|
||||||
<line number="1" hits="1"/>
|
|
||||||
<line number="2" hits="1"/>
|
|
||||||
<line number="3" hits="1"/>
|
|
||||||
<line number="5" hits="1"/>
|
|
||||||
<line number="6" hits="1"/>
|
|
||||||
<line number="7" hits="1"/>
|
|
||||||
<line number="10" hits="1"/>
|
|
||||||
<line number="11" hits="1"/>
|
|
||||||
<line number="29" hits="0"/>
|
|
||||||
<line number="30" hits="0"/>
|
|
||||||
<line number="31" hits="0"/>
|
|
||||||
<line number="33" hits="1"/>
|
|
||||||
<line number="35" hits="0"/>
|
|
||||||
<line number="37" hits="1"/>
|
|
||||||
<line number="40" hits="0"/>
|
|
||||||
<line number="41" hits="0"/>
|
|
||||||
<line number="44" hits="0"/>
|
|
||||||
<line number="47" hits="0"/>
|
|
||||||
<line number="50" hits="1"/>
|
|
||||||
<line number="51" hits="1"/>
|
|
||||||
<line number="70" hits="0"/>
|
|
||||||
<line number="72" hits="0"/>
|
|
||||||
<line number="73" hits="0"/>
|
|
||||||
<line number="74" hits="0"/>
|
|
||||||
<line number="77" hits="0"/>
|
|
||||||
<line number="78" hits="0"/>
|
|
||||||
<line number="81" hits="0"/>
|
|
||||||
<line number="83" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="exit,84"/>
|
|
||||||
<line number="84" hits="0"/>
|
|
||||||
<line number="85" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="87,103"/>
|
|
||||||
<line number="87" hits="0"/>
|
|
||||||
<line number="88" hits="0"/>
|
|
||||||
<line number="91" hits="0"/>
|
|
||||||
<line number="101" hits="0"/>
|
|
||||||
<line number="103" hits="0"/>
|
|
||||||
<line number="105" hits="1"/>
|
|
||||||
<line number="114" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="115,117"/>
|
|
||||||
<line number="115" hits="0"/>
|
|
||||||
<line number="117" hits="0"/>
|
|
||||||
<line number="119" hits="1"/>
|
|
||||||
<line number="122" hits="0"/>
|
|
||||||
<line number="125" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="126,137"/>
|
|
||||||
<line number="126" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="125,127"/>
|
|
||||||
<line number="127" hits="0"/>
|
|
||||||
<line number="128" hits="0"/>
|
|
||||||
<line number="131" hits="0"/>
|
|
||||||
<line number="135" hits="0"/>
|
|
||||||
<line number="137" hits="0"/>
|
|
||||||
</lines>
|
</lines>
|
||||||
</class>
|
</class>
|
||||||
</classes>
|
</classes>
|
||||||
@ -2353,7 +1726,7 @@
|
|||||||
</class>
|
</class>
|
||||||
</classes>
|
</classes>
|
||||||
</package>
|
</package>
|
||||||
<package name="core" line-rate="0.65" branch-rate="0" complexity="0">
|
<package name="core" line-rate="0.7" branch-rate="0" complexity="0">
|
||||||
<classes>
|
<classes>
|
||||||
<class name="__init__.py" filename="core/__init__.py" complexity="0" line-rate="1" branch-rate="1">
|
<class name="__init__.py" filename="core/__init__.py" complexity="0" line-rate="1" branch-rate="1">
|
||||||
<methods/>
|
<methods/>
|
||||||
@ -2361,7 +1734,7 @@
|
|||||||
<line number="8" hits="1"/>
|
<line number="8" hits="1"/>
|
||||||
</lines>
|
</lines>
|
||||||
</class>
|
</class>
|
||||||
<class name="base.py" filename="core/base.py" complexity="0" line-rate="0.6316" branch-rate="0">
|
<class name="base.py" filename="core/base.py" complexity="0" line-rate="0.6842" branch-rate="0">
|
||||||
<methods/>
|
<methods/>
|
||||||
<lines>
|
<lines>
|
||||||
<line number="1" hits="1"/>
|
<line number="1" hits="1"/>
|
||||||
@ -2372,7 +1745,7 @@
|
|||||||
<line number="19" hits="0"/>
|
<line number="19" hits="0"/>
|
||||||
<line number="21" hits="1"/>
|
<line number="21" hits="1"/>
|
||||||
<line number="26" hits="1"/>
|
<line number="26" hits="1"/>
|
||||||
<line number="33" hits="0"/>
|
<line number="33" hits="1"/>
|
||||||
<line number="35" hits="1"/>
|
<line number="35" hits="1"/>
|
||||||
<line number="45" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="46,47"/>
|
<line number="45" hits="0" branch="true" condition-coverage="0% (0/2)" missing-branches="46,47"/>
|
||||||
<line number="46" hits="0"/>
|
<line number="46" hits="0"/>
|
||||||
@ -3792,6 +3165,19 @@
|
|||||||
<line number="169" hits="1"/>
|
<line number="169" hits="1"/>
|
||||||
</lines>
|
</lines>
|
||||||
</class>
|
</class>
|
||||||
|
<class name="layout.py" filename="style/layout.py" complexity="0" line-rate="1" branch-rate="1">
|
||||||
|
<methods/>
|
||||||
|
<lines>
|
||||||
|
<line number="1" hits="1"/>
|
||||||
|
<line number="3" hits="1"/>
|
||||||
|
<line number="4" hits="1"/>
|
||||||
|
<line number="5" hits="1"/>
|
||||||
|
<line number="6" hits="1"/>
|
||||||
|
<line number="7" hits="1"/>
|
||||||
|
<line number="8" hits="1"/>
|
||||||
|
<line number="9" hits="1"/>
|
||||||
|
</lines>
|
||||||
|
</class>
|
||||||
</classes>
|
</classes>
|
||||||
</package>
|
</package>
|
||||||
<package name="typesetting" line-rate="0.2137" branch-rate="0" complexity="0">
|
<package name="typesetting" line-rate="0.2137" branch-rate="0" complexity="0">
|
||||||
|
|||||||
@ -224,6 +224,26 @@ class Document:
|
|||||||
"""
|
"""
|
||||||
self.set_metadata(MetadataType.TITLE, title)
|
self.set_metadata(MetadataType.TITLE, title)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title(self) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Get the document title as a property.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The document title, or None if not set
|
||||||
|
"""
|
||||||
|
return self.get_title()
|
||||||
|
|
||||||
|
@title.setter
|
||||||
|
def title(self, title: str):
|
||||||
|
"""
|
||||||
|
Set the document title as a property.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title: The document title
|
||||||
|
"""
|
||||||
|
self.set_title(title)
|
||||||
|
|
||||||
def find_blocks_by_type(self, block_type: BlockType) -> List[Block]:
|
def find_blocks_by_type(self, block_type: BlockType) -> List[Block]:
|
||||||
"""
|
"""
|
||||||
Find all blocks of a specific type.
|
Find all blocks of a specific type.
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import re
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from pyWebLayout.abstract.document import Document, Book, Chapter, MetadataType
|
from pyWebLayout.abstract.document import Document, Book, Chapter, MetadataType
|
||||||
from pyWebLayout.io.readers.html import parse_html_string as parse_html, read_html_file as html_to_document
|
from pyWebLayout.io.readers.html_extraction import parse_html_string
|
||||||
|
|
||||||
|
|
||||||
# XML namespaces used in EPUB files
|
# XML namespaces used in EPUB files
|
||||||
@ -346,6 +346,7 @@ class EPUBReader:
|
|||||||
add_to_toc_map(self.toc)
|
add_to_toc_map(self.toc)
|
||||||
|
|
||||||
# Process spine items
|
# Process spine items
|
||||||
|
chapter_index = 0 # Keep track of actual content chapters
|
||||||
for i, idref in enumerate(self.spine):
|
for i, idref in enumerate(self.spine):
|
||||||
if idref not in self.manifest:
|
if idref not in self.manifest:
|
||||||
continue
|
continue
|
||||||
@ -354,13 +355,20 @@ class EPUBReader:
|
|||||||
path = item['path']
|
path = item['path']
|
||||||
href = item['href']
|
href = item['href']
|
||||||
|
|
||||||
|
# Skip navigation files
|
||||||
|
if (idref == 'nav' or
|
||||||
|
item.get('media_type') == 'application/xhtml+xml' and
|
||||||
|
('nav' in href.lower() or 'toc' in href.lower())):
|
||||||
|
continue
|
||||||
|
|
||||||
# Check if this item is in the TOC
|
# Check if this item is in the TOC
|
||||||
chapter_title = None
|
chapter_title = None
|
||||||
if href in toc_map:
|
if href in toc_map:
|
||||||
chapter_title = toc_map[href]['label']
|
chapter_title = toc_map[href]['label']
|
||||||
|
|
||||||
# Create a chapter
|
# Create a chapter
|
||||||
chapter = self.book.create_chapter(chapter_title, i + 1)
|
chapter_index += 1
|
||||||
|
chapter = self.book.create_chapter(chapter_title, chapter_index)
|
||||||
|
|
||||||
# Parse the HTML content
|
# Parse the HTML content
|
||||||
try:
|
try:
|
||||||
@ -369,11 +377,10 @@ class EPUBReader:
|
|||||||
html = f.read()
|
html = f.read()
|
||||||
|
|
||||||
# Parse HTML and add blocks to chapter
|
# Parse HTML and add blocks to chapter
|
||||||
base_url = os.path.dirname(path)
|
blocks = parse_html_string(html)
|
||||||
document = parse_html(html, base_url=base_url)
|
|
||||||
|
|
||||||
# Copy blocks to the chapter
|
# Copy blocks to the chapter
|
||||||
for block in document.blocks:
|
for block in blocks:
|
||||||
chapter.add_block(block)
|
chapter.add_block(block)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -73,7 +73,7 @@ class Font:
|
|||||||
if self._font_size != 12: # Default size might not be 12
|
if self._font_size != 12: # Default size might not be 12
|
||||||
self._font = ImageFont.truetype(self._font.path, self._font_size)
|
self._font = ImageFont.truetype(self._font.path, self._font_size)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error loading font: {e}")
|
# Silently fall back to default font
|
||||||
self._font = ImageFont.load_default()
|
self._font = ImageFont.load_default()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -17,7 +17,8 @@ dependencies = [
|
|||||||
"numpy",
|
"numpy",
|
||||||
"pyphen",
|
"pyphen",
|
||||||
"beautifulsoup4",
|
"beautifulsoup4",
|
||||||
"flask"
|
"flask",
|
||||||
|
"ebooklib",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
|
|||||||
756
tests/test_epub_reader.py
Normal file
756
tests/test_epub_reader.py
Normal file
@ -0,0 +1,756 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for EPUB reader functionality.
|
||||||
|
|
||||||
|
Tests the EPUB parsing and conversion to pyWebLayout abstract elements,
|
||||||
|
using ebooklib to generate test EPUB files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Import ebooklib for creating test EPUB files
|
||||||
|
try:
|
||||||
|
from ebooklib import epub
|
||||||
|
EBOOKLIB_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
EBOOKLIB_AVAILABLE = False
|
||||||
|
|
||||||
|
from pyWebLayout.io.readers.epub_reader import read_epub, EPUBReader
|
||||||
|
from pyWebLayout.abstract.document import Book
|
||||||
|
from pyWebLayout.abstract.block import (
|
||||||
|
Paragraph, Heading, HeadingLevel, Quote, CodeBlock,
|
||||||
|
HList, ListStyle, Table, HorizontalRule, Image
|
||||||
|
)
|
||||||
|
from pyWebLayout.style import FontWeight, FontStyle, TextDecoration
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(EBOOKLIB_AVAILABLE, "ebooklib not available")
|
||||||
|
class TestEPUBReader(unittest.TestCase):
|
||||||
|
"""Test cases for EPUB reader functionality."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up test environment."""
|
||||||
|
self.test_dir = tempfile.mkdtemp()
|
||||||
|
self.epub_files = []
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Clean up test environment."""
|
||||||
|
# Clean up test EPUB files
|
||||||
|
for epub_file in self.epub_files:
|
||||||
|
try:
|
||||||
|
os.remove(epub_file)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Clean up test directory
|
||||||
|
if os.path.exists(self.test_dir):
|
||||||
|
shutil.rmtree(self.test_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
def create_simple_epub(self, title="Test Book", author="Test Author"):
|
||||||
|
"""Create a simple EPUB file for testing."""
|
||||||
|
book = epub.EpubBook()
|
||||||
|
|
||||||
|
# Set metadata
|
||||||
|
book.set_identifier('test-id-123')
|
||||||
|
book.set_title(title)
|
||||||
|
book.set_language('en')
|
||||||
|
book.add_author(author)
|
||||||
|
|
||||||
|
# Create a simple chapter
|
||||||
|
chapter1 = epub.EpubHtml(
|
||||||
|
title='Chapter 1',
|
||||||
|
file_name='chapter1.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter1.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>Chapter 1</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Chapter One</h1>
|
||||||
|
<p>This is the first paragraph of the first chapter.</p>
|
||||||
|
<p>This is a <strong>second paragraph</strong> with <em>some formatting</em>.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Add chapter to book
|
||||||
|
book.add_item(chapter1)
|
||||||
|
|
||||||
|
# Define table of contents
|
||||||
|
book.toc = (epub.Link("chapter1.xhtml", "Chapter 1", "ch1"),)
|
||||||
|
|
||||||
|
# Add navigation files
|
||||||
|
book.add_item(epub.EpubNcx())
|
||||||
|
book.add_item(epub.EpubNav())
|
||||||
|
|
||||||
|
# Define spine
|
||||||
|
book.spine = ['nav', chapter1]
|
||||||
|
|
||||||
|
# Create temporary file
|
||||||
|
epub_path = os.path.join(self.test_dir, f'test_simple_{len(self.epub_files)}.epub')
|
||||||
|
epub.write_epub(epub_path, book, {})
|
||||||
|
self.epub_files.append(epub_path)
|
||||||
|
|
||||||
|
return epub_path
|
||||||
|
|
||||||
|
def create_complex_epub(self):
|
||||||
|
"""Create a more complex EPUB file with multiple chapters and content types."""
|
||||||
|
book = epub.EpubBook()
|
||||||
|
|
||||||
|
# Set metadata
|
||||||
|
book.set_identifier('complex-test-id-456')
|
||||||
|
book.set_title('Complex Test Book')
|
||||||
|
book.set_language('en')
|
||||||
|
book.add_author('Test Author')
|
||||||
|
book.add_metadata('DC', 'description', 'A test book with complex content')
|
||||||
|
book.add_metadata('DC', 'subject', 'Testing')
|
||||||
|
book.add_metadata('DC', 'date', '2024-01-01')
|
||||||
|
book.add_metadata('DC', 'publisher', 'Test Publisher')
|
||||||
|
|
||||||
|
# Chapter 1: Basic content
|
||||||
|
chapter1 = epub.EpubHtml(
|
||||||
|
title='Introduction',
|
||||||
|
file_name='chapter1.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter1.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>Introduction</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Introduction</h1>
|
||||||
|
<p>Welcome to this <strong>complex test book</strong>.</p>
|
||||||
|
<p>This chapter contains basic content to test paragraph parsing.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Chapter 2: Styled content
|
||||||
|
chapter2 = epub.EpubHtml(
|
||||||
|
title='Styled Content',
|
||||||
|
file_name='chapter2.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter2.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>Styled Content</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Styled Content</h1>
|
||||||
|
<p>This chapter contains various <strong>bold text</strong>, <em>italic text</em>,
|
||||||
|
and <span style="color: red; font-weight: bold;">colored text</span>.</p>
|
||||||
|
<h2>Subsection</h2>
|
||||||
|
<p>Text with <u>underline</u> and <s>strikethrough</s>.</p>
|
||||||
|
<h3>More Formatting</h3>
|
||||||
|
<p>Nested formatting: <strong>bold with <em>italic inside</em></strong>.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Chapter 3: Lists and quotes
|
||||||
|
chapter3 = epub.EpubHtml(
|
||||||
|
title='Lists and Quotes',
|
||||||
|
file_name='chapter3.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter3.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>Lists and Quotes</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Lists and Quotes</h1>
|
||||||
|
|
||||||
|
<h2>Unordered List</h2>
|
||||||
|
<ul>
|
||||||
|
<li>First item</li>
|
||||||
|
<li><strong>Bold item</strong></li>
|
||||||
|
<li>Item with <em>italic text</em></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Ordered List</h2>
|
||||||
|
<ol>
|
||||||
|
<li>First numbered item</li>
|
||||||
|
<li>Second numbered item</li>
|
||||||
|
<li>Third numbered item</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Quote</h2>
|
||||||
|
<blockquote>
|
||||||
|
<p>This is a <span style="font-style: italic;">quoted paragraph</span>
|
||||||
|
with some styling.</p>
|
||||||
|
</blockquote>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Chapter 4: Tables and code
|
||||||
|
chapter4 = epub.EpubHtml(
|
||||||
|
title='Tables and Code',
|
||||||
|
file_name='chapter4.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter4.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>Tables and Code</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Tables and Code</h1>
|
||||||
|
|
||||||
|
<h2>Simple Table</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><strong>Header 1</strong></th>
|
||||||
|
<th><em>Header 2</em></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Cell 1</td>
|
||||||
|
<td>Cell 2 with <span style="color: blue;">blue text</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Bold cell</strong></td>
|
||||||
|
<td>Normal cell</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Code Block</h2>
|
||||||
|
<pre><code>function test() {
|
||||||
|
console.log("Hello, world!");
|
||||||
|
return true;
|
||||||
|
}</code></pre>
|
||||||
|
|
||||||
|
<h2>Inline Code</h2>
|
||||||
|
<p>Use the <code>print()</code> function to output text.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Add chapters to book
|
||||||
|
book.add_item(chapter1)
|
||||||
|
book.add_item(chapter2)
|
||||||
|
book.add_item(chapter3)
|
||||||
|
book.add_item(chapter4)
|
||||||
|
|
||||||
|
# Define table of contents
|
||||||
|
book.toc = (
|
||||||
|
epub.Link("chapter1.xhtml", "Introduction", "intro"),
|
||||||
|
epub.Link("chapter2.xhtml", "Styled Content", "styled"),
|
||||||
|
epub.Link("chapter3.xhtml", "Lists and Quotes", "lists"),
|
||||||
|
epub.Link("chapter4.xhtml", "Tables and Code", "tables")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add navigation files
|
||||||
|
book.add_item(epub.EpubNcx())
|
||||||
|
book.add_item(epub.EpubNav())
|
||||||
|
|
||||||
|
# Define spine
|
||||||
|
book.spine = ['nav', chapter1, chapter2, chapter3, chapter4]
|
||||||
|
|
||||||
|
# Create temporary file
|
||||||
|
epub_path = os.path.join(self.test_dir, f'test_complex_{len(self.epub_files)}.epub')
|
||||||
|
epub.write_epub(epub_path, book, {})
|
||||||
|
self.epub_files.append(epub_path)
|
||||||
|
|
||||||
|
return epub_path
|
||||||
|
|
||||||
|
def create_epub_with_nested_content(self):
|
||||||
|
"""Create an EPUB with nested content structures."""
|
||||||
|
book = epub.EpubBook()
|
||||||
|
|
||||||
|
# Set metadata
|
||||||
|
book.set_identifier('nested-test-id-789')
|
||||||
|
book.set_title('Nested Content Test')
|
||||||
|
book.set_language('en')
|
||||||
|
book.add_author('Test Author')
|
||||||
|
|
||||||
|
# Chapter with nested content
|
||||||
|
chapter = epub.EpubHtml(
|
||||||
|
title='Nested Content',
|
||||||
|
file_name='nested.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>Nested Content</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Nested Content Examples</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Section in Div</h2>
|
||||||
|
<p>Paragraph inside div.</p>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Subsection</h3>
|
||||||
|
<article>
|
||||||
|
<h4>Article Header</h4>
|
||||||
|
<p>Article content with <strong>nested <em>formatting</em></strong>.</p>
|
||||||
|
|
||||||
|
<aside>
|
||||||
|
<p>Sidebar content in aside element.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Nested list item</li>
|
||||||
|
<li>Another <strong>bold</strong> item</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>Footer content with <span style="font-size: 12px; color: gray;">small gray text</span>.</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Add chapter to book
|
||||||
|
book.add_item(chapter)
|
||||||
|
|
||||||
|
# Define table of contents
|
||||||
|
book.toc = (epub.Link("nested.xhtml", "Nested Content", "nested"),)
|
||||||
|
|
||||||
|
# Add navigation files
|
||||||
|
book.add_item(epub.EpubNcx())
|
||||||
|
book.add_item(epub.EpubNav())
|
||||||
|
|
||||||
|
# Define spine
|
||||||
|
book.spine = ['nav', chapter]
|
||||||
|
|
||||||
|
# Create temporary file
|
||||||
|
epub_path = os.path.join(self.test_dir, f'test_nested_{len(self.epub_files)}.epub')
|
||||||
|
epub.write_epub(epub_path, book, {})
|
||||||
|
self.epub_files.append(epub_path)
|
||||||
|
|
||||||
|
return epub_path
|
||||||
|
|
||||||
|
def test_simple_epub_reading(self):
|
||||||
|
"""Test reading a simple EPUB file."""
|
||||||
|
epub_path = self.create_simple_epub()
|
||||||
|
|
||||||
|
# Read the EPUB
|
||||||
|
book = read_epub(epub_path)
|
||||||
|
|
||||||
|
# Verify it's a Book object
|
||||||
|
self.assertIsInstance(book, Book)
|
||||||
|
|
||||||
|
# Check metadata
|
||||||
|
self.assertEqual(book.title, "Test Book")
|
||||||
|
|
||||||
|
# Check chapters
|
||||||
|
chapters = list(book.chapters)
|
||||||
|
self.assertEqual(len(chapters), 1)
|
||||||
|
|
||||||
|
# Check chapter content
|
||||||
|
chapter = chapters[0]
|
||||||
|
blocks = list(chapter.blocks)
|
||||||
|
self.assertGreater(len(blocks), 0)
|
||||||
|
|
||||||
|
# Should have a heading and paragraphs
|
||||||
|
has_heading = any(isinstance(block, Heading) for block in blocks)
|
||||||
|
has_paragraph = any(isinstance(block, Paragraph) for block in blocks)
|
||||||
|
|
||||||
|
self.assertTrue(has_heading, "Should contain at least one heading")
|
||||||
|
self.assertTrue(has_paragraph, "Should contain at least one paragraph")
|
||||||
|
|
||||||
|
def test_complex_epub_reading(self):
|
||||||
|
"""Test reading a complex EPUB file with multiple chapters."""
|
||||||
|
epub_path = self.create_complex_epub()
|
||||||
|
|
||||||
|
# Read the EPUB
|
||||||
|
book = read_epub(epub_path)
|
||||||
|
|
||||||
|
# Verify it's a Book object
|
||||||
|
self.assertIsInstance(book, Book)
|
||||||
|
|
||||||
|
# Check metadata
|
||||||
|
self.assertEqual(book.title, "Complex Test Book")
|
||||||
|
|
||||||
|
# Check chapters
|
||||||
|
chapters = list(book.chapters)
|
||||||
|
self.assertEqual(len(chapters), 4)
|
||||||
|
|
||||||
|
# Test each chapter has content
|
||||||
|
for i, chapter in enumerate(chapters):
|
||||||
|
blocks = list(chapter.blocks)
|
||||||
|
self.assertGreater(len(blocks), 0, f"Chapter {i+1} should have blocks")
|
||||||
|
|
||||||
|
# Each chapter should start with a heading
|
||||||
|
first_block = blocks[0]
|
||||||
|
self.assertIsInstance(first_block, Heading, f"Chapter {i+1} should start with heading")
|
||||||
|
|
||||||
|
def test_epub_styled_content(self):
|
||||||
|
"""Test that styled content in EPUB is properly parsed."""
|
||||||
|
epub_path = self.create_complex_epub()
|
||||||
|
book = read_epub(epub_path)
|
||||||
|
|
||||||
|
chapters = list(book.chapters)
|
||||||
|
|
||||||
|
# Check styled content in chapter 2 (index 1)
|
||||||
|
if len(chapters) > 1:
|
||||||
|
chapter2_blocks = list(chapters[1].blocks)
|
||||||
|
|
||||||
|
# Find paragraphs with styled text
|
||||||
|
styled_words_found = False
|
||||||
|
for block in chapter2_blocks:
|
||||||
|
if isinstance(block, Paragraph):
|
||||||
|
words = list(block.words())
|
||||||
|
for _, word in words:
|
||||||
|
if (word.style.weight == FontWeight.BOLD or
|
||||||
|
word.style.style == FontStyle.ITALIC or
|
||||||
|
word.style.colour != (0, 0, 0)): # Non-black color
|
||||||
|
styled_words_found = True
|
||||||
|
break
|
||||||
|
if styled_words_found:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.assertTrue(styled_words_found, "Should find styled words in chapter 2")
|
||||||
|
|
||||||
|
def test_epub_lists(self):
|
||||||
|
"""Test that lists in EPUB are properly parsed."""
|
||||||
|
epub_path = self.create_complex_epub()
|
||||||
|
book = read_epub(epub_path)
|
||||||
|
|
||||||
|
chapters = list(book.chapters)
|
||||||
|
|
||||||
|
# Check lists in chapter 3 (index 2)
|
||||||
|
if len(chapters) > 2:
|
||||||
|
chapter3_blocks = list(chapters[2].blocks)
|
||||||
|
|
||||||
|
# Find list blocks
|
||||||
|
unordered_list_found = False
|
||||||
|
ordered_list_found = False
|
||||||
|
quote_found = False
|
||||||
|
|
||||||
|
for block in chapter3_blocks:
|
||||||
|
if isinstance(block, HList):
|
||||||
|
if block.style == ListStyle.UNORDERED:
|
||||||
|
unordered_list_found = True
|
||||||
|
|
||||||
|
# Check list items
|
||||||
|
items = list(block.items())
|
||||||
|
self.assertGreater(len(items), 0, "Unordered list should have items")
|
||||||
|
|
||||||
|
elif block.style == ListStyle.ORDERED:
|
||||||
|
ordered_list_found = True
|
||||||
|
|
||||||
|
# Check list items
|
||||||
|
items = list(block.items())
|
||||||
|
self.assertGreater(len(items), 0, "Ordered list should have items")
|
||||||
|
|
||||||
|
elif isinstance(block, Quote):
|
||||||
|
quote_found = True
|
||||||
|
|
||||||
|
self.assertTrue(unordered_list_found, "Should find unordered list in chapter 3")
|
||||||
|
self.assertTrue(ordered_list_found, "Should find ordered list in chapter 3")
|
||||||
|
self.assertTrue(quote_found, "Should find quote in chapter 3")
|
||||||
|
|
||||||
|
def test_epub_tables(self):
|
||||||
|
"""Test that tables in EPUB are properly parsed."""
|
||||||
|
epub_path = self.create_complex_epub()
|
||||||
|
book = read_epub(epub_path)
|
||||||
|
|
||||||
|
chapters = list(book.chapters)
|
||||||
|
|
||||||
|
# Check tables in chapter 4 (index 3)
|
||||||
|
if len(chapters) > 3:
|
||||||
|
chapter4_blocks = list(chapters[3].blocks)
|
||||||
|
|
||||||
|
# Find table blocks
|
||||||
|
table_found = False
|
||||||
|
code_block_found = False
|
||||||
|
|
||||||
|
for block in chapter4_blocks:
|
||||||
|
if isinstance(block, Table):
|
||||||
|
table_found = True
|
||||||
|
|
||||||
|
# Check table has rows
|
||||||
|
rows = list(block.all_rows())
|
||||||
|
self.assertGreater(len(rows), 0, "Table should have rows")
|
||||||
|
|
||||||
|
elif isinstance(block, CodeBlock):
|
||||||
|
code_block_found = True
|
||||||
|
|
||||||
|
# Check code block has lines
|
||||||
|
lines = list(block.lines())
|
||||||
|
self.assertGreater(len(lines), 0, "Code block should have lines")
|
||||||
|
|
||||||
|
self.assertTrue(table_found, "Should find table in chapter 4")
|
||||||
|
self.assertTrue(code_block_found, "Should find code block in chapter 4")
|
||||||
|
|
||||||
|
def test_epub_nested_content(self):
|
||||||
|
"""Test that nested content structures are properly parsed."""
|
||||||
|
epub_path = self.create_epub_with_nested_content()
|
||||||
|
book = read_epub(epub_path)
|
||||||
|
|
||||||
|
chapters = list(book.chapters)
|
||||||
|
self.assertEqual(len(chapters), 1)
|
||||||
|
|
||||||
|
chapter_blocks = list(chapters[0].blocks)
|
||||||
|
self.assertGreater(len(chapter_blocks), 0)
|
||||||
|
|
||||||
|
# Should have multiple headings (h1, h2, h3, h4)
|
||||||
|
headings = [block for block in chapter_blocks if isinstance(block, Heading)]
|
||||||
|
self.assertGreater(len(headings), 2, "Should have multiple headings from nested content")
|
||||||
|
|
||||||
|
# Should have paragraphs and lists from nested content
|
||||||
|
paragraphs = [block for block in chapter_blocks if isinstance(block, Paragraph)]
|
||||||
|
lists = [block for block in chapter_blocks if isinstance(block, HList)]
|
||||||
|
|
||||||
|
self.assertGreater(len(paragraphs), 0, "Should have paragraphs from nested content")
|
||||||
|
self.assertGreater(len(lists), 0, "Should have lists from nested content")
|
||||||
|
|
||||||
|
def test_epub_metadata_extraction(self):
|
||||||
|
"""Test that EPUB metadata is properly extracted."""
|
||||||
|
epub_path = self.create_complex_epub()
|
||||||
|
book = read_epub(epub_path)
|
||||||
|
|
||||||
|
# Check basic metadata
|
||||||
|
self.assertEqual(book.title, "Complex Test Book")
|
||||||
|
|
||||||
|
# Check that metadata was set (implementation may vary)
|
||||||
|
# This tests that the metadata parsing doesn't crash
|
||||||
|
self.assertIsNotNone(book.title)
|
||||||
|
|
||||||
|
def test_epub_reader_class_direct(self):
|
||||||
|
"""Test EPUBReader class directly."""
|
||||||
|
epub_path = self.create_simple_epub()
|
||||||
|
|
||||||
|
reader = EPUBReader(epub_path)
|
||||||
|
book = reader.read()
|
||||||
|
|
||||||
|
self.assertIsInstance(book, Book)
|
||||||
|
self.assertEqual(book.title, "Test Book")
|
||||||
|
|
||||||
|
def test_invalid_epub_handling(self):
|
||||||
|
"""Test handling of invalid EPUB files."""
|
||||||
|
# Create a non-EPUB file
|
||||||
|
invalid_path = os.path.join(self.test_dir, 'invalid.epub')
|
||||||
|
with open(invalid_path, 'w') as f:
|
||||||
|
f.write("This is not an EPUB file")
|
||||||
|
|
||||||
|
# Should raise an exception or handle gracefully
|
||||||
|
with self.assertRaises(Exception):
|
||||||
|
read_epub(invalid_path)
|
||||||
|
|
||||||
|
def test_nonexistent_epub_handling(self):
|
||||||
|
"""Test handling of nonexistent EPUB files."""
|
||||||
|
nonexistent_path = os.path.join(self.test_dir, 'nonexistent.epub')
|
||||||
|
|
||||||
|
# Should raise an exception
|
||||||
|
with self.assertRaises(Exception):
|
||||||
|
read_epub(nonexistent_path)
|
||||||
|
|
||||||
|
def test_epub_with_custom_metadata(self):
|
||||||
|
"""Test EPUB with various metadata fields."""
|
||||||
|
book = epub.EpubBook()
|
||||||
|
|
||||||
|
# Set comprehensive metadata
|
||||||
|
book.set_identifier('custom-metadata-test')
|
||||||
|
book.set_title('Custom Metadata Test')
|
||||||
|
book.set_language('en')
|
||||||
|
book.add_author('Primary Author')
|
||||||
|
book.add_author('Secondary Author')
|
||||||
|
book.add_metadata('DC', 'description', 'A comprehensive test of metadata extraction')
|
||||||
|
book.add_metadata('DC', 'subject', 'Testing')
|
||||||
|
book.add_metadata('DC', 'subject', 'EPUB')
|
||||||
|
book.add_metadata('DC', 'date', '2024-06-07')
|
||||||
|
book.add_metadata('DC', 'publisher', 'Test Publishing House')
|
||||||
|
book.add_metadata('DC', 'rights', 'Public Domain')
|
||||||
|
|
||||||
|
# Simple chapter
|
||||||
|
chapter = epub.EpubHtml(
|
||||||
|
title='Metadata Test',
|
||||||
|
file_name='metadata.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>Metadata Test</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Metadata Test Chapter</h1>
|
||||||
|
<p>This chapter tests metadata extraction.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
book.add_item(chapter)
|
||||||
|
book.toc = (epub.Link("metadata.xhtml", "Metadata Test", "meta"),)
|
||||||
|
book.add_item(epub.EpubNcx())
|
||||||
|
book.add_item(epub.EpubNav())
|
||||||
|
book.spine = ['nav', chapter]
|
||||||
|
|
||||||
|
# Write and test
|
||||||
|
epub_path = os.path.join(self.test_dir, f'test_metadata_{len(self.epub_files)}.epub')
|
||||||
|
epub.write_epub(epub_path, book, {})
|
||||||
|
self.epub_files.append(epub_path)
|
||||||
|
|
||||||
|
# Read and verify
|
||||||
|
parsed_book = read_epub(epub_path)
|
||||||
|
self.assertEqual(parsed_book.title, "Custom Metadata Test")
|
||||||
|
|
||||||
|
# Verify chapters were created
|
||||||
|
chapters = list(parsed_book.chapters)
|
||||||
|
self.assertEqual(len(chapters), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestEPUBIntegrationWithHTMLExtraction(unittest.TestCase):
|
||||||
|
"""Test cases that specifically verify EPUB reader uses html_extraction properly."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up test environment."""
|
||||||
|
self.test_dir = tempfile.mkdtemp()
|
||||||
|
self.epub_files = []
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Clean up test environment."""
|
||||||
|
for epub_file in self.epub_files:
|
||||||
|
try:
|
||||||
|
os.remove(epub_file)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if os.path.exists(self.test_dir):
|
||||||
|
shutil.rmtree(self.test_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
@unittest.skipUnless(EBOOKLIB_AVAILABLE, "ebooklib not available")
|
||||||
|
def test_html_extraction_integration(self):
|
||||||
|
"""Test that EPUB reader properly uses html_extraction functionality."""
|
||||||
|
# Create an EPUB that exercises various HTML extraction features
|
||||||
|
book = epub.EpubBook()
|
||||||
|
book.set_identifier('html-extraction-test')
|
||||||
|
book.set_title('HTML Extraction Test')
|
||||||
|
book.set_language('en')
|
||||||
|
book.add_author('Test Author')
|
||||||
|
|
||||||
|
# Chapter that exercises html_extraction features
|
||||||
|
chapter = epub.EpubHtml(
|
||||||
|
title='HTML Features',
|
||||||
|
file_name='html_features.xhtml',
|
||||||
|
lang='en'
|
||||||
|
)
|
||||||
|
chapter.content = '''
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head><title>HTML Features</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>HTML Extraction Test</h1>
|
||||||
|
|
||||||
|
<!-- Test paragraph with inline formatting -->
|
||||||
|
<p>This paragraph has <strong>bold</strong>, <em>italic</em>,
|
||||||
|
<u>underlined</u>, and <span style="color: #ff0000; font-weight: bold;">styled</span> text.</p>
|
||||||
|
|
||||||
|
<!-- Test headings -->
|
||||||
|
<h2>Second Level Heading</h2>
|
||||||
|
<h3>Third Level Heading</h3>
|
||||||
|
|
||||||
|
<!-- Test lists with styled content -->
|
||||||
|
<ul>
|
||||||
|
<li>Plain list item</li>
|
||||||
|
<li><strong>Bold list item</strong></li>
|
||||||
|
<li>List item with <em>italic text</em></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Test table with styled cells -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th style="font-weight: bold;">Header</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span style="color: blue;">Blue text</span></td>
|
||||||
|
<td>Normal text</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Test blockquote -->
|
||||||
|
<blockquote>
|
||||||
|
<p>This is a quoted paragraph with <strong>bold text</strong>.</p>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<!-- Test code block -->
|
||||||
|
<pre><code>def test_function():
|
||||||
|
return "Hello, World!"</code></pre>
|
||||||
|
|
||||||
|
<!-- Test nested formatting -->
|
||||||
|
<p>Nested formatting: <strong>bold with <em>italic nested</em> inside</strong>.</p>
|
||||||
|
|
||||||
|
<!-- Test color variations -->
|
||||||
|
<p>
|
||||||
|
<span style="color: red;">Red text</span>,
|
||||||
|
<span style="color: #00ff00;">Green hex</span>,
|
||||||
|
<span style="color: blue; text-decoration: underline;">Blue underlined</span>.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
book.add_item(chapter)
|
||||||
|
book.toc = (epub.Link("html_features.xhtml", "HTML Features", "html"),)
|
||||||
|
book.add_item(epub.EpubNcx())
|
||||||
|
book.add_item(epub.EpubNav())
|
||||||
|
book.spine = ['nav', chapter]
|
||||||
|
|
||||||
|
# Write EPUB
|
||||||
|
epub_path = os.path.join(self.test_dir, 'html_extraction_test.epub')
|
||||||
|
epub.write_epub(epub_path, book, {})
|
||||||
|
self.epub_files.append(epub_path)
|
||||||
|
|
||||||
|
# Read and analyze
|
||||||
|
parsed_book = read_epub(epub_path)
|
||||||
|
chapters = list(parsed_book.chapters)
|
||||||
|
self.assertEqual(len(chapters), 1)
|
||||||
|
|
||||||
|
blocks = list(chapters[0].blocks)
|
||||||
|
self.assertGreater(len(blocks), 5) # Should have multiple blocks
|
||||||
|
|
||||||
|
# Test that we get the expected block types
|
||||||
|
block_types = [type(block).__name__ for block in blocks]
|
||||||
|
self.assertIn('Heading', block_types, "Should have heading blocks")
|
||||||
|
self.assertIn('Paragraph', block_types, "Should have paragraph blocks")
|
||||||
|
self.assertIn('HList', block_types, "Should have list blocks")
|
||||||
|
self.assertIn('Table', block_types, "Should have table blocks")
|
||||||
|
self.assertIn('Quote', block_types, "Should have quote blocks")
|
||||||
|
self.assertIn('CodeBlock', block_types, "Should have code blocks")
|
||||||
|
|
||||||
|
# Test styled content was preserved
|
||||||
|
styled_content_found = False
|
||||||
|
for block in blocks:
|
||||||
|
if isinstance(block, Paragraph):
|
||||||
|
words = list(block.words())
|
||||||
|
for _, word in words:
|
||||||
|
if (word.style.weight == FontWeight.BOLD or
|
||||||
|
word.style.style == FontStyle.ITALIC or
|
||||||
|
word.style.decoration == TextDecoration.UNDERLINE or
|
||||||
|
word.style.colour != (0, 0, 0)):
|
||||||
|
styled_content_found = True
|
||||||
|
break
|
||||||
|
if styled_content_found:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.assertTrue(styled_content_found, "Should find styled content in parsed blocks")
|
||||||
|
|
||||||
|
# Test specific color parsing
|
||||||
|
red_text_found = False
|
||||||
|
green_text_found = False
|
||||||
|
blue_text_found = False
|
||||||
|
|
||||||
|
for block in blocks:
|
||||||
|
if isinstance(block, (Paragraph, Table)):
|
||||||
|
if isinstance(block, Paragraph):
|
||||||
|
words = list(block.words())
|
||||||
|
for _, word in words:
|
||||||
|
if word.style.colour == (255, 0, 0): # Red
|
||||||
|
red_text_found = True
|
||||||
|
elif word.style.colour == (0, 255, 0): # Green
|
||||||
|
green_text_found = True
|
||||||
|
elif word.style.colour == (0, 0, 255): # Blue
|
||||||
|
blue_text_found = True
|
||||||
|
|
||||||
|
# At least one color should be found (depending on implementation)
|
||||||
|
color_found = red_text_found or green_text_found or blue_text_found
|
||||||
|
self.assertTrue(color_found, "Should find at least one colored text")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Loading…
x
Reference in New Issue
Block a user