Skip to content

Allow instrumentation to be turned on using .ember-cli#7491

Merged
hjdivad merged 2 commits intoember-cli:masterfrom
patocallaghan:patoc/config-instrumentation
Jan 31, 2018
Merged

Allow instrumentation to be turned on using .ember-cli#7491
hjdivad merged 2 commits intoember-cli:masterfrom
patocallaghan:patoc/config-instrumentation

Conversation

@patocallaghan
Copy link
Copy Markdown
Contributor

@patocallaghan patocallaghan commented Dec 6, 2017

Addresses #7429

This is a WIP so hoping to get some feedback. I'm open to changing the namings + approach if required. What I initially thought to be straightforward has turned into somewhat of a rabbit-hole 😄

  • abstracted retrieving the Yam config for the project out to utilities/get-config.
  • Introducing the config into utiltities/instrumentation meant needing to move some things around as there are circular dependencies between Instrumentation => Project => Instrumentation. Otherwise it would blow up on init.
  • Testing is pretty tricky and not fully 🍏 yet. Due to:
    1. Finding an issue with yam whereby a falsey value returns undefined. I have a fix here [FIX] Fix issue where falsey property is returned as undefined twokul/yam#16
    2. The _enableFSMonitorIfInstrumentationEnabled tests in models/instrumentation-test.js are not atomic. Using heimdall-fs-monitor to wrap around fs.statSync doesn't clean up after itself so affects later tests.
      describe('._enableFSMonitorIfInstrumentationEnabled', function() {
      let originalStatSync = fs.statSync;
      beforeEach(function() {
      expect(!!process.env.BROCCOLI_VIZ).to.eql(false);
      expect(!!process.env.EMBER_CLI_INSTRUMENTATION).to.eql(false);
      });
      afterEach(function() {
      td.reset();
      });
      it('if VIZ is NOT enabled, do not monitor', function() {
      let monitor = Instrumentation._enableFSMonitorIfInstrumentationEnabled();
      try {
      expect(fs.statSync).to.equal(originalStatSync);
      expect(monitor).to.eql(undefined);
      } finally {
      if (monitor) {
      monitor.stop();
      }
      }
      });
      it('if VIZ is enabled, monitor', function() {
      process.env.BROCCOLI_VIZ = '1';
      let monitor = Instrumentation._enableFSMonitorIfInstrumentationEnabled();
      try {
      expect(fs.statSync).to.not.equal(originalStatSync);
      } finally {
      if (monitor) {
      monitor.stop();
      }
      }
      });
      it('if instrumentation is enabled, monitor', function() {
      process.env.EMBER_CLI_INSTRUMENTATION = '1';
      let monitor = Instrumentation._enableFSMonitorIfInstrumentationEnabled();
      try {
      expect(fs.statSync, 'fs.statSync').to.not.equal(originalStatSync, '[original] fs.statSync');
      } finally {
      if (monitor) {
      monitor.stop();
      }
      }
      });
      });
      . In particular it's because 1) we don't reassign fs.statSync back to it's initial state after each test and 2) calling FSMonitor() sets some module-level vars in heimdall-fs-monitor (isFirstInstance, isMonitorRegistrant) which means trying to set up FSMonitor() in a following test doesn't fully work. It doesn't re-"attach" the monitor functions.

Any feedback on the approach in general is appreciated. Also any ideas how to workaround around the heimdall-fs-monitor testing issue. I could submit a PR to fix that repo too if useful?

let instrumentationEnabled = utilsInstrumentation.instrumentationEnabled;

function _enableFSMonitorIfInstrumentationEnabled() {
function _enableFSMonitorIfInstrumentationEnabled(config) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Need to be able to pass the config in here so we can "mock" it in tests? Is this a smell or okay to do?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks fine to me.

return cli.instrumentation;
}

let Instrumentation = require('./instrumentation');
Copy link
Copy Markdown
Contributor Author

@patocallaghan patocallaghan Dec 6, 2017

Choose a reason for hiding this comment

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

I needed to move the Instrumentation require inline otherwise it blew up on init due to circular dependencies between Instrumentation => getConfig => Project => Instrumentation and things not being fully set up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we add a comment that describes this?

Also note the only reason we need the project in get-config is to get the project root, so it's more immediately clear to a future traveler what they would need to tackle if they wanted to change things.

primary: Project.getProjectRoot(),
});

