Unverified Commit 76cbbeb6 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tool] Send the local time to analytics with screens and events (#36545)

parent 6830edd0
......@@ -12,6 +12,7 @@ import '../base/context.dart';
import '../base/file_system.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/time.dart';
import '../base/utils.dart';
import '../features.dart';
import '../globals.dart';
......@@ -19,6 +20,9 @@ import '../version.dart';
const String _kFlutterUA = 'UA-67589403-6';
// Attached to all `Usage.sendCommand` and `Usage.sendEvent`.
const String _kLocalTimeParameter = 'cd33';
const String kSessionHostOsDetails = 'cd1';
const String kSessionChannelName = 'cd2';
......@@ -59,7 +63,7 @@ const String reloadExceptionEmulator = 'cd29';
const String reloadExceptionFullRestart = 'cd30';
const String enabledFlutterFeatures = 'cd32';
// Next ID: cd33
// Next ID: cd34
Usage get flutterUsage => Usage.instance;
......@@ -141,12 +145,16 @@ class Usage {
String get clientId => _analytics.clientId;
void sendCommand(String command, { Map<String, String> parameters }) {
if (suppressAnalytics)
if (suppressAnalytics) {
return;
}
parameters ??= const <String, String>{};
final Map<String, String> paramsWithLocalTime = <String, String>{
...?parameters,
_kLocalTimeParameter: systemClock.now().toString(),
};
_analytics.sendScreenView(command, parameters: parameters);
_analytics.sendScreenView(command, parameters: paramsWithLocalTime);
}
void sendEvent(
......@@ -154,12 +162,16 @@ class Usage {
String parameter, {
Map<String, String> parameters,
}) {
if (suppressAnalytics)
if (suppressAnalytics) {
return;
}
parameters ??= const <String, String>{};
final Map<String, String> paramsWithLocalTime = <String, String>{
...?parameters,
_kLocalTimeParameter: systemClock.now().toString(),
};
_analytics.sendEvent(category, parameter, parameters: parameters);
_analytics.sendEvent(category, parameter, parameters: paramsWithLocalTime);
}
void sendTiming(
......@@ -168,7 +180,9 @@ class Usage {
Duration duration, {
String label,
}) {
if (!suppressAnalytics) {
if (suppressAnalytics) {
return;
}
_analytics.sendTiming(
variableName,
duration.inMilliseconds,
......@@ -176,10 +190,11 @@ class Usage {
label: label,
);
}
}
void sendException(dynamic exception) {
if (!suppressAnalytics)
if (suppressAnalytics) {
return;
}
_analytics.sendException(exception.runtimeType.toString());
}
......@@ -199,8 +214,9 @@ class Usage {
void printWelcome() {
// This gets called if it's the first run by the selected command, if any,
// and on exit, in case there was no command.
if (_printedWelcome)
if (_printedWelcome) {
return;
}
_printedWelcome = true;
printStatus('');
......@@ -239,7 +255,17 @@ class LogToFileAnalytics extends AnalyticsMock {
Future<void> sendScreenView(String viewName, {Map<String, String> parameters}) {
parameters ??= <String, String>{};
parameters['viewName'] = viewName;
logFile.writeAsStringSync('screenView $parameters\n');
logFile.writeAsStringSync('screenView $parameters\n', mode: FileMode.append);
return Future<void>.value(null);
}
@override
Future<void> sendEvent(String category, String action,
{String label, int value, Map<String, String> parameters}) {
parameters ??= <String, String>{};
parameters['category'] = category;
parameters['action'] = action;
logFile.writeAsStringSync('event $parameters\n', mode: FileMode.append);
return Future<void>.value(null);
}
}
......@@ -3,23 +3,26 @@
// found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/commands/config.dart';
import 'package:flutter_tools/src/commands/doctor.dart';
import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/reporting/usage.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/mocks.dart';
void main() {
group('analytics', () {
......@@ -123,12 +126,16 @@ void main() {
});
group('analytics with mocks', () {
MemoryFileSystem memoryFileSystem;
MockStdio mockStdio;
Usage mockUsage;
SystemClock mockClock;
Doctor mockDoctor;
List<int> mockTimes;
setUp(() {
memoryFileSystem = MemoryFileSystem();
mockStdio = MockStdio();
mockUsage = MockUsage();
when(mockUsage.isFirstRun).thenReturn(false);
mockClock = MockClock();
......@@ -190,6 +197,56 @@ void main() {
}, overrides: <Type, Generator>{
Usage: () => mockUsage,
});
testUsingContext('command sends localtime', () async {
const int kMillis = 1000;
mockTimes = <int>[kMillis];
// Since FLUTTER_ANALYTICS_LOG_FILE is set in the environment, analytics
// will be written to a file.
final Usage usage = Usage(versionOverride: 'test');
usage.suppressAnalytics = false;
usage.enabled = true;
usage.sendCommand('test');
final String log = fs.file('analytics.log').readAsStringSync();
final DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(kMillis);
expect(log.contains(dateTime.toString()), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
SystemClock: () => mockClock,
Platform: () => FakePlatform(
environment: <String, String>{
'FLUTTER_ANALYTICS_LOG_FILE': 'analytics.log',
},
),
Stdio: () => mockStdio,
});
testUsingContext('event sends localtime', () async {
const int kMillis = 1000;
mockTimes = <int>[kMillis];
// Since FLUTTER_ANALYTICS_LOG_FILE is set in the environment, analytics
// will be written to a file.
final Usage usage = Usage(versionOverride: 'test');
usage.suppressAnalytics = false;
usage.enabled = true;
usage.sendEvent('test', 'test');
final String log = fs.file('analytics.log').readAsStringSync();
final DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(kMillis);
expect(log.contains(dateTime.toString()), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
SystemClock: () => mockClock,
Platform: () => FakePlatform(
environment: <String, String>{
'FLUTTER_ANALYTICS_LOG_FILE': 'analytics.log',
},
),
Stdio: () => mockStdio,
});
});
group('analytics bots', () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment