Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/devtools_app/lib/src/shared/managers/survey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ class SurveyService {
final contents = json.decode(response.body) as Map<String, Object?>;
return DevToolsSurvey.fromJson(contents);
}
} on ClientException catch (e, st) {
_log.fine('Unable to fetch survey content: $e', e, st);
} on Error catch (e, st) {
_log.shout('Error fetching survey content: $e', e, st);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ To learn more about DevTools, check out the

## General updates

* Handled network failures when fetching survey metadata without an unhandled
exception. [#8562](https://github.com/flutter/devtools/issues/8562)

* Added guidance to run "flutter attach" when a Flutter debug app cannot
evaluate expressions and a debug-only screen is unavailable.
[#4091](https://github.com/flutter/devtools/issues/4091)
Expand Down
62 changes: 60 additions & 2 deletions packages/devtools_app/test/shared/managers/survey_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,74 @@ import 'package:devtools_app/devtools_app.dart';
import 'package:devtools_app/src/shared/managers/survey.dart';
import 'package:devtools_app/src/shared/primitives/query_parameters.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';

void main() {
setUp(() {
fakeClockTimeForSurvey = null;
});

group('SurveyService', () {
Future<DevToolsSurvey?> fetchWithClient(MockClient client) =>
http.runWithClient(
() => SurveyService().fetchSurveyContent(),
() => client,
);

test('can fetch survey metadata', () async {
final survey = await SurveyService().fetchSurveyContent();
expect(survey, isNotNull);
final survey = await fetchWithClient(
MockClient(
(_) async => http.Response('{"uniqueId":"test-survey"}', 200),
),
);
expect(survey!.id, 'test-survey');
});

test('returns no survey for an unsuccessful response', () async {
final survey = await fetchWithClient(
MockClient((_) async => http.Response('Unavailable', 503)),
);
expect(survey, isNull);
});

test('returns no survey when the network request fails', () async {
final survey = await fetchWithClient(
MockClient(
(request) => Future<http.Response>.error(
http.ClientException('Network unavailable', request.url),
),
),
);
expect(survey, isNull);
});

test('can fetch a survey after a network failure', () async {
var requests = 0;
final client = MockClient((request) async {
if (requests++ == 0) {
throw http.ClientException('Network unavailable', request.url);
}
return http.Response('{"uniqueId":"test-survey"}', 200);
});

expect(await fetchWithClient(client), isNull);
expect((await fetchWithClient(client))!.id, 'test-survey');
expect(requests, 2);
});

test('preserves error handling for invalid metadata types', () async {
final survey = await fetchWithClient(
MockClient((_) async => http.Response('{"uniqueId":42}', 200)),
);
expect(survey, isNull);
});

test('does not suppress malformed JSON exceptions', () async {
await expectLater(
fetchWithClient(MockClient((_) async => http.Response('{', 200))),
throwsFormatException,
);
});
});

Expand Down
Loading