Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 8572

Show
Ignore:
Timestamp:
01/06/08 00:34:39 (8 months ago)
Author:
tobie
Message:

prototype: Test.Unit refactoring. Allow running multiple instances of Test.Unit.Runner on the same page. Allow rake to run specific testcases. Closes #10704, #10705, #10706.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • spinoffs/prototype/trunk/CHANGELOG

    r8557 r8572  
    11*SVN* 
     2 
     3* Test.Unit refactoring. Allow running multiple instances of Test.Unit.Runner on the same page. Allow rake to run specific testcases (e.g.: rake test BROWSERS=firefox TESTS=array TESTCASES=testUniq,test$w). Closes #10704, #10705, #10706. [nicwilliams, Tobie Langel] 
    24 
    35* Optimize property detection of outerHTML. Avoids triggering FOUC in Safari 3.0.4. Closes #10702. [subimage, Tobie Langel] 
  • spinoffs/prototype/trunk/Rakefile

    r8274 r8572  
    3838desc "Runs all the JavaScript unit tests and collects the results" 
    3939JavaScriptTestTask.new(:test_units) do |t| 
     40  testcases        = ENV['TESTCASES'] 
    4041  tests_to_run     = ENV['TESTS']    && ENV['TESTS'].split(',') 
    4142  browsers_to_test = ENV['BROWSERS'] && ENV['BROWSERS'].split(',') 
    42  
     43   
    4344  t.mount("/dist") 
    4445  t.mount("/test") 
    4546   
    4647  Dir["test/unit/*.html"].sort.each do |test_file| 
    47     test_file = "/#{test_file}" 
    48     test_name = test_file[/.*\/(.+?)\.html/, 1] 
    49     t.run(test_file) unless tests_to_run && !tests_to_run.include?(test_name) 
     48    tests = testcases ? { :url => "/#{test_file}", :testcases => testcases } : "/#{test_file}" 
     49    test_filename = test_file[/.*\/(.+?)\.html/, 1] 
     50    t.run(tests) unless tests_to_run && !tests_to_run.include?(test_filename) 
    5051  end 
    5152   
  • spinoffs/prototype/trunk/src/dom.js

    r8557 r8572  
    186186   
    187187  descendants: function(element) { 
    188     return $(element).getElementsBySelector("*"); 
     188    return $(element).select("*"); 
    189189  }, 
    190190   
  • spinoffs/prototype/trunk/test/lib/jstest.rb

    r8514 r8572  
    309309          puts "\nStarted tests in #{browser}" 
    310310          @tests.each do |test| 
    311             browser.visit("http://localhost:4711#{test}?resultsURL=http://localhost:4711/results&t=" + ("%.6f" % Time.now.to_f)) 
     311            params = "resultsURL=http://localhost:4711/results&t=" + ("%.6f" % Time.now.to_f) 
     312            if test.is_a?(Hash) 
     313              params << "&tests=#{test[:testcases]}" if test[:testcases] 
     314              test = test[:url] 
     315            end 
     316            browser.visit("http://localhost:4711#{test}?#{params}") 
    312317  
    313318            result = @queue.pop 
     
    350355  end 
    351356 
    352   # test should be specified as a url 
     357  # test should be specified as a url or as a hash of the form 
     358  # {:url => "url", :testcases => "testFoo,testBar"} 
    353359  def run(test) 
    354360    @tests<<test 
  • spinoffs/prototype/trunk/test/lib/unittest.js

    r7983 r8572  
    3636   
    3737  if(this.mark) Element.remove(this.mark); 
    38   this.mark = document.createElement('div'); 
     38   
     39  var style = 'position: absolute; width: 5px; height: 5px;' +  
     40    'top: #{pointerY}px; left: #{pointerX}px;'.interpolate(options) +  
     41    'border-top: 1px solid red; border-left: 1px solid red;' 
     42     
     43  this.mark = new Element('div', { style: style }); 
    3944  this.mark.appendChild(document.createTextNode(" ")); 
    4045  document.body.appendChild(this.mark); 
    41   this.mark.style.position = 'absolute'; 
    42   this.mark.style.top = options.pointerY + "px"; 
    43   this.mark.style.left = options.pointerX + "px"; 
    44   this.mark.style.width = "5px"; 
    45   this.mark.style.height = "5px;"; 
    46   this.mark.style.borderTop = "1px solid red;" 
    47   this.mark.style.borderLeft = "1px solid red;" 
    4846   
    4947  if(this.step) 
     
    7977}; 
    8078 
    81 var Test = {} 
    82 Test.Unit = {}; 
    83  
    84 // security exception workaround 
    85 Test.Unit.inspect = Object.inspect; 
    86  
    87 Test.Unit.Logger = Class.create(); 
    88 Test.Unit.Logger.prototype = { 
    89   initialize: function(log) { 
    90     this.log = $(log); 
    91     if (this.log) { 
    92       this._createLogTable(); 
    93     } 
    94   }, 
     79var Test = { 
     80  Unit: { 
     81    inspect: Object.inspect // security exception workaround 
     82  } 
     83}; 
     84 
     85Test.Unit.Logger = Class.create({ 
     86  initialize: function(element) { 
     87    this.element = $(element); 
     88    if (this.element) this._createLogTable(); 
     89  }, 
     90   
    9591  start: function(testName) { 
    96     if (!this.log) return; 
    97     this.testName = testName; 
    98     this.lastLogLine = document.createElement('tr'); 
    99     this.statusCell = document.createElement('td'); 
    100     this.nameCell = document.createElement('td'); 
    101     this.nameCell.appendChild(document.createTextNode(testName)); 
    102     this.messageCell = document.createElement('td'); 
    103     this.lastLogLine.appendChild(this.statusCell); 
    104     this.lastLogLine.appendChild(this.nameCell); 
    105     this.lastLogLine.appendChild(this.messageCell); 
    106     this.loglines.appendChild(this.lastLogLine); 
    107   }, 
     92    if (!this.element) return; 
     93    this.element.down('tbody').insert('<tr><td>' + testName + '</td><td></td><td></td></tr>'); 
     94  }, 
     95   
     96  setStatus: function(status) { 
     97    this.getLastLogLine().addClassName(status).down('td', 1).update(status); 
     98  }, 
     99   
    108100  finish: function(status, summary) { 
    109     if (!this.log) return; 
    110     this.lastLogLine.className = status
    111     this.statusCell.innerHTML = status
    112     this.messageCell.innerHTML = this._toHTML(summary); 
    113   }, 
     101    if (!this.element) return; 
     102    this.setStatus(status)
     103    this.message(summary)
     104  }, 
     105   
    114106  message: function(message) { 
    115     if (!this.log) return; 
    116     this.messageCell.innerHTML = this._toHTML(message); 
    117   }, 
     107    if (!this.element) return; 
     108    this.getMessageCell().update(this._toHTML(message)); 
     109  }, 
     110   
    118111  summary: function(summary) { 
    119     if (!this.log) return; 
    120     this.logsummary.innerHTML = this._toHTML(summary); 
    121   }, 
     112    if (!this.element) return; 
     113    this.element.down('div').update(this._toHTML(summary)); 
     114  }, 
     115   
     116  getLastLogLine: function() { 
     117    return this.element.select('tr').last() 
     118  }, 
     119   
     120  getMessageCell: function() { 
     121    return this.getLastLogLine().down('td', 2); 
     122  }, 
     123   
    122124  _createLogTable: function() { 
    123     this.log.innerHTML = 
    124     '<div id="logsummary"></div>' + 
    125     '<table id="logtable">' + 
     125    var html = '<div class="logsummary">running...</div>' + 
     126    '<table class="logtable">' + 
    126127    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' + 
    127     '<tbody id="loglines"></tbody>' + 
     128    '<tbody class="loglines"></tbody>' + 
    128129    '</table>'; 
    129     this.logsummary = $('logsummary') 
    130     this.loglines = $('loglines'); 
    131   }, 
     130    this.element.update(html) 
     131     
     132  }, 
     133   
     134  appendActionButtons: function(actions) { 
     135    actions = $H(actions); 
     136    if (!actions.any()) return; 
     137    var div = new Element("div", {className: 'action_buttons'}); 
     138    actions.inject(div, function(container, action) { 
     139      var button = new Element("input").setValue(action.key).observe("click", action.value); 
     140      button.type = "button"; 
     141      return container.insert(button); 
     142    }); 
     143    this.getMessageCell().insert(div); 
     144  }, 
     145   
    132146  _toHTML: function(txt) { 
    133147    return txt.escapeHTML().replace(/\n/g,"<br/>"); 
    134148  } 
    135 
    136  
    137 Test.Unit.Runner = Class.create(); 
    138 Test.Unit.Runner.prototype = { 
     149}); 
     150 
     151Test.Unit.Runner = Class.create({ 
    139152  initialize: function(testcases) { 
    140     this.options = Object.extend({ 
     153    var options = this.options = Object.extend({ 
    141154      testLog: 'testlog' 
    142155    }, arguments[1] || {}); 
    143     this.options.resultsURL = this.parseResultsURLQueryParameter(); 
    144     if (this.options.testLog) { 
    145       this.options.testLog = $(this.options.testLog) || null; 
    146     } 
    147     if(this.options.tests) { 
    148       this.tests = []; 
    149       for(var i = 0; i < this.options.tests.length; i++) { 
    150         if(/^test/.test(this.options.tests[i])) { 
    151           this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"])); 
    152         } 
    153       } 
    154     } else { 
    155       if (this.options.test) { 
    156         this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])]; 
    157       } else { 
    158         this.tests = []; 
    159         for(var testcase in testcases) { 
    160           if(/^test/.test(testcase)) { 
    161             this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"])); 
    162           } 
    163         } 
    164       } 
    165     } 
     156     
     157    options.resultsURL = this.queryParams.resultsURL; 
     158    options.testLog = $(options.testLog); 
     159     
     160    this.tests = this.getTests(testcases, options); 
    166161    this.currentTest = 0; 
    167     this.logger = new Test.Unit.Logger(this.options.testLog); 
    168     Event.observe(window, "load", function() {  
    169       setTimeout(this.runTests.bind(this), 100); 
     162    this.logger = new Test.Unit.Logger(options.testLog); 
     163    Event.observe(window, "load", function() { 
     164      this.runTests.bind(this).delay(0.1); 
    170165    }.bind(this)); 
    171166  }, 
    172   parseResultsURLQueryParameter: function() { 
    173     return window.location.search.parseQuery()["resultsURL"]; 
    174   }, 
    175   // Returns: 
    176   //  "ERROR" if there was an error, 
    177   //  "FAILURE" if there was a failure, or 
    178   //  "SUCCESS" if there was neither 
     167   
     168  queryParams: window.location.search.parseQuery(), 
     169   
     170  getTests: function(testcases, options) { 
     171    var tests; 
     172    if (this.queryParams.tests) tests = this.queryParams.tests.split(','); 
     173    else if (options.tests) tests = options.tests; 
     174    else if (options.test) tests = [option.test]; 
     175    else tests = Object.keys(testcases).grep(/^test/); 
     176     
     177    return tests.map(function(test) { 
     178      if (testcases[test]) 
     179        return new Test.Unit.Testcase(test, testcases[test], testcases.setup, testcases.teardown); 
     180    }).compact(); 
     181  }, 
     182   
    179183  getResult: function() { 
    180184    var results = { 
     
    199203    } 
    200204  }, 
     205   
    201206  runTests: function() { 
    202     var test = this.tests[this.currentTest]; 
    203     if (!test) { 
    204       // finished! 
    205       this.postResults(); 
    206       this.logger.summary(this.summary()); 
    207       return; 
    208     } 
    209     if(!test.isWaiting) { 
    210       this.logger.start(test.name); 
    211     } 
     207    var test = this.tests[this.currentTest], actions; 
     208     
     209    if (!test) return this.finish(); 
     210    if (!test.isWaiting) this.logger.start(test.name); 
    212211    test.run(); 
    213212    if(test.isWaiting) { 
    214213      this.logger.message("Waiting for " + test.timeToWait + "ms"); 
    215214      setTimeout(this.runTests.bind(this), test.timeToWait || 1000); 
    216     } else { 
    217       this.logger.finish(test.status(), test.summary()); 
    218       var actionButtons = test.actionButtons(); 
    219       if (actionButtons)  
    220         $(this.logger.lastLogLine).down('td', 2).appendChild(actionButtons); 
    221          
    222       this.currentTest++; 
    223       // tail recursive, hopefully the browser will skip the stackframe 
    224       this.runTests(); 
    225     } 
     215      return; 
     216    } 
     217     
     218    this.logger.finish(test.status(), test.summary()); 
     219    if (actions = test.actions) this.logger.appendActionButtons(actions); 
     220    this.currentTest++; 
     221    // tail recursive, hopefully the browser will skip the stackframe 
     222    this.runTests(); 
     223  }, 
     224   
     225  finish: function() { 
     226    this.postResults(); 
     227    this.logger.summary(this.summary()); 
    226228  }, 
    227229   
     
    230232      .interpolate(this.getResult()); 
    231233  } 
    232 
    233  
    234 Test.Unit.Assertions = Class.create(); 
    235 Test.Unit.Assertions.prototype = { 
    236   initialize: function() { 
    237     this.assertions = 0; 
    238     this.failures   = 0; 
    239     this.errors     = 0; 
    240     this.messages   = []; 
    241     this.actions    = {}; 
    242   }, 
    243   summary: function() { 
    244     return ( 
    245       this.assertions + " assertions, " +  
    246       this.failures   + " failures, " + 
    247       this.errors     + " errors" + "\n" + 
    248       this.messages.join("\n")); 
    249   }, 
    250   actionButtons: function() { 
    251     if (!Object.keys(this.actions).any()) return false; 
    252     var div = $(document.createElement("div")); 
    253     div.addClassName("action_buttons"); 
    254    
    255     for (var title in this.actions) { 
    256       var button = $(document.createElement("input")); 
    257       button.value = title; 
    258       button.type = "button"; 
    259       button.observe("click", this.actions[title]); 
    260       div.appendChild(button); 
    261     } 
    262     return div; 
    263   }, 
    264   pass: function() { 
    265     this.assertions++; 
    266   }, 
    267   fail: function(message) { 
    268     this.failures++; 
    269  
    270     var line = ""; 
    271     try { 
    272       throw new Error("stack"); 
    273     } catch(e){ 
    274       line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1]; 
    275     } 
    276  
    277     this.messages.push("Failure: " + message + (line ? " Line #" + line : "")); 
    278   }, 
    279   info: function(message) { 
    280     this.messages.push("Info: " + message); 
    281   }, 
    282   error: function(error, test) { 
    283     this.errors++; 
    284     this.actions['retry with throw'] = function() { test.run(true) }; 
    285     this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) + ")"); 
    286   }, 
    287   status: function() { 
    288     if (this.failures > 0) return 'failed'; 
    289     if (this.errors > 0) return 'error'; 
    290     return 'passed'; 
    291   }, 
    292   isRunningFromRake: (function() { 
    293     return window.location.port == 4711; 
    294   })(), 
     234}); 
     235 
     236Test.Unit.Assertions = { 
    295237  assert: function(expression) { 
    296238    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"'; 
     
    498440  assertElementMatches: function(element, expression) { 
    499441    this.assertElementsMatch([element], expression); 
    500   }, 
     442  } 
     443}; 
     444 
     445Test.Unit.Testcase = Class.create(Test.Unit.Assertions, { 
     446  initialize: function(name, test, setup, teardown) { 
     447    this.name           = name; 
     448    this.test           = test || Prototype.emptyFunction; 
     449    this.setup          = setup || Prototype.emptyFunction; 
     450    this.teardown       = teardown || Prototype.emptyFunction; 
     451    this.messages       = []; 
     452    this.actions        = {}; 
     453  }, 
     454   
     455  isWaiting:  false, 
     456  timeToWait: 1000, 
     457  assertions: 0, 
     458  failures:   0, 
     459  errors:     0, 
     460  isRunningFromRake: window.location.port == 4711, 
     461   
     462  wait: function(time, nextPart) { 
     463    this.isWaiting = true; 
     464    this.test = nextPart; 
     465    this.timeToWait = time; 
     466  }, 
     467   
     468  run: function(rethrow) { 
     469    try { 
     470      try { 
     471        if (!this.isWaiting) this.setup(); 
     472        this.isWaiting = false; 
     473        this.test(); 
     474      } finally { 
     475        if(!this.isWaiting) { 
     476          this.teardown(); 
     477        } 
     478      } 
     479    } 
     480    catch(e) {  
     481      if (rethrow) throw e; 
     482      this.error(e, this);  
     483    } 
     484  }, 
     485   
     486  summary: function() { 
     487    var msg = '#{assertions} assertions, #{failures} failures, #{errors} errors\n'; 
     488    return msg.interpolate(this) + this.messages.join("\n"); 
     489  }, 
     490 
     491  pass: function() { 
     492    this.assertions++; 
     493  }, 
     494   
     495  fail: function(message) { 
     496    this.failures++; 
     497    var line = ""; 
     498    try { 
     499      throw new Error("stack"); 
     500    } catch(e){ 
     501      line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1]; 
     502    } 
     503    this.messages.push("Failure: " + message + (line ? " Line #" + line : "")); 
     504  }, 
     505   
     506  info: function(message) { 
     507    this.messages.push("Info: " + message); 
     508  }, 
     509   
     510  error: function(error, test) { 
     511    this.errors++; 
     512    this.actions['retry with throw'] = function() { test.run(true) }; 
     513    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) + ")"); 
     514  }, 
     515   
     516  status: function() { 
     517    if (this.failures > 0) return 'failed'; 
     518    if (this.errors > 0) return 'error'; 
     519    return 'passed'; 
     520  }, 
     521   
    501522  benchmark: function(operation, iterations) { 
    502523    var startAt = new Date(); 
     
    507528    return timeTaken; 
    508529  } 
    509 } 
    510  
    511 Test.Unit.Testcase = Class.create(); 
    512 Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), { 
    513   initialize: function(name, test, setup, teardown) { 
    514     Test.Unit.Assertions.prototype.initialize.bind(this)(); 
    515     this.name           = name; 
    516     this.test           = test || function() {}; 
    517     this.setup          = setup || function() {}; 
    518     this.teardown       = teardown || function() {}; 
    519     this.isWaiting      = false; 
    520     this.timeToWait     = 1000; 
    521   }, 
    522   wait: function(time, nextPart) { 
    523     this.isWaiting = true; 
    524     this.test = nextPart; 
    525     this.timeToWait = time; 
    526   }, 
    527   run: function(rethrow) { 
    528     try { 
    529       try { 
    530         if (!this.isWaiting) this.setup.bind(this)(); 
    531         this.isWaiting = false; 
    532         this.test.bind(this)(); 
    533       } finally { 
    534         if(!this.isWaiting) { 
    535           this.teardown.bind(this)(); 
    536         } 
    537       } 
    538     } 
    539     catch(e) {  
    540       if (rethrow) throw e; 
    541       this.error(e, this);  
    542     } 
    543   } 
    544530}); 
  • spinoffs/prototype/trunk/test/test.css

    r5456 r8572  
    1313} 
    1414 
    15 #logsummary { 
     15.logsummary { 
     16  margin-top: 1em; 
    1617  margin-bottom: 1em; 
    1718  padding: 1ex; 
     
    2021} 
    2122 
    22 #logtable { 
     23.logtable { 
    2324  width:100%; 
    2425  border-collapse: collapse; 
     
    2627} 
    2728 
    28 #logtable td, #logtable th { 
     29.logtable td, .logtable th { 
    2930  text-align: left; 
    3031  padding: 3px 8px; 
     
    3233} 
    3334 
    34 #logtable .passed { 
     35.logtable .passed { 
    3536  background-color: #cfc; 
    3637} 
    3738 
    38 #logtable .failed, #logtable .error { 
     39.logtable .failed, .logtable .error { 
    3940  background-color: #fcc; 
    4041} 
    4142 
    42 #logtable td div.action_buttons { 
     43.logtable td div.action_buttons { 
    4344  display: inline; 
    4445} 
    4546 
    46 #logtable td div.action_buttons input { 
     47.logtable td div.action_buttons input { 
    4748  margin: 0 5px; 
    4849  font-size: 10px; 
  • spinoffs/prototype/trunk/test/unit/ajax.html

    r8512 r8572  
    412412      } 
    413413    }} 
    414   }, 'testlog'); 
     414  }); 
    415415// ]]> 
    416416</script> 
  • spinoffs/prototype/trunk/test/unit/array.html

    r8451 r8572  
    219219    }} 
    220220 
    221   }, 'testlog'); 
     221  }); 
    222222// ]]> 
    223223</script> 
  • spinoffs/prototype/trunk/test/unit/base.html

    r8190 r8572  
    624624   }} 
    625625 
    626   }, 'testlog'); 
     626  }); 
    627627 
    628628// ]]> 
  • spinoffs/prototype/trunk/test/unit/dom.html

    r8537 r8572  
    16841684      }, this); 
    16851685    }} 
    1686   }, 'testlog'); 
     1686  }); 
    16871687 
    16881688  function preservingBrowserDimensions(callback) { 
  • spinoffs/prototype/trunk/test/unit/element_mixins.html

    r6598 r8572  
    6767      })); 
    6868    }} 
    69   }, 'testlog'); 
     69  }); 
    7070// ]]> 
    7171</script> 
  • spinoffs/prototype/trunk/test/unit/enumerable.html

    r7333 r8572  
    319319      assertEqual(0, [].size()); 
    320320    }} 
    321   }, 'testlog'); 
     321  }); 
    322322// ]]> 
    323323</script> 
  • spinoffs/prototype/trunk/test/unit/event.html

    r8548 r8572  
    243243     
    244244     
    245   }, 'testlog'); 
     245  }); 
    246246 
    247247  document.observe("dom:loaded", function(event) { 
  • spinoffs/prototype/trunk/test/unit/hash.html

    r8139 r8572  
    227227    }} 
    228228     
    229   }, 'testlog'); 
     229  }); 
    230230// ]]> 
    231231</script> 
  • spinoffs/prototype/trunk/test/unit/number.html

    r6957 r8572  
    6262    }} 
    6363 
    64   }, 'testlog'); 
     64  }); 
    6565 
    6666// ]]> 
  • spinoffs/prototype/trunk/test/unit/position.html

    r7303 r8572  
    8282    }} 
    8383     
    84   }, 'testlog'); 
     84  }); 
    8585 
    8686// ]]> 
  • spinoffs/prototype/trunk/test/unit/range.html

    r4942 r8572  
    8787    }} 
    8888     
    89   }, 'testlog'); 
     89  }); 
    9090// ]]> 
    9191</script> 
  • spinoffs/prototype/trunk/test/unit/selector.html

    r8449 r8572  
    432432      assert(typeof results[2].show == 'function'); 
    433433    }} 
    434   }, 'testlog'); 
     434  }); 
    435435// ]]> 
    436436</script> 
  • spinoffs/prototype/trunk/test/unit/string.html

    r8148 r8572  
    566566      assertEqual('"', '"\\""'.evalJSON()); 
    567567    }} 
    568   }, 'testlog'); 
     568  }); 
    569569// ]]> 
    570570</script> 
  • spinoffs/prototype/trunk/test/unit/unit_tests.html

    r7852 r8572  
    2323<!-- Log output --> 
    2424<div id="testlog"> </div> 
     25<div id="testlog_2"> </div> 
    2526 
    2627<!-- Test elements follow --> 
     
    174175    }} 
    175176       
    176   }, "testlog"); 
     177  }); 
     178   
     179  new Test.Unit.Runner({ 
     180    testDummy: function() { with(this) { 
     181      assert(true); 
     182    }}, 
     183     
     184    testMultipleTestRunner: function() { with(this) { 
     185      assertEqual('passed', $('testlog_2').down('td', 1).innerHTML); 
     186    }} 
     187       
     188  }, {testLog: 'testlog_2'}); 
    177189// ]]> 
    178190</script>