module.exports = function getConfig(mockedConfig) {
Copy link
Copy Markdown
Contributor Author

@patocallaghan patocallaghan Dec 6, 2017

Choose a reason for hiding this comment

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

allow passing in a mocked config to support current mocking behavior in some tests

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Super minor, but within here can we call it configOverride instead of mockedConfig?

beforeEach(function() {
expect(!!process.env.BROCCOLI_VIZ).to.eql(false);
expect(!!process.env.EMBER_CLI_INSTRUMENTATION).to.eql(false);
expect(fs.statSync).to.equal(originalStatSync);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Assert that we always start with clean slate on fs.statSync

});

afterEach(function() {
fs.statSync = originalStatSync;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Reset fs.statSync to its original state

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's not do this; we should instead fix heimdall-fs-monitor

process.env.EMBER_CLI_INSTRUMENTATION = '1';
let monitor = Instrumentation._enableFSMonitorIfInstrumentationEnabled();
try {
expect(fs.statSync).to.not.equal(originalStatSync);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This test fails due to heimdall-fs-monitor not fully cleaning up after itself. Some module level variables are set in a previous test which cause it be isEnabled already. This means when calling FSMonitor() a second time, it doesn't re-"attach" the monitor functions around the fs functions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

okay it looks like this problem in heimdall-fs-monitor was actually fixed a while ago. I've released 0.2.0 to deal with stop not cleaning up properly.

Copy link
Copy Markdown
Contributor

@hjdivad hjdivad left a comment

Choose a reason for hiding this comment

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

Couple of very minor suggestions but otherwise LGTM (sans greening up tests of course)

let instrumentationEnabled = utilsInstrumentation.instrumentationEnabled;

function _enableFSMonitorIfInstrumentationEnabled() {
function _enableFSMonitorIfInstrumentationEnabled(config) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks fine to me.

return cli.instrumentation;
}

let Instrumentation = require('./instrumentation');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we add a comment that describes this?

Also note the only reason we need the project in get-config is to get the project root, so it's more immediately clear to a future traveler what they would need to tackle if they wanted to change things.

primary: Project.getProjectRoot(),
});

module.exports = function getConfig(mockedConfig) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Super minor, but within here can we call it configOverride instead of mockedConfig?

});

