Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #942 from ckeditor/t/engine-debug-tools-1
Browse files Browse the repository at this point in the history
Feature: When engine debugging is on, additional logs will be provided when delta transformation causes editor to throw an error.
  • Loading branch information
Piotr Jasiun authored May 16, 2017
2 parents cb18a95 + 0461cb1 commit 2ae80ca
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 40 deletions.
79 changes: 44 additions & 35 deletions src/dev-utils/enableenginedebug.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const LOG_SEPARATOR = '-------';
let enabled = false;

// Logging function used to log debug messages.
let log = console.log;
let logger = console;

/**
* Enhances model classes with logging methods. Returns a plugin that should be loaded in the editor to
Expand Down Expand Up @@ -101,13 +101,12 @@ let log = console.log;
* All those methods take one parameter, which is a version of {@link module:engine/model/document~Document model document}
* for which model or view document state should be logged.
*
* @param {Function} [logger] Function used to log messages. By default messages are logged to console.
* @param {Object} [_logger] Object with functions used to log messages and errors. By default messages are logged to console.
* If specified, it is expected to have `log()` and `error()` methods.
* @returns {module:engine/dev-utils/enableenginedebug~DebugPlugin} Plugin to be loaded in the editor.
*/
export default function enableEngineDebug( logger ) {
if ( logger ) {
log = logger;
}
export default function enableEngineDebug( _logger = console ) {
logger = _logger;

if ( !enabled ) {
enabled = true;
Expand All @@ -126,58 +125,58 @@ function enableLoggingTools() {
};

ModelPosition.prototype.log = function() {
log( 'ModelPosition: ' + this );
logger.log( 'ModelPosition: ' + this );
};

ModelRange.prototype.toString = function() {
return `${ this.root } [ ${ this.start.path.join( ', ' ) } ] - [ ${ this.end.path.join( ', ' ) } ]`;
};

ModelRange.prototype.log = function() {
log( 'ModelRange: ' + this );
logger.log( 'ModelRange: ' + this );
};

ModelText.prototype.toString = function() {
return `#${ this.data }`;
};

ModelText.prototype.logExtended = function() {
log( `ModelText: ${ this }, attrs: ${ mapString( this.getAttributes() ) }` );
logger.log( `ModelText: ${ this }, attrs: ${ mapString( this.getAttributes() ) }` );
};

ModelText.prototype.log = function() {
log( 'ModelText: ' + this );
logger.log( 'ModelText: ' + this );
};

ModelTextProxy.prototype.toString = function() {
return `#${ this.data }`;
};

ModelTextProxy.prototype.logExtended = function() {
log( `ModelTextProxy: ${ this }, attrs: ${ mapString( this.getAttributes() ) }` );
logger.log( `ModelTextProxy: ${ this }, attrs: ${ mapString( this.getAttributes() ) }` );
};

ModelTextProxy.prototype.log = function() {
log( 'ModelTextProxy: ' + this );
logger.log( 'ModelTextProxy: ' + this );
};

ModelElement.prototype.toString = function() {
return `<${ this.rootName || this.name }>`;
};

ModelElement.prototype.log = function() {
log( 'ModelElement: ' + this );
logger.log( 'ModelElement: ' + this );
};

ModelElement.prototype.logExtended = function() {
log( `ModelElement: ${ this }, ${ this.childCount } children, attrs: ${ mapString( this.getAttributes() ) }` );
logger.log( `ModelElement: ${ this }, ${ this.childCount } children, attrs: ${ mapString( this.getAttributes() ) }` );
};

ModelElement.prototype.logAll = function() {
log( '--------------------' );
logger.log( '--------------------' );

this.logExtended();
log( 'List of children:' );
logger.log( 'List of children:' );

for ( let child of this.getChildren() ) {
child.log();
Expand Down Expand Up @@ -217,23 +216,23 @@ function enableLoggingTools() {
};

ModelElement.prototype.logTree = function() {
log( this.printTree() );
logger.log( this.printTree() );
};

ModelRootElement.prototype.toString = function() {
return this.rootName;
};

ModelRootElement.prototype.log = function() {
log( 'ModelRootElement: ' + this );
logger.log( 'ModelRootElement: ' + this );
};

ModelDocumentFragment.prototype.toString = function() {
return `documentFragment`;
};

ModelDocumentFragment.prototype.log = function() {
log( 'ModelDocumentFragment: ' + this );
logger.log( 'ModelDocumentFragment: ' + this );
};

ModelDocumentFragment.prototype.printTree = function() {
Expand Down Expand Up @@ -263,11 +262,11 @@ function enableLoggingTools() {
};

ModelDocumentFragment.prototype.logTree = function() {
log( this.printTree() );
logger.log( this.printTree() );
};

Operation.prototype.log = function() {
log( this.toString() );
logger.log( this.toString() );
};

AttributeOperation.prototype.toString = function() {
Expand Down Expand Up @@ -307,11 +306,11 @@ function enableLoggingTools() {
};

Delta.prototype.log = function() {
log( this.toString() );
logger.log( this.toString() );
};

Delta.prototype.logAll = function() {
log( '--------------------' );
logger.log( '--------------------' );

this.log();

Expand All @@ -337,7 +336,17 @@ function enableLoggingTools() {
const _deltaTransformTransform = deltaTransform.transform;

deltaTransform.transform = function( a, b, isAMoreImportantThanB ) {
const results = _deltaTransformTransform( a, b, isAMoreImportantThanB );
let results;

try {
results = _deltaTransformTransform( a, b, isAMoreImportantThanB );
} catch ( e ) {
logger.error( 'Error during delta transformation!' );
logger.error( a.toString() + ( isAMoreImportantThanB ? ' (important)' : '' ) );
logger.error( b.toString() + ( isAMoreImportantThanB ? '' : ' (important)' ) );

throw e;
}

for ( let i = 0; i < results.length; i++ ) {
results[ i ]._saveHistory( {
Expand Down Expand Up @@ -425,23 +434,23 @@ function enableLoggingTools() {
};

ViewText.prototype.logExtended = function() {
log( 'ViewText: ' + this );
logger.log( 'ViewText: ' + this );
};

ViewText.prototype.log = function() {
log( 'ViewText: ' + this );
logger.log( 'ViewText: ' + this );
};

ViewTextProxy.prototype.toString = function() {
return `#${ this.data }`;
};

ViewTextProxy.prototype.logExtended = function() {
log( 'ViewTextProxy: ' + this );
logger.log( 'ViewTextProxy: ' + this );
};

ViewTextProxy.prototype.log = function() {
log( 'ViewTextProxy: ' + this );
logger.log( 'ViewTextProxy: ' + this );
};

ViewElement.prototype.printTree = function( level = 0 ) {
Expand All @@ -467,7 +476,7 @@ function enableLoggingTools() {
};

ViewElement.prototype.logTree = function() {
log( this.printTree() );
logger.log( this.printTree() );
};

ViewDocumentFragment.prototype.printTree = function() {
Expand All @@ -487,7 +496,7 @@ function enableLoggingTools() {
};

ViewDocumentFragment.prototype.logTree = function() {
log( this.printTree() );
logger.log( this.printTree() );
};
}

Expand All @@ -512,7 +521,7 @@ function enableReplayerTools() {
return '';
}

const appliedDeltas = this._appliedDeltas.concat( this._lastDelta.toJSON() );
const appliedDeltas = this._appliedDeltas.concat( this._lastDelta );

return appliedDeltas.map( JSON.stringify ).join( LOG_SEPARATOR );
};
Expand All @@ -526,7 +535,7 @@ function enableDocumentTools() {
const _modelDocumentApplyOperation = ModelDocument.prototype.applyOperation;

ModelDocument.prototype.applyOperation = function( operation ) {
log( 'Applying ' + operation );
logger.log( 'Applying ' + operation );

if ( !this._operationLogs ) {
this._operationLogs = [];
Expand Down Expand Up @@ -565,12 +574,12 @@ function enableDocumentTools() {
};

function logDocument( document, version ) {
log( '--------------------' );
logger.log( '--------------------' );

if ( document[ treeDump ][ version ] ) {
log( document[ treeDump ][ version ] );
logger.log( document[ treeDump ][ version ] );
} else {
log( 'Tree log unavailable for given version: ' + version );
logger.log( 'Tree log unavailable for given version: ' + version );
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/model/delta/attributedelta.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ export default class AttributeDelta extends Delta {
return AttributeDelta;
}

/**
* @inheritDoc
*/
toJSON() {
const json = super.toJSON();

delete json._range;

return json;
}

/**
* @inheritDoc
*/
Expand Down
27 changes: 23 additions & 4 deletions tests/dev-utils/enableenginedebug.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe( 'enableEngineDebug', () => {
} );

describe( 'debug tools', () => {
let DebugPlugin, log;
let DebugPlugin, log, error;

class TestEditor extends StandardEditor {
constructor( ...args ) {
Expand All @@ -75,7 +75,8 @@ describe( 'debug tools', () => {

before( () => {
log = sinon.spy();
DebugPlugin = enableEngineDebug( log );
error = sinon.spy();
DebugPlugin = enableEngineDebug( { log, error } );
} );

afterEach( () => {
Expand Down Expand Up @@ -766,7 +767,7 @@ describe( 'debug tools', () => {
} );
} );

describe( 'should provide means for saving delta history transformation', () => {
describe( 'should provide debug tools for delta transformation', () => {
let document, root, otherRoot;

beforeEach( () => {
Expand Down Expand Up @@ -892,7 +893,7 @@ describe( 'debug tools', () => {
} );
} );

it( 'recreate delta using history', () => {
it( 'recreate delta using Delta#history', () => {
const deltaA = new MoveDelta();
const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
deltaA.addOperation( opA );
Expand Down Expand Up @@ -931,6 +932,24 @@ describe( 'debug tools', () => {

expect( JSON.stringify( recreated ) ).to.equal( JSON.stringify( original ) );
} );

it( 'provide additional logging when transformation crashes', () => {
const deltaA = new MoveDelta();
const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
deltaA.addOperation( opA );

const deltaB = new InsertDelta();
const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
deltaB.addOperation( opB );

deltaTransform.defaultTransform = () => {
throw new Error();
};

expect( () => deltaTransform.transform( deltaA, deltaB, true ) ).to.throw( Error );
expect( error.calledWith( deltaA.toString() + ' (important)' ) ).to.be.true;
expect( error.calledWith( deltaB.toString() ) ).to.be.true;
} );
} );

function expectLog( expectedLogMsg ) {
Expand Down
2 changes: 1 addition & 1 deletion tests/dev-utils/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe( 'model test utils', () => {
test( '[this is test] text' );
} );

it( 'should insert text with selection inside #2', () => {
it( 'should insert text with selection inside #3', () => {
test( 'this is [test text]' );
} );

Expand Down
7 changes: 7 additions & 0 deletions tests/model/delta/attributedelta.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Element from '../../../src/model/element';
import AttributeDelta from '../../../src/model/delta/attributedelta';
import { RootAttributeDelta } from '../../../src/model/delta/attributedelta';
import AttributeOperation from '../../../src/model/operation/attributeoperation';
import { jsonParseStringify } from '../../../tests/model/_utils/utils';

describe( 'Batch', () => {
let batch, doc, root;
Expand Down Expand Up @@ -507,6 +508,12 @@ describe( 'AttributeDelta', () => {
it( 'should provide proper className', () => {
expect( AttributeDelta.className ).to.equal( 'engine.model.delta.AttributeDelta' );
} );

it( 'should not have _range property when converted to JSON', () => {
const json = jsonParseStringify( delta );

expect( json ).not.to.have.property( '_range' );
} );
} );

describe( 'RootAttributeDelta', () => {
Expand Down

0 comments on commit 2ae80ca

Please sign in to comment.