| {% if table_entry_type == "Component" %} |
| <p> |
|  Filter: <input type="text" id="filter" size="30" /> (e.g. "crypto,VR") |
| </p> |
| {% endif %} |
| <table> |
| <thead> |
| <tr> |
| <th class="column-entry-bold">{{ table_entry_type }}</th> |
| <th class="column-entry-bold" onclick="sortTable(SORT_COLUMN.LINE)" title= |
| "Line coverage is the percentage of code lines which have been executed at least once. Only executable lines within function bodies are considered to be code lines."> |
| Line Coverage |
| </th> |
| <th class="column-entry-bold" onclick="sortTable(SORT_COLUMN.FUNCTION)" title= |
| "Function coverage is the percentage of functions which have been executed at least once. A function is considered to be executed if any of its instantiations are executed."> |
| Function Coverage |
| </th> |
| <th class="column-entry-bold" onclick="sortTable(SORT_COLUMN.REGION)" title= |
| "Region coverage is the percentage of code regions which have been executed at least once. A code region may span multiple lines (e.g in a large function body with no control flow). However, it's also possible for a single line to contain multiple code regions (e.g in 'return x || y && z')."> |
| Region Coverage |
| </th> |
| </tr> |
| </thead> |
| <tbody> |
| {% for entry in entries %} |
| <tr class="light-row"> |
| <td> |
| {% if entry["is_dir"] == True %} |
| <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}/</a></pre> |
| {% else %} |
| <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}</a></pre> |
| {% endif %} |
| </td> |
| {% for feature in ("lines", "functions", "regions") %} |
| <td class='column-entry-{{ entry[feature]["color_class"] }}'> |
| <pre>{{ entry[feature]["percentage"] }}% ({{ entry[feature]["covered"] }}/{{ entry[feature]["total"] }})</pre> |
| </td> |
| {% endfor %} |
| </tr> |
| {% endfor %} |
| </tbody> |
| {% if total_entry %} |
| <tfoot> |
| <tr class="light-row-bold"> |
| <td> |
| <pre>Totals</pre> |
| </td> |
| {% for feature in ("lines", "functions", "regions") %} |
| <td class='column-entry-{{ total_entry[feature]["color_class"] }}'> |
| <pre>{{ total_entry[feature]["percentage"] }}% ({{ total_entry[feature]["covered"] }}/{{ total_entry[feature]["total"] }})</pre> |
| </td> |
| {% endfor %} |
| </tr> |
| </tfoot> |
| {% endif %} |
| </table> |
| |
| <script> |
| const SORT_COLUMN = { |
| LINE: 2, |
| FUNCTION: 3, |
| REGION: 4, |
| } |
| |
| const SORT_TYPES = { |
| UNSET: -1, |
| PERCENT: 0, |
| AGGREGATE: 1, |
| } |
| |
| var SORT_ORDER = { |
| [SORT_COLUMN.LINE]: [SORT_TYPES.UNSET], |
| [SORT_COLUMN.FUNCTION]: [SORT_TYPES.UNSET], |
| [SORT_COLUMN.REGION]: [SORT_TYPES.UNSET], |
| } |
| |
| function sortTable(columnNumber) { |
| SORT_ORDER[columnNumber] = ++SORT_ORDER[columnNumber] % 2; |
| |
| let columnSortOrder = SORT_ORDER[columnNumber]; |
| let tbody = document.getElementsByTagName("tbody")[0]; |
| |
| [].slice.call(tbody.rows).sort(function(a, b) { |
| let aColumn = a.cells[columnNumber-1].textContent; |
| let bColumn = b.cells[columnNumber-1].textContent; |
| |
| let aColumnCompare, bColumnCompare; |
| if (columnSortOrder == SORT_TYPES.PERCENT) { |
| aColumnCompare = parseFloat(/([0-9.]+)%/.exec(aColumn)[1]); |
| bColumnCompare = parseFloat(/([0-9.]+)%/.exec(bColumn)[1]); |
| } else { |
| aColumnCompare = parseInt(/\/(\d+)/.exec(aColumn)[1]); |
| bColumnCompare = parseInt(/\/(\d+)/.exec(bColumn)[1]); |
| } |
| |
| return ( |
| aColumnCompare < bColumnCompare ? -1: |
| aColumnCompare > bColumnCompare ? 1 : 0); |
| }).forEach(function(value, index) { |
| tbody.appendChild(value); |
| }); |
| } |
| |
| {% if table_entry_type == "Component" %} |
| function filterInputChanged() { |
| let filter = document.getElementById("filter"); |
| let filterString = filter.value; |
| |
| // Update query paramater in URL. |
| let queryParams = new URLSearchParams(window.location.search); |
| queryParams.set('filter', filterString); |
| let newPath = window.location.pathname; |
| if (filterString) |
| newPath += '?' + queryParams.toString(); |
| history.pushState(null, '', newPath); |
| |
| filterRowsIfNeeded(); |
| } |
| |
| function filterRowsIfNeeded() { |
| let queryParams = new URLSearchParams(window.location.search); |
| let filterString = queryParams.get('filter') || ""; |
| let filterValuesLower = filterString.toLowerCase().split(','); |
| let tbody = document.getElementsByTagName("tbody")[0]; |
| let rows = tbody.getElementsByTagName("tr"); |
| for (let row of rows) { |
| let td = row.getElementsByTagName("td"); |
| let tdContent = row.innerText.toLowerCase(); |
| |
| // |match| should be true on empty search (show everything). |
| let match = !filterValuesLower; |
| for (let filterValueLower of filterValuesLower) { |
| if (tdContent.includes(filterValueLower)) { |
| match = true; |
| break |
| } |
| } |
| |
| if (match && row.style.display == 'none') |
| row.style.display = 'table-row'; |
| else if (!match) |
| row.style.display = 'none'; |
| } |
| |
| // Update filter value in input box (for cases when filter is provided |
| // as part of page load URL). |
| let filter = document.getElementById("filter"); |
| if (filter.value != filterString) |
| filter.value = filterString; |
| } |
| |
| window.onload = filterRowsIfNeeded; |
| |
| var filter = document.getElementById("filter"); |
| filter.onchange = filter.onkeyup = filterInputChanged; |
| {% endif %} |
| </script> |