jsinstrument 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,34 +6,70 @@
6
6
  !function(root){
7
7
  if (root && (typeof root.%{js_object_name} === 'undefined')) {
8
8
  root.%{js_object_name} = {
9
+ /**
10
+ * Const for coverage data types
11
+ */
12
+ TYPES: ['line', 'func', 'branch'],
9
13
  /**
10
14
  * Coverage data
15
+ * data = {
16
+ * filename: {
17
+ * line: [lines],
18
+ * func: [lines],
19
+ * branch: [lines]
20
+ * }
21
+ * }
11
22
  */
12
- _data: {},
23
+ data: {},
13
24
  /**
14
25
  * register instrumented lines for the file
15
26
  */
16
- register: function(file, lines) {
27
+ register: function(file, lines, func_lines) {
17
28
  // replace the dot in the filename by underscore to be competible with mongodb
18
29
  file = file.replace(/\./g, '_')
19
30
  // can't register twice for one file
20
- if(!this._data[file]) {
21
- this._data[file] = [];
31
+ if(!this.data[file]) {
32
+ this.data[file] = {
33
+ line: [],
34
+ func: [],
35
+ branch: []
36
+ };
22
37
  for (var i in lines) {
23
- this._data[file][lines[i]] = 0;
38
+ this.data[file].line[lines[i]] = 0;
39
+ }
40
+ for (var i in func_lines) {
41
+ this.data[file].func[func_lines[i]] = 0;
42
+ }
43
+ }
44
+ },
45
+ /**
46
+ * Hit a data
47
+ */
48
+ _hit: function(type, file, line) {
49
+ if (file && (this.TYPES.indexOf(type) !== -1)) {
50
+ file = file.replace(/\./g, '_');
51
+ if (this.data[file] && this.data[file][type]) {
52
+ this.data[file][type][line] = (this.data[file][type][line]) ? (this.data[file][type][line] + 1) : 1;
24
53
  }
25
54
  }
26
55
  },
27
56
  /**
28
57
  * Hit a line
29
58
  */
30
- hit: function(file, line) {
31
- // replace the dot in the filename by underscore to be competible with mongodb
32
- file = file.replace(/\./g, '_')
33
- if(!this._data[file]) this._data[file] = [];
34
-
35
- if(!this._data[file][line]) this._data[file][line] = 1;
36
- else this._data[file][line] = this._data[file][line] + 1;
59
+ hit_line: function(file, line) {
60
+ this._hit('line', file, line);
61
+ },
62
+ /**
63
+ * Hit a func
64
+ */
65
+ hit_func: function(file, line) {
66
+ this._hit('func', file, line);
67
+ },
68
+ /**
69
+ * Hit a branch
70
+ */
71
+ hit_branch: function(file, line) {
72
+ this._hit('branch', file, line);
37
73
  },
38
74
  /**
39
75
  * Upload coverage data to server
@@ -46,7 +82,7 @@
46
82
  this._uploading = true;
47
83
 
48
84
  try {
49
- $.post('%{upload_url}', this._serialize(this._data, mark)).done(function() {
85
+ $.post('%{upload_url}', this._serialize(mark)).done(function() {
50
86
  console.log('Coverage data uploaded!');
51
87
  }).always(function() {
52
88
  self._uploading = false;
@@ -58,23 +94,30 @@
58
94
  /**
59
95
  * serialize data for data uploading
60
96
  */
61
- _serialize: function(data, mark) {
97
+ _serialize: function(mark) {
98
+ var data = this.data;
62
99
  var json = [];
63
100
 
64
101
  for (var file in data) {
65
- var coverage = data[file];
102
+ var file_json = [];
103
+
104
+ for (var i in TYPES) {
105
+ var type = TYPES[i];
106
+ var type_coverage = data[file][type];
66
107
 
67
- var array = [];
68
- var length = coverage.length;
69
- for (var line = 0; line < length; line++) {
70
- var value = coverage[line];
71
- if (value === undefined || value === null) {
72
- value = '';
108
+ var array = [];
109
+ var length = type_coverage.length;
110
+ for (var line = 0; line < length; line++) {
111
+ var value = type_coverage[line];
112
+ if (value === undefined || value === null) {
113
+ value = '';
114
+ }
115
+ array.push(value);
73
116
  }
74
- array.push(value);
117
+ file_json.push(this._quote(type) + ':[' + array.join(',') + ']');
75
118
  }
76
119
 
77
- json.push(this._quote(file) + ':[' + array.join(',') + ']');
120
+ json.push(this._quote(file) + ':{' + file_json.join(',') + '}');
78
121
  }
79
122
 
80
123
  var result = '{' + json.join(',') + '}';
@@ -119,7 +162,7 @@
119
162
  }
120
163
 
121
164
  // register the instrumented lines for current file
122
- root.%{js_object_name}.register('%{src_filename}', %{instrumented_lines});
165
+ root.%{js_object_name}.register('%{src_filename}', %{instrumented_lines}, %{instrumented_func_lines});
123
166
  }(this);
124
167
 
125
168
 
@@ -22,7 +22,7 @@ module JSInstrument
22
22
  self.src_filename = filename
23
23
  ast = RKelly::Parser.new.parse src
24
24
  raise 'Parse source failed.' unless ast
25
- ast, instrumented_lines = instrument_ast ast
25
+ ast, instrumented_lines, instrumented_func_lines = instrument_ast ast
26
26
  instrumented_src = ast.to_ecma
27
27
 
28
28
  inserting = File.open(File.dirname(__FILE__) + '/inserting.js').read
@@ -35,7 +35,8 @@ module JSInstrument
35
35
  commit_hash: self.commit_hash,
36
36
  batch_text: self.batch_text,
37
37
  data_compress_method: self.data_compress_method,
38
- instrumented_lines: instrumented_lines
38
+ instrumented_lines: instrumented_lines,
39
+ instrumented_func_lines: instrumented_func_lines
39
40
  }
40
41
 
41
42
  (inserting % bindings) + instrumented_src
@@ -46,12 +47,16 @@ module JSInstrument
46
47
  #
47
48
  ast = RKelly::Visitors::ParentBuilder.new.build ast
48
49
  instrumented_lines = Set.new
50
+ instrumented_func_lines = Set.new
49
51
  ast.each do |node|
50
52
  parent = node.parent
51
53
  line = node.line
52
54
  node_classname = node.class.name.split(/::/)[-1].sub('Node', '')
53
55
  parent_classname = parent.class.name.split(/::/)[-1].sub('Node', '')
56
+
54
57
  # instrument only if we can get the line no. and we haven't instrumented it
58
+
59
+ # line coverage
55
60
  #
56
61
  if line and not instrumented_lines.member? line
57
62
  if ['ExpressionStatement', 'EmptyStatement', 'DoWhile', 'While', 'For', 'ForIn', 'Continue',
@@ -60,7 +65,7 @@ module JSInstrument
60
65
  if 'DoWhile' == parent_classname
61
66
  if parent.left.eql? node
62
67
  instrumented_lines.add line
63
- parent.left = get_inserted_block node
68
+ parent.left = get_hitline_call_block node
64
69
  end
65
70
  # in RKelly, ConditionalNode extends IfNode
66
71
  # which is so annoying
@@ -68,39 +73,45 @@ module JSInstrument
68
73
  elsif ['If', 'While', 'For', 'ForIn', 'With'].index parent_classname
69
74
  if parent.value.eql? node
70
75
  instrumented_lines.add line
71
- parent.value = get_inserted_block node
76
+ parent.value = get_hitline_call_block node
72
77
  elsif parent.else.eql? node
73
78
  instrumented_lines.add line
74
- parent.else = get_inserted_block node
79
+ parent.else = get_hitline_call_block node
75
80
  end
76
81
  elsif ['CaseBlock', 'Label'].index parent_classname
77
82
  # do nothing here
78
83
  elsif 'SourceElements' == parent_classname
79
- if insert_func_before node
84
+ if insert_hitline_before node
80
85
  instrumented_lines.add line
81
86
  end
82
87
  end
83
88
  elsif ['With', 'Try'].index node_classname
84
89
  if 'SourceElements' == parent_classname
85
- if insert_func_before node
90
+ if insert_hitline_before node
86
91
  instrumented_lines.add line
87
92
  end
88
93
  end
89
- elsif ['FunctionDecl', 'FunctionExpr'].index node_classname
90
- if insert_func_before node
91
- instrumented_lines.add line
94
+ end
95
+ end
96
+ # function coverage
97
+ #
98
+ if line and not instrumented_func_lines.member? line
99
+ if ['FunctionDecl', 'FunctionExpr'].index node_classname
100
+ if node.function_body
101
+ insert_hitfunc_in node.function_body, line
102
+ instrumented_func_lines.add line
92
103
  end
93
104
  end
94
105
  end
95
106
  end
96
- [ast, instrumented_lines.sort]
107
+ [ast, instrumented_lines.sort, instrumented_func_lines.sort]
97
108
  end
98
109
 
99
110
  private
100
- def get_insert_func_call line
111
+ def get_hit_call line, func
101
112
  ExpressionStatementNode.new(
102
113
  FunctionCallNode.new(
103
- ResolveNode.new("#{self.js_object_name}.hit"),
114
+ ResolveNode.new("#{self.js_object_name}.#{func}"),
104
115
  ArgumentsNode.new([
105
116
  StringNode.new("\"#{self.src_filename}\""),
106
117
  NumberNode.new(line)
@@ -109,16 +120,16 @@ module JSInstrument
109
120
  )
110
121
  end
111
122
 
112
- def get_inserted_block node
123
+ def get_hitline_call_block node
113
124
  BlockNode.new(
114
125
  SourceElementsNode.new([
115
- get_insert_func_call(node.line),
126
+ get_hit_call(node.line, "hit_line"),
116
127
  node
117
128
  ])
118
129
  )
119
130
  end
120
131
 
121
- def insert_func_before node
132
+ def insert_hitline_before node
122
133
  # get the original node line
123
134
  #
124
135
  line = node.line
@@ -131,9 +142,15 @@ module JSInstrument
131
142
  false
132
143
  else
133
144
  index = node.parent.value.index{|x| x.eql? node}
134
- node.parent.value.insert index, get_insert_func_call(line)
145
+ node.parent.value.insert index, get_hit_call(line, "hit_line")
135
146
  true
136
147
  end
137
148
  end
149
+
150
+ def insert_hitfunc_in function_body_node, line
151
+ # FunctionBody.value(SourceElement).value(array).unshift hit_func
152
+ function_body_node.value.value.unshift get_hit_call(line, "hit_func")
153
+ end
154
+
138
155
  end
139
156
  end
@@ -1,3 +1,3 @@
1
1
  module JSInstrument
2
- VERSION = "0.0.10"
2
+ VERSION = "0.0.11"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsinstrument
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.11
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-17 00:00:00.000000000 Z
12
+ date: 2013-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rkelly