Skip to content

Conversation

@DanTup
Copy link
Contributor

@DanTup DanTup commented Jun 6, 2018

This is still a bit rough and has no tests, but I wanted to nail down the logic before I start adding tests against it. This is based on the logic described in #13379 (comment). I don't entirely like how we have to do it (for ex. trying to create on without the sdk ID because we can't get a list of these, only devices/targets) but (on my Macbook at least) it seems to work.

LMK what you think. We may also wish to review the device we pick (currently we try pixel and pixel_xl) and whether we want to lock to a specific Android API version or just take the highest (which is what this currently does).

screen shot 2018-06-06 at 1 13 35 pm

(cc @devoncarew @mit-mit)

Copy link
Contributor

@devoncarew devoncarew left a comment

Choose a reason for hiding this comment

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

Thanks for starting this! I added some comments on the CLI UI.

Copy link
Contributor

Choose a reason for hiding this comment

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

use a named param instead?

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 matches other functions in this file. Should we change them to keep them consistent?

String getAdbPath([AndroidSdk existingSdk]);
String getEmulatorPath([AndroidSdk existingSdk]);

Copy link
Contributor

Choose a reason for hiding this comment

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

No, let's keep them consistent.

Copy link
Contributor

Choose a reason for hiding this comment

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

I suspect this fallback logic parallels other android tools lookups in the source base (adb?). We may want to just fail if we can't locate an android sdk however (not do the path based lookup for avdmanager).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently there are three possibilities in here (same for the other few in this file):

  1. If it exists on the SDK passed in (which looks to come from context[AndroidSdk]), we use that
  2. If we can locate an SDK, that's used
  3. Otherwise we try os.which('emulator')?.path

I'm not sure which out 1 and 2 is the best one (or whether we need 3). WDYT? If going with 1 then we can just delete this function; but I'm not sure whether that's a good bet. There are some other paths that do seem to use that and bail out if it's not set without any fallback, eg.:

  Future<bool> _checkForSupportedAdbVersion() async {
    if (androidSdk == null)
      return false;

Though interestingly, it then calls getAdbPath which does the fallback; but in this case it would never trigger because of the return above.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's just delete the os.which('emulator')?.path option.

Though interestingly, it then calls getAdbPath which does the fallback; but in this case it would never trigger because of the return above.

Hmm, sounds like some logic was orphaned by a refactor. Falling back to 'which adb' was useful since the user could then have a little functionality - like listing devices - even if they didn't have the full functionality that configuring an android sdk would give. If this regressed at some point, and people didn't notice, than it's likely that we don't need the fallback even for adb.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll remove the which. Looks like there are still some code paths that don't do what I described above, including getAdbDevices, so my comment about was a little misleading. Whether or not _checkForSupportedAdbVersion is behaving as we'd like though, I'm not sure. LMK if you think this needs some investigation and I'll take a look outside of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should --create be a flag (instead of an option)? We could auto-name the resulting image (pixel, pixel_xl-2, ...), and have an optional flag to name a created image.

Copy link
Contributor

Choose a reason for hiding this comment

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

create feels like a subcommand, even if the package:args api doesn't make it easy to have it as one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should --create be a flag (instead of an option)? We could auto-name the resulting image

I was a bit unsure about this. If we pick the name for the user then it feels weird if we tell them "an emulator with that name already exists" so I was going to make them always provide it.

Of course, we could do the check ourselves first and only prompt if it already exists, but that might be better handled in the editor than here so it has better control over how to present it.

I was wondering if we should expose an emulator.getDeviceProfiles. That way when the user chooses "create emulator" in the editor, we can fetch the possible profiles (and filter it to pixels, nexuses, whatever) and then let them pick a device. I think being able to pick between a phone and a tablet could be helpful?

Copy link
Contributor

Choose a reason for hiding this comment

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

If we pick the name for the user then it feels weird if we tell them "an emulator with that name already exists"

We could choose a name, see if it already exists, and suffix something to de-dup (my_name-2).

and then let them pick a device

I generally like to avoid asking the user for input on stdin. I think flutter emulators --create --name=my_device would be a reasonable UI, but am not fixed on it if you think there's a better way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, gone with this. We only add suffixes if we're generating the name, so if the user provides an explicit name we'll just give them an error:

dantup-macbookpro:flutter dantup$ flutter emulator --create
Emulator 'flutter_emulator' created successfully.

dantup-macbookpro:flutter dantup$ flutter emulator --create
Emulator 'flutter_emulator_1' created successfully.

dantup-macbookpro:flutter dantup$ flutter emulator --create --name flutter_emulator_1
Failed to create emulator 'flutter_emulator_1'.
Error: Android Virtual Device 'flutter_emulator_1' already exists.

It feels a bit weird doing flutter emulator --create and not flutter emulator create but I think that's unavoidable as-is.

Do we have any convention for indicating when a flag is only usable in conjunction with another (eg. --name only makes sense with --create)? I couldn't see anything obvious.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have any convention for indicating when a flag is only usable in conjunction with another (eg. --name only makes sense with --create)? I couldn't see anything obvious

Not that I know of; likely just something we should do w/ the help text for the flags.

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 found some are prefixed with Used with flag --xxx. so I'll copy that.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should print some text here about where they can find more info about creating emulator images (referencing flutter.io, or Android / Android Studio docs).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How's this? Seems a bit weird sending people to Android Studio or avdmanager docs but since the errors come from them I'm not sure if we can provide the info ourselves.

screen shot 2018-06-11 at 6 00 30 pm

Copy link
Contributor

Choose a reason for hiding this comment

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

This looks great for error text. We'll probably also want something brief for flutter emulators -h help text as well - perhaps pointing to a flutter.io page?

@mit-mit, @mjohnsullivan - we may want to consider a brief landing page on flutter.io describing how to create android emulators, or at least just routing people to places to find for info about it.

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've tweaked this a little so the help text is output in more places (eg. at the bottom of the emulator list it gives you instruction for create and launch). If we add a page to the website we should swap out the URLs currently there for our own (and our web page can link to those others if required).

@DanTup
Copy link
Contributor Author

DanTup commented Jun 12, 2018

This still badly needs some tests - assuming the general idea is ok I'll work on them this week.

@devoncarew
Copy link
Contributor

This still badly needs some tests - assuming the general idea is ok I'll work on them this week.

Idea looks great!

@DanTup
Copy link
Contributor Author

DanTup commented Jun 14, 2018

Ok, I've pushed some tests for this. It turned out to be a bit more involved than I expected - I had to make some changes to the existing code to use processManager (so it can be mocked) plus some other tweaks. I tried to split the commits up to make it easier to understand all the changes.

I don't know if this is all the best way to do this (I've not used Mockito before and I'm not sure of our preferences between mocked and real integration tests) but this basically mocks the process manager to return canned responses instead of calling real SDK commands (I wasn't sure how well that's work in the build environment). Please review carefully and nitpick away!

I'm going to do some additional manual testing on Windows shortly, but seemed worthwhile kicking the CI run off to get the results quicker.

(also rebased on master due to conflicts with the error changes merged yesterday)

DanTup added 24 commits June 14, 2018 10:54
This allows it to be tested in the EmulatorManager tests and also used by daemon later if desired.
Process calls are mocked to avoid needing a real SDK (and to be fast). Full integration tests may be useful, but may require ensuring all build environments etc. are set up correctly.
Previous changes were to emulatorPath!
reduce throws on an empty collection.
The name we attempted to use will now always be returned, even in the case of failure.
On Windows after installing Andriod Studio I didn't have any of these and got this message. Installing with sdkmanager fixed the issue.
runResult had a toString() but we moved to ProcessResult when switching to ProcessManager to this ended up throwing "Instance of ProcessResult".
There seemed to be issues using Lists in args with Mockito that I couldn't figure out (docs say to use typed() but I couldn't make this compile with these lists still)..
@DanTup
Copy link
Contributor Author

