Skip to content

Commit 4ba31b7

Browse files
Giovanni Buccipmarchini
authored andcommitted
assert: make assertion_error use Myers diff algorithm
Fixes: #51733 Co-Authored-By: Pietro Marchini <pietro.marchini94@gmail.com> PR-URL: #54862 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 0130780 commit 4ba31b7

8 files changed

Lines changed: 731 additions & 424 deletions

File tree

‎lib/internal/assert/assertion_error.js‎

Lines changed: 138 additions & 242 deletions
Large diffs are not rendered by default.

‎lib/internal/assert/myers_diff.js‎

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
'use strict';
2+
3+
const{
4+
Array,
5+
ArrayPrototypeFill,
6+
ArrayPrototypePush,
7+
ArrayPrototypeSlice,
8+
StringPrototypeEndsWith,
9+
}=primordials;
10+
11+
constcolors=require('internal/util/colors');
12+
13+
constkNopLinesToCollapse=5;
14+
15+
functionareLinesEqual(actual,expected,checkCommaDisparity){
16+
if(actual===expected){
17+
returntrue;
18+
}
19+
if(checkCommaDisparity){
20+
return`${actual},`===expected||actual===`${expected},`;
21+
}
22+
returnfalse;
23+
}
24+
25+
functionmyersDiff(actual,expected,checkCommaDisparity=false){
26+
constactualLength=actual.length;
27+
constexpectedLength=expected.length;
28+
constmax=actualLength+expectedLength;
29+
constv=ArrayPrototypeFill(Array(2*max+1),0);
30+
31+
consttrace=[];
32+
33+
for(letdiffLevel=0;diffLevel<=max;diffLevel++){
34+
constnewTrace=ArrayPrototypeSlice(v);
35+
ArrayPrototypePush(trace,newTrace);
36+
37+
for(letdiagonalIndex=-diffLevel;diagonalIndex<=diffLevel;diagonalIndex+=2){
38+
letx;
39+
if(diagonalIndex===-diffLevel||
40+
(diagonalIndex!==diffLevel&&v[diagonalIndex-1+max]<v[diagonalIndex+1+max])){
41+
x=v[diagonalIndex+1+max];
42+
}else{
43+
x=v[diagonalIndex-1+max]+1;
44+
}
45+
46+
lety=x-diagonalIndex;
47+
48+
while(x<actualLength&&y<expectedLength&&areLinesEqual(actual[x],expected[y],checkCommaDisparity)){
49+
x++;
50+
y++;
51+
}
52+
53+
v[diagonalIndex+max]=x;
54+
55+
if(x>=actualLength&&y>=expectedLength){
56+
returnbacktrack(trace,actual,expected,checkCommaDisparity);
57+
}
58+
}
59+
}
60+
}
61+
62+
functionbacktrack(trace,actual,expected,checkCommaDisparity){
63+
constactualLength=actual.length;
64+
constexpectedLength=expected.length;
65+
constmax=actualLength+expectedLength;
66+
67+
letx=actualLength;
68+
lety=expectedLength;
69+
constresult=[];
70+
71+
for(letdiffLevel=trace.length-1;diffLevel>=0;diffLevel--){
72+
constv=trace[diffLevel];
73+
constdiagonalIndex=x-y;
74+
letprevDiagonalIndex;
75+
76+
if(diagonalIndex===-diffLevel||
77+
(diagonalIndex!==diffLevel&&v[diagonalIndex-1+max]<v[diagonalIndex+1+max])){
78+
prevDiagonalIndex=diagonalIndex+1;
79+
}else{
80+
prevDiagonalIndex=diagonalIndex-1;
81+
}
82+
83+
constprevX=v[prevDiagonalIndex+max];
84+
constprevY=prevX-prevDiagonalIndex;
85+
86+
while(x>prevX&&y>prevY){
87+
constvalue=!checkCommaDisparity||
88+
StringPrototypeEndsWith(actual[x-1],',') ? actual[x-1] : expected[y-1];
89+
ArrayPrototypePush(result,{__proto__: null,type: 'nop', value });
90+
x--;
91+
y--;
92+
}
93+
94+
if(diffLevel>0){
95+
if(x>prevX){
96+
ArrayPrototypePush(result,{__proto__: null,type: 'insert',value: actual[x-1]});
97+
x--;
98+
}else{
99+
ArrayPrototypePush(result,{__proto__: null,type: 'delete',value: expected[y-1]});
100+
y--;
101+
}
102+
}
103+
}
104+
105+
returnresult;
106+
}
107+
108+
functionprintSimpleMyersDiff(diff){
109+
letmessage='';
110+
111+
for(letdiffIdx=diff.length-1;diffIdx>=0;diffIdx--){
112+
const{ type, value }=diff[diffIdx];
113+
if(type==='insert'){
114+
message+=`${colors.green}${value}${colors.white}`;
115+
}elseif(type==='delete'){
116+
message+=`${colors.red}${value}${colors.white}`;
117+
}else{
118+
message+=`${colors.white}${value}${colors.white}`;
119+
}
120+
}
121+
122+
return`\n${message}`;
123+
}
124+
125+
functionprintMyersDiff(diff,simple=false){
126+
letmessage='';
127+
letskipped=false;
128+
letnopCount=0;
129+
130+
for(letdiffIdx=diff.length-1;diffIdx>=0;diffIdx--){
131+
const{ type, value }=diff[diffIdx];
132+
constpreviousType=(diffIdx<(diff.length-1)) ? diff[diffIdx+1].type : null;
133+
consttypeChanged=previousType&&(type!==previousType);
134+
135+
if(typeChanged&&previousType==='nop'){
136+
// Avoid grouping if only one line would have been grouped otherwise
137+
if(nopCount===kNopLinesToCollapse+1){
138+
message+=`${colors.white}${diff[diffIdx+1].value}\n`;
139+
}elseif(nopCount===kNopLinesToCollapse+2){
140+
message+=`${colors.white}${diff[diffIdx+2].value}\n`;
141+
message+=`${colors.white}${diff[diffIdx+1].value}\n`;
142+
}if(nopCount>=(kNopLinesToCollapse+3)){
143+
message+=`${colors.blue}...${colors.white}\n`;
144+
message+=`${colors.white}${diff[diffIdx+1].value}\n`;
145+
skipped=true;
146+
}
147+
nopCount=0;
148+
}
149+
150+
if(type==='insert'){
151+
message+=`${colors.green}+${colors.white}${value}\n`;
152+
}elseif(type==='delete'){
153+
message+=`${colors.red}-${colors.white}${value}\n`;
154+
}elseif(type==='nop'){
155+
if(nopCount<kNopLinesToCollapse){
156+
message+=`${colors.white}${value}\n`;
157+
}
158+
nopCount++;
159+
}
160+
}
161+
162+
message=message.trimEnd();
163+
164+
return{message: `\n${message}`, skipped };
165+
}
166+
167+
module.exports={ myersDiff, printMyersDiff, printSimpleMyersDiff };

‎test/parallel/test-assert-checktag.js‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,20 @@ test('', { skip: !hasCrypto }, () => {
2929
()=>assert.deepStrictEqual(date,fake),
3030
{
3131
message: 'Expected values to be strictly deep-equal:\n'+
32-
'+ actual - expected\n\n+ 2016-01-01T00:00:00.000Z\n- Date {}'
32+
'+ actual - expected\n'+
33+
'\n'+
34+
'+ 2016-01-01T00:00:00.000Z\n'+
35+
'- Date {}\n'
3336
}
3437
);
3538
assert.throws(
3639
()=>assert.deepStrictEqual(fake,date),
3740
{
3841
message: 'Expected values to be strictly deep-equal:\n'+
39-
'+ actual - expected\n\n+ Date {}\n- 2016-01-01T00:00:00.000Z'
42+
'+ actual - expected\n'+
43+
'\n'+
44+
'+ Date {}\n'+
45+
'- 2016-01-01T00:00:00.000Z\n'
4046
}
4147
);
4248
}

0 commit comments

Comments
 (0)