afterEach(function() {
fs.statSync = originalStatSync;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's not do this; we should instead fix heimdall-fs-monitor

process.env.EMBER_CLI_INSTRUMENTATION = '1';
let monitor = Instrumentation._enableFSMonitorIfInstrumentationEnabled();
try {
expect(fs.statSync).to.not.equal(originalStatSync);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

okay it looks like this problem in heimdall-fs-monitor was actually fixed a while ago. I've released 0.2.0 to deal with stop not cleaning up properly.

@hjdivad
Copy link
Copy Markdown
Contributor

hjdivad commented Dec 12, 2017

@patocallaghan updated heimdall-fs-monitor on your branch; stop should clean up better now, LMK if you're still seeing issues here.

@patocallaghan
Copy link
Copy Markdown
Contributor Author

Appreciate the feedback @hjdivad. Will fix up per your comments.

@patocallaghan patocallaghan force-pushed the patoc/config-instrumentation branch from 33141d2 to f88c4a2 Compare December 18, 2017 00:14

before(function() {
settings = new Yam('ember-cli', {
let mockedYam = new Yam('ember-cli', {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Needed to slightly refactor this test to get it to pass.

if (configOverride) {
return configOverride;
}
return new Yam('ember-cli', {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I needed to bring the new Yam() into the getConfig function because it was failing the tests/acceptance/pods-destroy-test.js tests otherwise. Preferably the new Yam would happen at the module scope but in the pod-destroy tests the config wasn't being re-calculated after modifying the .ember-cli file.

@patocallaghan patocallaghan changed the title [WIP] Allow instrumentation to be turned on using .ember-cli Allow instrumentation to be turned on using .ember-cli Dec 18, 2017
@patocallaghan
Copy link
Copy Markdown
Contributor Author

@hjdivad made some changes per your feedback. Also tests should be 🍏 now.

@patocallaghan patocallaghan force-pushed the patoc/config-instrumentation branch from f88c4a2 to 0e8853e Compare January 8, 2018 12:42
@patocallaghan
Copy link
Copy Markdown
Contributor Author

@hjdivad I just did rebase against current master and the branch is still 🍏

Copy link
Copy Markdown
Contributor

@twokul twokul left a comment

Choose a reason for hiding this comment

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

Couple of minor things but I think this looks good apart from @hjdivad comments

return cli.instrumentation;
}

// Instrumentation `require` needs to occur inline due to circular dependencies between Instrumentation => getConfig => Project => Instrumentation. getConfig is used in Project to get the project root.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sorry for nitpicking but could you wrap break this comment into separate lines, around 80 characters long?


function instrumentationEnabled() {
return vizEnabled() || process.env.EMBER_CLI_INSTRUMENTATION === '1';
function isConfigEnabled(configOverride) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe better to name it isInstrumetationConfigEnabled?

@patocallaghan patocallaghan force-pushed the patoc/config-instrumentation branch from 0e8853e to 4f76e86 Compare January 9, 2018 00:05
@patocallaghan
Copy link
Copy Markdown
Contributor Author

No problem @twokul, I've addressed yours and @hjdivad's comments. I've also resolved some merge conflicts with the yarn.lock on master.

if (configOverride) {
return configOverride;
}
return new Yam('ember-cli', {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should we load a new Yam on each invocation of getConfig, or memoize?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@stefanpenner I'd initially tried have the new Yam line at the module level but had to refactor because tests which relied on mocking the config failed (pod tests I believe). I can't remember the specific reasoning but I'll have another go and see.

Copy link
Copy Markdown
Contributor

@hjdivad hjdivad left a comment

Choose a reason for hiding this comment

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

LGTM although it'd be good to look into @stefanpenner's comment re Yam instances

@hjdivad
Copy link
Copy Markdown
Contributor

hjdivad commented Jan 10, 2018

thanks @patocallaghan!

@patocallaghan
Copy link
Copy Markdown
Contributor Author

@stefanpenner @hjdivad I've just pushed a little spike of memoizing the Yam config in getConfig by moving it back to the module level. By doing that it fails a bunch of pod tests which rely on having the .ember-cli file having usePods: true. For example this test here:

it('.ember-cli usePods setting correctly destroys component', function() {
let commandArgs = ['component', 'x-foo'];
let files = [
'app/components/x-foo/component.js',
'app/components/x-foo/template.hbs',
'tests/integration/components/x-foo/component-test.js',
];
return assertDestroyAfterGenerateWithUsePods(commandArgs, files);
});

When the test is initially run the get-config module is loaded and the config is generated but then when we get to the assertDestroyAfterGenerateWithUsePods we augment the config with the usePods: true setting:

replaceFile('.ember-cli', '"disableAnalytics": false', '"disableAnalytics": false,\n"usePods" : true\n');

But as we've memoized the config on module load we don't have a Yam with usePods set.

I've added some sort of solution but to be honest it feels really wrong. When in test mode it generates a new Yam but when not it uses the memoized one. There's a precedent for this elsewhere in the tests but it still doesn't make it right 😄

If anyone has any suggestions for a better approach I'm all ears :)

@patocallaghan patocallaghan force-pushed the patoc/config-instrumentation branch from ab1d50c to fa901c2 Compare January 17, 2018 21:15
@patocallaghan
Copy link
Copy Markdown
Contributor Author

@stefanpenner @hjdivad @twokul any thoughts re my last comment here? Happy to rethink the testing if it's not acceptable.

FYI I rebased against master and now the branch is failing. It appears master is also failing with the same error.


let config = generateConfig();

module.exports = function getConfig(configOverride, shouldRegenerateConfig) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

instead of adding another parameter for this, what do you think about exporting a separate reset function for tests?

in es6 this would be a non default export, but in this case it should be fine to hang it off the default export. eg

let config;

module.exports = function getConfig(configOverride) {
  if (configOverride) {
    return configOverride;
  }

  if (config === undefined) {
    config = generateConfig();
  }

  return config;
}

// private; used in testing
module.exports._reset = function() {
  config = undefined;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll give that a shot. Thanks 👍

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@hjdivad it turns out the problematic tests were actually removed in a recent commit 😄 So there's no longer a need to jump through hoops to get the suite to pass. Tests are 🍏 locally and the config memoization works.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

oh nice; so if you rebase and push we should expect the PR to be green then

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it looks like master is still 🔴

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think these tests we just moved into ember-source so that is why we're seeing the failures over there now when upgrading ember-cli.

@patocallaghan if it suits, let's have a chat about this tomorrow

@patocallaghan patocallaghan force-pushed the patoc/config-instrumentation branch from fa901c2 to 5bed858 Compare January 17, 2018 23:27
@stefanpenner
Copy link
Copy Markdown
Contributor

is there a downside to not memoizing?

@patocallaghan patocallaghan force-pushed the patoc/config-instrumentation branch from 5bed858 to d284dc8 Compare January 18, 2018 21:52
@patocallaghan
Copy link
Copy Markdown
Contributor Author

@stefanpenner I guess the downside would be that we're creating new Yam objects every time we call getConfig even though it doesn't change over the lifecycle of the command (or can it?)

@patocallaghan patocallaghan force-pushed the patoc/config-instrumentation branch from d284dc8 to ca48df5 Compare January 29, 2018 14:34
@patocallaghan
Copy link
Copy Markdown
Contributor Author

@hjdivad and @stefanpenner I've rebased again and the PR is now 🍏

@hjdivad hjdivad merged commit f46648f into ember-cli:master Jan 31, 2018
@hjdivad
Copy link
Copy Markdown
Contributor

hjdivad commented Jan 31, 2018

thanks @patocallaghan!

@GavinJoyce
Copy link
Copy Markdown
Contributor

GavinJoyce commented Apr 4, 2018

This appears to break some generators which use "usePods": true in .ember-cli: https://github.com/emberjs/ember.js/pull/16465/files#r179132445

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.

5 participants