Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

error origins #529

Merged
merged 30 commits into from
Oct 12, 2020
Merged

error origins #529

merged 30 commits into from
Oct 12, 2020

Conversation

izulin
Copy link
Collaborator

@izulin izulin commented Sep 23, 2020

Context

Errors should preserve the original cell that triggers them.

How has this been tested?

Adds a new feature: CellError type carries the address of the original cell that produced the error.

Additionally, exporting/serialization produces nicely human-readable addresses. So we can have following tests:

    const engine = HyperFormula.buildFromArray([
      ['=NA()', '=A1']
    ])
    expect(engine.getCellValue(adr('A1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1'))
    expect(engine.getCellValue(adr('B1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1'))

We also add custom matchers for jest/jasmine, so the test can be written simply:

    expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA))
    expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.NA))

An example of a usecase is to track the 'sources' of error in the spreadsheet:

  const vals = engine.getAllSheetsValues()

  for(let sheetName in vals) {
    const sheetVals = vals[sheetName]
    for(let x = 0; x<sheetVals.length; x++) {
      for(let y = 0; y<sheetVals[x].length; y++) {
        const val = sheetVals[x][y]
        if(val instanceof DetailedCellError) {
          const addr = {
            sheet: engine.getSheetId(sheetName)!,
            col: y,
            row: x
          }
          if (val.address === engine.simpleCellAddressToString(addr, -1)) {
            console.log(addr, val, engine.getCellFormula(addr))
          }
        }
      }
    }
  }

Custom matchers setup

In order to compare detailed errors without refactoring all unit tests, new matcher toEqualError was introduced. Defining new matcher for both jest and jasmine turned out to be tricky as there is type definition clash.

Current setup looks like this:

  1. Jest matchers and additional boostrap file are defined in separate catalog to easily exclude them in jasmine tsconfig (tsconfig.test.json). Otherwise jasmine will complain during the tests.

  2. For some reason using tsconfig.jest.json in main directory didn't do the job. Neither VSC nor IJ were able to detect new matchers correctly. Workaround was to move this file to ./test/tsconfig.json. It is crucial for local development and debugging as it overrides types definition to use jest ones.

This allows to run jest and jasmine tests and debug tests locally. Hopefully this will simplify someday.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature or improvement (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Additional language file or change to the existing one (translations)

Related issue(s):

Checklist:

  • My code follows the code style of this project,
  • My change requires a change to the documentation,
  • I described the modification in the CHANGELOG.md file.

@izulin izulin marked this pull request as draft September 23, 2020 11:51
@izulin izulin marked this pull request as ready for review October 3, 2020 16:05
Copy link
Contributor

@wojciechczerniak wojciechczerniak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to request a review from @swistach for the tests configuration changes

src/Cell.ts Outdated
@@ -112,6 +112,7 @@ export class CellError {
constructor(
public readonly type: ErrorType,
public readonly message?: string,
public address?: SimpleCellAddress,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO shouldn't be optional, and should be readonly. Set once and propagated. Do we need to edit it later?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to edit it later?

Yes, errors are sometimes created in a context that lacks address, and this value is supplemented later.
Right now this address is set in a single place in an interpreter.

// @ts-ignore
spyOn = jest.spyOn
expect.extend(toEqualError)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we add this to the main bootstrap.ts? Now we have two, one clearly marked as jest the other unknown. We should follow a convention one way or another.

CC @swistach

Copy link
Collaborator

@voodoo11 voodoo11 Oct 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that present setup looks a littlebit strange, but there were some problems and type definition clashes otherwise. If you now how to simplify this setup to be able to work locally with jest in IntelliJ and debugger and not break jasmine at the same time, feel free to correct it. I failed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline. I will try to describe problem behind it in PR description.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wojciechczerniak I've added reminders in tsconfigs and updated PR description.

"extends": "../tsconfig",
"include": ["../src", "../test"],
"exclude": []
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file no longer has jest in its name. Looks like the main tsconfig for tests 🤔 If we're doing such changes they should be in a separate PR for easier discussion. I have to summon @swistach here for help 🧙

CC @swistach

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment above #529 (comment)

if (typeof val === 'number') {
if (isNumberOverflow(val)) {
return new CellError(ErrorType.NUM, ErrorMessage.NaN)
val = new CellError(ErrorType.NUM, ErrorMessage.NaN)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the wrapperForAddress reason why the error address is not private? We could pass it to the constructor right here without wrapping it later

Suggested change
val = new CellError(ErrorType.NUM, ErrorMessage.NaN)
return new CellError(ErrorType.NUM, ErrorMessage.NaN, formulaAddress)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, but the reason behind wrapperForAddress is to fix whatever comes from evaluation, even in nested, recursive calls

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't all functions return the correct error? We're patching the error object just to avoid fixing this in the right places or there is some way to create an error that don't know it's origin?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored it slightly to make the field private.
To answer your question:

  • I'm not sure whether in every place the constructor of CellError is called, we have formulaAddress in the context (there might be some obscure case that would require ugly refactor)
  • Adding it here, in a single place, is a minimal change to code that made this feature work, and thus it's much easier to make sure the logic is correctly added in every evaluation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getter/setter are ok, but my main idea was that CellError is immutable.

Once created it cannot be altered, especially the origin address. Having this editable is IMO a risk.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not getter/setter, its getter and (setter that only sets it once, if the value is not present yet). So I don't see any risk here. Also, keep in mind that this value is supplementary one, it does not affect outcome of calculation, only provides debugging info.

test/_setupFiles/jest/toEqualError.ts Outdated Show resolved Hide resolved
expect(engine.getCellValue(adr('A1'))).toEqualError(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1'))
expect(engine.getCellValue(adr('B1'))).toEqualError(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1'))
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a few more test cases.

Does it work with Named Expressions (they are in -1 worksheet)?
Does it work with a function, nested function?
With operators?
Between two worksheets?
CRUD operations? (I guess that removing sheet/column will create a new #REF error anyway)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some tests. This logic is orthogonal to CRUDs, so any fault here would be fault in CRUDs, and should be tested with CRUDs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, true 👍 Thanks for the tests. I've seen that we support all those scenarios, just wanted avoid regressions in the future.

test/matchers.spec.ts Outdated Show resolved Hide resolved
src/CellValue.ts Outdated Show resolved Hide resolved
@wojciechczerniak
Copy link
Contributor

@swistach Nevermind! Let's talk it over

@wojciechczerniak wojciechczerniak removed the request for review from mrpiotr-dev October 5, 2020 15:09
izulin and others added 6 commits October 5, 2020 22:17
Co-authored-by: Wojciech Czerniak <wojciech.czerniak@gmail.com>
Co-authored-by: Wojciech Czerniak <wojciech.czerniak@gmail.com>
@lgtm-com
Copy link
Contributor

lgtm-com bot commented Oct 5, 2020

This pull request fixes 8 alerts when merging 66b3b4d into 7152125 - view on LGTM.com

fixed alerts:

  • 8 for Unused variable, import, function or class

@lgtm-com
Copy link
Contributor

lgtm-com bot commented Oct 6, 2020

This pull request fixes 8 alerts when merging 8c4cff4 into 7152125 - view on LGTM.com

fixed alerts:

  • 8 for Unused variable, import, function or class

@izulin
Copy link
Collaborator Author

izulin commented Oct 8, 2020

@wojciechczerniak CellError address is now immutable.

Please see new tests for CYCLE resolving.

Also, namedExpression + Error address tests were using improper matcher...

@lgtm-com
Copy link
Contributor

lgtm-com bot commented Oct 8, 2020

This pull request introduces 1 alert and fixes 8 when merging 1f112c6 into 7152125 - view on LGTM.com

new alerts:

  • 1 for Unused variable, import, function or class

fixed alerts:

  • 8 for Unused variable, import, function or class

@lgtm-com
Copy link
Contributor

lgtm-com bot commented Oct 8, 2020

This pull request introduces 1 alert and fixes 8 when merging e60f181 into 7152125 - view on LGTM.com

new alerts:

  • 1 for Unused variable, import, function or class

fixed alerts:

  • 8 for Unused variable, import, function or class

@lgtm-com
Copy link
Contributor

lgtm-com bot commented Oct 10, 2020

This pull request fixes 8 alerts when merging 0cbfd9e into c1630f0 - view on LGTM.com

fixed alerts:

  • 8 for Unused variable, import, function or class

@lgtm-com
Copy link
Contributor

lgtm-com bot commented Oct 10, 2020

This pull request fixes 8 alerts when merging 287ee6e into c1630f0 - view on LGTM.com

fixed alerts:

  • 8 for Unused variable, import, function or class

@codecov
Copy link

codecov bot commented Oct 10, 2020

Codecov Report

Merging #529 into develop will increase coverage by 0.00%.
The diff coverage is 93.75%.

Impacted file tree graph

@@           Coverage Diff            @@
##           develop     #529   +/-   ##
========================================
  Coverage    97.23%   97.23%           
========================================
  Files          155      156    +1     
  Lines        29429    29466   +37     
  Branches      3575     3583    +8     
========================================
+ Hits         28614    28651   +37     
  Misses         809      809           
  Partials         6        6           
Impacted Files Coverage Δ
src/Exporter.ts 91.66% <91.66%> (ø)
src/BuildEngineFactory.ts 100.00% <100.00%> (ø)
src/Cell.ts 97.05% <100.00%> (+0.16%) ⬆️
src/CellValue.ts 100.00% <100.00%> (+8.26%) ⬆️
src/Emitter.ts 100.00% <100.00%> (ø)
src/Evaluator.ts 97.88% <100.00%> (ø)
src/HyperFormula.ts 99.38% <100.00%> (+<0.01%) ⬆️
src/Serialization.ts 100.00% <100.00%> (ø)
src/index.ts 100.00% <100.00%> (ø)
src/interpreter/Interpreter.ts 97.26% <100.00%> (+0.05%) ⬆️
... and 5 more

Copy link
Contributor

@wojciechczerniak wojciechczerniak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 🏆

@izulin izulin merged commit 7fb1926 into develop Oct 12, 2020
@izulin izulin deleted the pu/errors branch October 12, 2020 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants