Home | History | Annotate | Download | only in test
      1 #!/bin/bash
      2 # Loading... <!--
      3 # Copyright (C) 2017 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 cd $(dirname $0)
     18 python -m webbrowser -t "http://localhost:8000/$(basename $0)"
     19 python -m SimpleHTTPServer
     20 
     21 <<-EOF
     22 -->
     23 <body>
     24 <style>
     25 * {
     26   box-sizing: border-box;
     27 }
     28 
     29 .main {
     30   display: flex;
     31   font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
     32   font-weight: 300;
     33 }
     34 
     35 pre {
     36   font-size: 12px;
     37 }
     38 
     39 ul {
     40   margin: 0;
     41   padding: 0;
     42 }
     43 
     44 li {
     45   list-style: none;
     46   border-radius: 3px;
     47   border: solid rgba(0, 0, 0, 0) 1px;
     48   padding: 3px;
     49   margin-right: 5px 0;
     50 }
     51 
     52 li.selected {
     53   border: solid rgba(0, 0, 0, 0.89) 1px;
     54 }
     55 
     56 h1 {
     57   font-weight: 200;
     58   margin-bottom: 0;
     59 }
     60 
     61 h2 {
     62   font-size: smaller;
     63 }
     64 
     65 .focus {
     66   flex: 1;
     67   margin: 20px;
     68 }
     69 
     70 .context {
     71   flex: 0 0 25%;
     72 }
     73 
     74 .green {
     75   color: green;
     76 }
     77 
     78 .red {
     79   color: red;
     80 }
     81 
     82 .files {
     83   position: sticky;
     84   top: 15px;
     85 }
     86 
     87 .file {
     88   display: flex;
     89   justify-content: flex-start;
     90   flex-direction: row;
     91 }
     92 
     93 .file *:first-child {
     94   flex: 0 0 300px;
     95 }
     96 
     97 .file *:last-child {
     98   flex-grow: 1;
     99 }
    100 
    101 .version {
    102   display: flex;
    103   margin-bottom: 4px;
    104 }
    105 
    106 .version li {
    107   margin-right: 20px;
    108 }
    109 
    110 input {
    111   font-size: large;
    112   margin: 20px 0;
    113 }
    114 
    115 </style>
    116 <script src="//unpkg.com/mithril"></script>
    117 <script src="//unpkg.com/diff"></script>
    118 
    119 <div id="content"></div>
    120 
    121 <script>
    122 // Remove hash bang.
    123 document.body.firstChild.remove();
    124 
    125 let THIS_URL = window.location.href;
    126 let gDirectoryToFormatFiles;
    127 let gNamesToRecords = new Map();
    128 let gFilterText = '';
    129 let gDisplayedRecords = null;
    130 let gDisplayedName = null;
    131 let gADevice = null;
    132 let gBDevice = null;
    133 let gDevices = []
    134 let gCache = new Map();
    135 
    136 function isdir(url) {
    137   return url[url.length - 1] == '/';
    138 }
    139 
    140 function isfile(url) {
    141   return !isdir(url);
    142 }
    143 
    144 function getdir(url) {
    145   return url.slice(0, url.lastIndexOf('/')+1);
    146 }
    147 
    148 let getdirectories = url => listdir(url).then(xs => xs.filter(isdir));
    149 let getfiles = url => listdir(url).then(xs => xs.filter(isfile));
    150 
    151 function fetch(url) {
    152   return new Promise(function(resolve, reject) {
    153     let xhr = new XMLHttpRequest();
    154     xhr.open("GET", url, true);
    155     xhr.onload = e => resolve({
    156       text: () => Promise.resolve(xhr.responseText),
    157     });
    158     xhr.onerror = e => reject(xhr.statusText);
    159     xhr.send(null);
    160   });
    161 }
    162 
    163 function geturl(url) {
    164   console.log('Fetch:', url);
    165   if (gCache.has(url)) return Promise.resolve(gCache.get(url));
    166   return fetch(url).then(r => r.text()).then(text => {
    167     gCache.set(url, text);
    168     return text;
    169   });
    170 }
    171 
    172 function listdir(url) {
    173   return geturl(url).then(text => {
    174     let re = new RegExp('<li><a href="(.+)">(.+)</a>', 'g');
    175     if (window.location.href.indexOf('x20') != -1)
    176       re = new RegExp('[^>]</td>\n<td>\n<a href="(.+)">(.+)</a>', 'g');
    177     let match;
    178     let matches = [];
    179     while (match = re.exec(text)) {
    180       matches.push(match[1]);
    181     }
    182     return matches;
    183   });
    184 }
    185 
    186 function getfiletext(url) {
    187   if (gCache.has(url)) return gCache.get(url);
    188   geturl(url).then(() => m.redraw());
    189   return "";
    190 }
    191 
    192 function makeFormatFileRecord(base_url, device, group_name, event_name) {
    193   let url = base_url + device + 'events/' + group_name + event_name + 'format';
    194   let group = group_name.replace('/', '');
    195   let name = event_name.replace('/', '');
    196   return new FormatFileRecord(device, group, name, url);
    197 }
    198 
    199 function findFormatFilesByDirectory() {
    200   let url = getdir(THIS_URL) + 'data/';
    201   let directoryToFormatFiles = new Map();
    202   return getdirectories(url).then(directories => {
    203     return Promise.all(directories.map(device => {
    204       directoryToFormatFiles.set(device, []);
    205       return getdirectories(url + device + 'events/').then(groups => {
    206         return Promise.all(groups.map(group_name => {
    207           let innerUrl = url + device + 'events/' + group_name;
    208           return getdirectories(innerUrl).then(event_names => {
    209             event_names.map(event_name => {
    210               let record = makeFormatFileRecord(
    211                   url,
    212                   device,
    213                   group_name,
    214                   event_name);
    215               directoryToFormatFiles.get(device).push(record);
    216             });
    217           });
    218         }));
    219       });
    220     }));
    221   }).then(_ => {
    222     return directoryToFormatFiles
    223   });
    224 }
    225 
    226 class FormatFileRecord {
    227   constructor(device, group, name, url) {
    228     this.device = device;
    229     this.group = group;
    230     this.name = name;
    231     this.url = url;
    232   }
    233 }
    234 
    235 function fuzzyMatch(query) {
    236   let re = new RegExp(Array.from(query).join('.*'));
    237   return text => text.match(re);
    238 }
    239 
    240 function contextView(filterText, namesToRecords) {
    241   let matcher = fuzzyMatch(filterText);
    242   return m('.context', [
    243     m('h1', {class: 'title'}, 'Ftrace Format Explorer'),
    244     m('input[type=text][placeholder=Filter]', {
    245       oninput: m.withAttr('value', value => gFilterText = value),
    246       value: filterText,
    247     }),
    248     m('ul',
    249       Array.from(namesToRecords.entries())
    250           .filter(e => matcher(e[0])).map(e => m('li[tabindex=0]', {
    251         onfocus: () => { gDisplayedRecords = e[1]; gDisplayedName = e[0];
    252       },
    253       class: gDisplayedName == e[0] ? 'selected' : '',
    254     }, e[0] + ' (' + e[1].length + ')' ))),
    255   ]);
    256 }
    257 
    258 function focusView(records) {
    259   if (records == null) {
    260     return m('div.focus');
    261   }
    262 
    263   let r1 = records.filter(r => r.device == gADevice)[0];
    264   let r2 = records.filter(r => r.device == gBDevice)[0];
    265   if (!r1) r1 = records[0];
    266   if (!r2) r2 = records[0];
    267   let f1 = getfiletext(r1.url);
    268   let f2 = getfiletext(r2.url);
    269   let diff = JsDiff.diffChars(f1, f2);
    270 
    271   let es = diff.map(part => {
    272     let color = part.added ? 'green' : part.removed ? 'red' : 'grey';
    273     let e = m('span.' + color, part.value);
    274     return e;
    275   });
    276   return m('.focus', [
    277     m('ul.version', gDevices.map(device => m('li', {
    278       onclick: () => gADevice = device,
    279       class: device == gADevice ? 'selected' : '',
    280     }, device))),
    281     m('ul.version', gDevices.map(device => m('li', {
    282       onclick: () => gBDevice = device,
    283       class: device == gBDevice ? 'selected' : '',
    284     }, device))),
    285     m('.files', [
    286       m('.file', [m('h2', gADevice),  m('pre', f1)]),
    287       gADevice == gBDevice ? undefined : [
    288         m('.file', [m('h2', gBDevice),  m('pre', f2)]),
    289         m('.file', [m('h2', 'Delta'), m('pre', es)]),
    290       ]
    291     ]),
    292   ]);
    293 }
    294 
    295 let root = document.getElementById('content');
    296 let App = {
    297   view: function() {
    298     if (!gDirectoryToFormatFiles)
    299       return m('.main', 'Loading...');
    300     return m('.main', [
    301       contextView(gFilterText, gNamesToRecords),
    302       focusView(gDisplayedRecords),
    303     ])
    304   }
    305 }
    306 m.mount(root, App);
    307 
    308 findFormatFilesByDirectory().then(data => {
    309   gDirectoryToFormatFiles = data;
    310   gNamesToRecords = new Map();
    311   gDevices = Array.from(gDirectoryToFormatFiles.keys());
    312   for (let records of gDirectoryToFormatFiles.values()) {
    313     for (let record of records) {
    314       geturl(record.url);
    315       if (gNamesToRecords.get(record.name) == null) {
    316         gNamesToRecords.set(record.name, []);
    317       }
    318       gNamesToRecords.get(record.name).push(record);
    319     }
    320   }
    321   [gADevice, gBDevice] = gDevices;
    322   m.redraw();
    323 });
    324 
    325 </script>
    326 
    327 <!--
    328 EOF
    329 #-->
    330