DanTup commented Jun 14, 2018

Apparently this code had many issues! :(

I've fixed a bunch of bugs from testing on Windows and on the build servers (eg. due to the avd folder didn't exist) and improved some errors. We're touching a lot of stuff so I suspect we may find more edge cases at it runs in more places.

After this, I think it'd might be good to have a real end-to-end test that does something like:

  • create emulator
  • launch emulator
  • check a new device appeared in flutter devices
  • maybe even launch an app on it?

I'm not sure how fragile that might be (or how well the build infrastructure will support it - I'd guess the devicelab machines should be able to do it?) but it might be a good check.

@DanTup
Copy link
Contributor Author

DanTup commented Jun 14, 2018

I seem to get a lot of issues like this on build servers:

00:18 +109: test/emulator_test.dart: EmulatorManager create emulator with an empty name does not fail
type 'MappedListIterable' is not a subtype of type 'List<int>' of 'availableApiVersions'

Yet get no warnings locally, even with --preview-dart-2 passed to VM and analyzer. Not sure if I'm doing something wrong?

}
}

Future<Null> _createEmulator(String name) async {
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 name be optional 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.

I'll make it so, though it's not required since there's only one place that calls this and it pulls it from a map, so it's always passing an argument. It is valid for it to be null (and we'll auto-generate it) though, so I'll make it optional.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool, I think it just makes it a bit more explicit that it can be null (in the absence of non-null annotations).


final String device = await _getPreferredAvailableDevice();
if (device == null)
return new CreateEmulatorResult(
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: the formatting here looks off. 2 space indent? no trailing comma?

Since this is a newer file - added by you recently - you may just want to dartfmt it as a level-set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done (though I tweaked some of its changes where they seemed to make it less readable).

error: 'No device definitions are available'
);

final String sdkId = await _getPreferredSdkId();
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: two spaces before await.

argParser.addOption('launch',
help: 'The full or partial ID of the emulator to launch.');
argParser.addFlag('create',
help: 'Creates a new Android emulator.',
Copy link
Contributor

Choose a reason for hiding this comment

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

We may want to make this a bit longer; we could say that we'll try and create a pixel device, and perhaps include a goo.gl shortened link for more info (until we have a short link to a flutter.io page).

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 added "based on a Pixel device" though not sure what more to add. Do we have somewhere reasonable to link to? The current links we have (in the help) go to pages on managing AVDs with the Android SDK which might not be totally appropriate here?

}

Future<Null> _createEmulator(String name) async {
Future<Null> _createEmulator([String name]) async {
Copy link
Contributor

Choose a reason for hiding this comment

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

As a nit, I generally use named params.

@DanTup DanTup merged commit cdb0118 into flutter:master Jun 28, 2018
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants