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

Bugfix: Stryker init creates a temp folder #361

Conversation

sharikovvladislav
Copy link
Contributor

@sharikovvladislav sharikovvladislav commented Aug 28, 2017

Impl of #320

TODO:

  • change name of class to TempFolder
  • refactor codebase to the new TempFolder class
  • refactor unit tests
  • perhaps: units

@sharikovvladislav
Copy link
Contributor Author

sharikovvladislav commented Aug 28, 2017

  • Is TODO is ok, should I do something else?
  • If I understand correctly there are no units for current StrykerTempFolder module. Should I add tests? What kind of tests do we need?
  • do you have this problem? http://i.imgur.com/gowExH0.png. I can't see @types/jasmine. If I add it, everything becomes ok.

Also:

  • is it ok that I ask you this questions? :) should I do everything alone maybe? I think it is much faster if I ask. It is much worse when I do something and do it again after you said I did bad.
  • can I ask in gitter then?

PS: some time build will be red.

@nicojs
Copy link
Member

nicojs commented Aug 28, 2017

Todo looks fine indeed. Thanks for picking this up!

is it ok that I ask you this questions? :) should I do everything alone maybe? I think it is much faster if I ask. It is much worse when I do something and do it again after you said I did bad.

That's fine, if your ok with waiting up to a day on answers. Gitter can be faster at times.

do you have this problem? http://i.imgur.com/gowExH0.png. I can't see @types/jasmine. If I add it, everything becomes ok.

We're not using jasmine as a test framework, instead we're using mocha (test framework) and chai (assertion library). It looks like jasmine, but it is not.

If I understand correctly there are no units for current StrykerTempFolder module. Should I add tests? What kind of tests do we need?

True and yes i would like unit tests, but not strictly necessary. We can do that some other time.

@sharikovvladislav
Copy link
Contributor Author

Ok, lets continue with questions in gitter.

@sharikovvladislav
Copy link
Contributor Author

sharikovvladislav commented Aug 29, 2017

Any ideas ? https://gitter.im/stryker-mutator/stryker?at=59a40add66c1c7c477ecba5c

I put back old name and tests became green. I am not sure. Is this correct?

@nicojs
Copy link
Member

nicojs commented Aug 29, 2017

indeed, the integration tests of the stryker-html-reporter contain a snapshot of the events stryker reporter callbacks, in order to test the integration of stryker-html-reporter without the need to run stryker each time. So it's loosely coupled.

I see you changed the expect back to it's original name, i think that's fine. There is no technical reason to keep the events in the stryker-html-reporter in sync with current stryker file names. That would be a waste of time

@nicojs
Copy link
Member

nicojs commented Aug 29, 2017

For the unit tests, be sure to mock away all the interactions with the file system, as we want our unit tests to be able to run in complete isolation, without any side effects. That is the difference with integration tests, as there we usually test with side effects. Side effects here is creating files stuff.

In this case, you should stub mkdirp.sync, fs.writeFile and deleteDir.

@sharikovvladislav
Copy link
Contributor Author

Okay! So one more item to do is to create new unit tests for refactored class, right?

I did all other work? Am I right? Is everything correct? I mean: refactoring of the class to fix the bug.

@nicojs
Copy link
Member

nicojs commented Aug 29, 2017

I did all other work? Am I right? Is everything correct? I mean: refactoring of the class to fix the bug.

Yes looks good! indeed the only thing remaining is add unit tests.

@sharikovvladislav
Copy link
Contributor Author

Ok, cool. I will research how to do this.

Copy link
Member

@nicojs nicojs left a comment

Choose a reason for hiding this comment

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

Yes looks good! indeed the only thing remaining is add unit tests.

@nicojs
Copy link
Member

nicojs commented Aug 30, 2017

It might be better to not create the temp folder in the constructor. Instead, we should have an initialize(folderName: string) method. This creates the temp folder. This will make it better testable and we are able to implement #336 easier in a later stage. The idea is that the initialize function is called once right before the initial test run, from the Stryker class. Right now it would always provide .stryker-tmp as a default, later we can change it to a config setting. If initialize is not called, we should not do anything different, just let the code break.

Making methods dependent on each other like this is a bad practise, but the only way around that would be to not use a Singleton, instead create an instance of a TempFolder and pass that along. You can do that as well if you choose.

The TempFolder is only used during mutation testing. It is the place were we store the sandboxes on disk. A sandbox is a copy of all your files with (possibly) a mutation in one of them. It is where the actual mutation testing occurs. I started a discussion on #366 to see if we can do in-memory testing. If that happens we can delete the TempFolder all together as its not needed.

So the TempFolder is only used for sandboxes. If a class wants to log something it should create an instance of the log4js Logger and start using that. Later we can add logging to a log file using log4js functionality. For now, don't mind about log files.

@sharikovvladislav
Copy link
Contributor Author

sharikovvladislav commented Aug 30, 2017

Okay, I updated the code. What do you think? I added initialize method. I will update tests if you think this is ok. https://github.com/stryker-mutator/stryker/pull/361/files#diff-3b57d820a130bce2b8a1d1428567eb28R13

@sharikovvladislav
Copy link
Contributor Author

Any thoughts?

Copy link
Member

@nicojs nicojs left a comment

Choose a reason for hiding this comment

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

Already looks good. Had some small remarks on the unit tests.


beforeEach(() => {
sandbox = sinon.sandbox.create();
sandbox.stub(StrykerTempFolder, 'writeFile');
tempFolderMock = mock(TempFolder);
Copy link
Member

Choose a reason for hiding this comment

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

Looks good! Awesome this works, even with a class with a private constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Ah true. Damn, i think this is a limitation of typescript. It will work if you you change the mock:

export function mock<T>(constructorFn: { new(...args: any[]): T; }): Mock<T>;
export function mock<T>(constructorFn: any): Mock<T>;
export function mock<T>(constructorFn: any): Mock<T> {
  return sinon.createStubInstance(constructorFn) as Mock<T>;
}

And than use it like this: mock<TempFolder>(TempFolder);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm. Now I am getting this: https://travis-ci.org/stryker-mutator/stryker/jobs/270886353#L1771. Still can not get what is the problem.


const log = log4js.getLogger('TempFolder');

export class TempFolder {
Copy link
Member

Choose a reason for hiding this comment

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

We should add a private constructor() { } in order to prevent anyone from creating an instance them selves.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

tempFolderMock = mock(TempFolder);
sandbox.stub(TempFolder, 'instance').returns(tempFolderMock);
tempFolderMock.createRandomFolder.returns(workingFolder);
tempFolderMock.copyFile.resolves({});
Copy link
Member

Choose a reason for hiding this comment

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

Looks great! It is indeed better to use stub.resolves instead of stub.returns(Promise.resolve())

});
afterEach(() => sandbox.restore());

it('TempFolder is presented', () => {
Copy link
Member

Choose a reason for hiding this comment

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

What exactly are you testing here? This test can never fail, right? If one of the methods is missing, we would get a compile error in our production code. The benefit of using typescript, is that we don't need tests like these. You can remove it 👍

In this test, your basically verifying that TypeScript is transpiled correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test will fail if somebody remove/rename this class by accident . This test will force him think about it again. Do you think it is not needed?

Copy link
Member

Choose a reason for hiding this comment

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

No. That's why we use typescript. Having the test won't hurt of course.

import * as mkdirp from 'mkdirp';
import * as fs from 'mz/fs';

describe.only('TempFolder', () => {
Copy link
Member

Choose a reason for hiding this comment

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

Remove the only here 😺

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, I will. Tests are not ready atm 😿 I will do it of course when I finish to debug my units.

const mockCwd = process.cwd() + '/some/dir';
sandbox.stub(mkdirp, 'sync');
sandbox.stub(process, 'cwd').returns(mockCwd);
sandbox.stub(TempFolder.instance(), 'random').returns('rand');
Copy link
Member

Choose a reason for hiding this comment

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

I think it's better to move this stubbing to the top before each. Putting it right here clutters the tests a bit. You can still do the cwdStub.returns(mockCwd) and randomStub.returns('rand') here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

});
});

xdescribe('constructor logic', () => {
Copy link
Member

Choose a reason for hiding this comment

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

Should this still be removed? Or changed into the testing of initialize()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it should be changed. Tests are not ready. I will update them since current code is ok (as I am getting from your review). I just wanted to know if TempFolder.ts code is correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed it.

@sharikovvladislav
Copy link
Contributor Author

sharikovvladislav commented Sep 1, 2017

Uhm. I was asking about is current implementation is ok (not tests). Test are not ready at all. Actually, I am ready to put them into a trash.

Since everything except units looks good (as I understood from your review) I am going to fix units now.

@sharikovvladislav
Copy link
Contributor Author

sharikovvladislav commented Sep 1, 2017

@nicojs Do you think TempFolder should generate an exception if some method was called, but initialize() were not called?

* Deletes the Stryker-temp folder
*/
clean() {
log.debug(`Cleaning stryker temp folder ${this.baseTempFolder}`);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is word "clean" in that context"? As I understand "clean" means "clean contents of the directory". But purpose of that function is to remove directory completely.

Are you sure this is correct? Perhaps "remove" will be better since it will say what really happens.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, remove would be better. Yes the purpose is to remove the dir completely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is word "clean" in that context"?

Sorry I wanted to say "is it correct in that context"?

OK! I will fix.

@sharikovvladislav
Copy link
Contributor Author

How do you see unit test for copyFile?:

  copyFile(fromFilename: string, toFilename: string, instrumenter: NodeJS.ReadWriteStream | null): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      let readStream: NodeJS.ReadableStream = fs.createReadStream(fromFilename, { encoding: 'utf8' });
      let writeStream = fs.createWriteStream(toFilename);
      readStream.on('error', reject);
      writeStream.on('error', reject);
      if (instrumenter) {
        readStream = readStream.pipe(instrumenter);
      }
      readStream.pipe(writeStream);
      readStream.on('end', () => resolve());
    });
  }

Do you have any examples of units for same things?

@nicojs
Copy link
Member

nicojs commented Sep 1, 2017

@nicojs Do you think TempFolder should generate an exception if some method was called, but initialize() were not called?

Well... maybe that's smart. It communicates clear intent (but you will need extra unit tests).

How do you see unit test for copyFile?:

You can use the 'test/helpers/streamHelpers.ts'. It has helpers to convert a stream to a string and back. But you can also skip this unit test, as we're not going to use it anymore in the future when we have transpiler support (coming with stryker-typescript).

@sharikovvladislav
Copy link
Contributor Author

but you will need extra unit tests

How this unit test will look like if one and first initialize call will influence all tests. Perhaps I can reset state to uninitialised manually after each test. I will not initialize it before each test but only for specific.

@@ -81,7 +81,8 @@ export default class Stryker {
this.reporter.onScoreCalculated(score);
ScoreResultCalculator.determineExitCode(score, this.config.thresholds);
await this.wrapUpReporter();
await StrykerTempFolder.clean();
TempFolder.instance().initialize();
Copy link
Member

Choose a reason for hiding this comment

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

I think this one should be moved up right? Right before the initialTestRun?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@nicojs
Copy link
Member

nicojs commented Sep 2, 2017

Perhaps I can reset state to uninitialized manually after each test.

I will not initialize it before each test but only for specific.

That sounds good, but i would do it in a beforeEach just to be sure. So one beforeEach in a describe('when temp folder is initialized') and one in a describe('when temp folder is not initialized')

The private constructor seems to work now. I think just moving the call to the initialization

@sharikovvladislav
Copy link
Contributor Author

sharikovvladislav commented Sep 2, 2017

I added tests. Here is test run output:

    createRandomFolder
      when temp folder is initialized
        ✓ should create dir with correct path
      when temp folder is not initialized
        ✓ should throw error
    clean
      when temp folder is initialized
        ✓ should call deleteDir fileApi
      when temp folder is not initialized
        ✓ should throw error
    writeFile
      ✓ should call fs.writeFile
  6 passing (17ms)
  • What do you think?
  • I did not add initialise call check to writeFile since this methods does not work with baseTempFolder. Why they are in TempFolder?
  • Did you try prettier? (:

@sharikovvladislav
Copy link
Contributor Author

Also final check is passed: tmp dir is not created on stryker init but it is created on stryker run.

@nicojs nicojs merged commit a4333c9 into stryker-mutator:master Sep 3, 2017
@nicojs
Copy link
Member

nicojs commented Sep 3, 2017

Thanks!

@sharikovvladislav sharikovvladislav deleted the bugfix/issue-320/temp-directory-creates-on-file-import branch September 3, 2017 14:37
sharikovvladislav added a commit to sharikovvladislav/stryker that referenced this pull request Sep 3, 2017
* master:
  fix(stryker-init): Stryker init won't create temp folder (stryker-mutator#361)
  Publish
  fix(stryker task): Guard against clearing the require cache (stryker-mutator#362) (stryker-mutator#369)
  chore(build): Make build ts 2.5 compatible (stryker-mutator#368)
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

2 participants