Skip to content

Commit 7324592

Browse files
chore: resolve conflict with remove deprecated apis branch, update CHANGELOG.md, fix some sync logic
1 parent f2c14a9 commit 7324592

File tree

11 files changed

+109
-870
lines changed

11 files changed

+109
-870
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
- **BREAKING** Remove deprecated APIs ([#614](https://github.com/Instabug/Instabug-Flutter/pull/614)). See migration guide for more details.
88
### Added
99

10-
- Add support for Advanced UI customization with comprehensive theming capabilities ([#599](https://github.com/Instabug/Instabug-Flutter/pull/599))
10+
- Add support for Advanced UI customization with comprehensive theming capabilities. ([#599](https://github.com/Instabug/Instabug-Flutter/pull/599))
11+
12+
- Add screen rendering monitoring functionality within the APM product. ([#605](https://github.com/Instabug/Instabug-Flutter/pull/605))
1113

1214

1315
## [15.0.2](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...15.0.2) (Jul 7, 2025)

android/src/test/java/com/instabug/flutter/ApmApiTest.java

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import static org.junit.Assert.assertEquals;
66
import static org.mockito.ArgumentMatchers.any;
77
import static org.mockito.ArgumentMatchers.anyInt;
8-
import static org.mockito.ArgumentMatchers.anyString;
98
import static org.mockito.ArgumentMatchers.eq;
109
import static org.mockito.Mockito.mock;
1110
import static org.mockito.Mockito.mockStatic;
@@ -16,6 +15,7 @@
1615
import com.instabug.apm.InternalAPM;
1716
import com.instabug.apm.configuration.cp.APMFeature;
1817
import com.instabug.apm.configuration.cp.FeatureAvailabilityCallback;
18+
import com.instabug.apm.configuration.cp.ToleranceValueCallback;
1919
import com.instabug.apm.networking.APMNetworkLogger;
2020
import com.instabug.flutter.generated.ApmPigeon;
2121
import com.instabug.flutter.modules.ApmApi;
@@ -24,20 +24,18 @@
2424

2525
import org.json.JSONObject;
2626
import org.junit.After;
27-
import org.junit.Assert;
2827
import org.junit.Before;
2928
import org.junit.Test;
3029
import org.mockito.MockedConstruction;
3130
import org.mockito.MockedStatic;
3231

32+
import java.lang.reflect.Array;
33+
import java.util.Arrays;
3334
import java.util.HashMap;
35+
import java.util.List;
3436
import java.util.Map;
3537
import java.util.concurrent.Callable;
3638

37-
import static com.instabug.flutter.util.GlobalMocks.reflected;
38-
import static com.instabug.flutter.util.MockResult.makeResult;
39-
import static org.junit.Assert.assertEquals;
40-
import static org.mockito.ArgumentMatchers.*;
4139
import static org.mockito.Mockito.*;
4240
import io.flutter.plugin.common.BinaryMessenger;
4341

@@ -361,38 +359,52 @@ public void testSetScreenRenderEnabled() {
361359
mAPM.verify(() -> APM.setScreenRenderingEnabled(isEnabled));
362360
}
363361

364-
@Test
365-
public void testDeviceRefreshRate() throws Exception {
366-
float expectedRefreshRate = 60.0f;
367-
Double expectedResult = 60.0;
368-
ApmPigeon.Result<Double> result = spy(makeResult((actual) -> assertEquals(expectedResult, actual)));
369-
370-
// Mock the refresh rate provider to return the expected value
371-
Callable<Float> mockRefreshRateProvider = () -> expectedRefreshRate;
372-
ApmApi testApi = new ApmApi(mockRefreshRateProvider);
373-
374-
testApi.deviceRefreshRate(result);
375-
376-
verify(result).success(expectedResult);
377-
}
378362

379363
@Test
380364
public void testDeviceRefreshRateWithException() throws Exception {
381-
ApmPigeon.Result<Double> result = spy(makeResult((actual) -> {}));
365+
ApmPigeon.Result<List<Double>> result = spy(makeResult((actual) -> {}));
382366

383367
// Mock the refresh rate provider to throw an exception
384368
Callable<Float> mockRefreshRateProvider = () -> {
385369
throw new RuntimeException("Test exception");
386370
};
387371
ApmApi testApi = new ApmApi(mockRefreshRateProvider);
388372

389-
testApi.deviceRefreshRate(result);
373+
testApi.getDeviceRefreshRateAndTolerance(result);
390374

391375
// Verify that the method doesn't crash when an exception occurs
392376
// The exception is caught and printed, but the result is not called
393377
verify(result, never()).success(any());
394378
}
395379

380+
@Test
381+
public void testGetDeviceRefreshRateAndTolerance() throws Exception {
382+
// Arrange
383+
double expectedRefreshRate = 60.0;
384+
long expectedTolerance = 5L;
385+
List<Double> expectedResult = Arrays.asList(expectedRefreshRate, (double) expectedTolerance);
386+
ApmPigeon.Result<List<Double>> result = spy(makeResult((actual) -> assertEquals(expectedResult, actual)));
387+
388+
// Mock the refresh rate provider
389+
Callable<Float> mockRefreshRateProvider = () -> (float) expectedRefreshRate;
390+
ApmApi testApi = new ApmApi(mockRefreshRateProvider);
391+
392+
// Mock the tolerance callback
393+
mInternalApmStatic.when(() -> InternalAPM._getToleranceValueForScreenRenderingCP(any(ToleranceValueCallback.class))).thenAnswer(invocation -> {
394+
ToleranceValueCallback callback = invocation.getArgument(0);
395+
callback.invoke(expectedTolerance);
396+
return null;
397+
});
398+
399+
// Act
400+
testApi.getDeviceRefreshRateAndTolerance(result);
401+
402+
// Assert
403+
verify(result).success(expectedResult);
404+
mInternalApmStatic.verify(() -> InternalAPM._getToleranceValueForScreenRenderingCP(any(ToleranceValueCallback.class)));
405+
mInternalApmStatic.verifyNoMoreInteractions();
406+
}
407+
396408
@Test
397409
public void testEndScreenRenderForAutoUiTrace() {
398410
Map<String, Object> data = new HashMap<>();

example/ios/InstabugTests/ApmApiTests.m

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -249,50 +249,41 @@ - (void)testSetScreenRenderDisabled {
249249
OCMVerify([self.mAPM setScreenRenderingEnabled:NO]);
250250
}
251251

252-
- (void)testDeviceRefreshRate {
252+
- (void)testGetDeviceRefreshRateAndTolerance {
253253
XCTestExpectation *expectation = [self expectationWithDescription:@"Call completion handler"];
254254

255-
// Mock UIScreen for iOS 10.3+
256-
id mockScreen = OCMClassMock([UIScreen class]);
257-
OCMStub([mockScreen mainScreen]).andReturn(mockScreen);
258-
OCMStub([mockScreen maximumFramesPerSecond]).andReturn(120.0);
255+
// Mock values
256+
double expectedTolerance = 5.0;
257+
double expectedRefreshRate = 60.0;
259258

260-
[self.api deviceRefreshRateWithCompletion:^(NSNumber *refreshRate, FlutterError *error) {
261-
[expectation fulfill];
262-
263-
XCTAssertEqualObjects(refreshRate, @(120.0));
264-
XCTAssertNil(error);
265-
}];
266-
267-
[self waitForExpectations:@[expectation] timeout:5.0];
259+
// Mock the tolerance value
260+
OCMStub([self.mAPM screenRenderingThreshold]).andReturn(expectedTolerance);
268261

269-
[mockScreen stopMocking];
270-
}
271-
272-
- (void)testDeviceRefreshRateFallback {
273-
XCTestExpectation *expectation = [self expectationWithDescription:@"Call completion handler"];
274-
275-
// Note: Testing the fallback behavior for iOS < 10.3 is challenging in unit tests
276-
// since we can't easily mock the iOS version check. In a real scenario, this would
277-
// return 60.0 for older iOS versions. For now, we'll test the normal case.
262+
// Mock UIScreen class methods
263+
id mockUIScreen = OCMClassMock([UIScreen class]);
264+
id mockMainScreen = OCMClassMock([UIScreen class]);
278265

279-
// Mock UIScreen to return 60.0 (typical fallback value)
280-
id mockScreen = OCMClassMock([UIScreen class]);
281-
OCMStub([mockScreen mainScreen]).andReturn(mockScreen);
282-
OCMStub([mockScreen maximumFramesPerSecond]).andReturn(60.0);
266+
// Stub the class method and instance property
267+
OCMStub([mockUIScreen mainScreen]).andReturn(mockMainScreen);
268+
OCMStub([mockMainScreen maximumFramesPerSecond]).andReturn(expectedRefreshRate);
283269

284-
[self.api deviceRefreshRateWithCompletion:^(NSNumber *refreshRate, FlutterError *error) {
270+
[self.api getDeviceRefreshRateAndToleranceWithCompletion:^(NSArray<NSNumber *> *result, FlutterError *error) {
285271
[expectation fulfill];
286272

287-
XCTAssertEqualObjects(refreshRate, @(60.0));
273+
XCTAssertNotNil(result);
274+
XCTAssertEqual(result.count, 2);
275+
XCTAssertEqualObjects(result[0], @(expectedRefreshRate));
276+
XCTAssertEqualObjects(result[1], @(expectedTolerance));
288277
XCTAssertNil(error);
289278
}];
290-
279+
291280
[self waitForExpectations:@[expectation] timeout:5.0];
292281

293-
[mockScreen stopMocking];
282+
[mockUIScreen stopMocking];
283+
[mockMainScreen stopMocking];
294284
}
295285

286+
296287
- (void)testEndScreenRenderForAutoUiTrace {
297288
FlutterError *error;
298289

example/lib/main.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ part 'src/components/non_fatal_crashes_content.dart';
2828
part 'src/components/page.dart';
2929
part 'src/components/screen_render.dart';
3030
part 'src/components/screen_render_switch.dart';
31-
part 'src/components/traces_content.dart';
3231
part 'src/components/ui_traces_content.dart';
3332
part 'src/screens/apm_page.dart';
3433
part 'src/screens/complex_page.dart';
@@ -38,8 +37,6 @@ part 'src/screens/screen_capture_premature_extension_page.dart';
3837
part 'src/screens/screen_loading_page.dart';
3938
part 'src/screens/screen_render_page.dart';
4039

41-
part 'src/components/flows_content.dart';
42-
4340
void main() {
4441
runZonedGuarded(
4542
() {

example/pubspec.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,10 @@ packages:
112112
dependency: "direct main"
113113
description:
114114
name: instabug_http_client
115-
sha256: "97a6ab88491eff87e42437564b528d6986a65eb3f3262f73373009f949cb4560"
115+
sha256: a38bed979f549ffe85efa46c46ca743cbfab95a51295b60f143f249b71655231
116116
url: "https://pub.dev"
117117
source: hosted
118-
version: "2.5.1"
118+
version: "2.6.0"
119119
leak_tracker:
120120
dependency: transitive
121121
description:

lib/src/modules/apm.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ class APM {
142142
(_) async {
143143
// Start screen render collector for custom ui trace if enabled.
144144
if (await FlagsConfig.screenRendering.isEnabled()) {
145-
InstabugScreenRenderManager.I.endScreenRenderCollector();
145+
InstabugScreenRenderManager.I
146+
.endScreenRenderCollector(UiTraceType.custom);
146147

147-
// final uiTraceId = IBGDateTime.I.now().millisecondsSinceEpoch;
148148
InstabugScreenRenderManager.I
149149
.startScreenRenderCollectorForTraceId(0, UiTraceType.custom);
150150
}
@@ -159,7 +159,8 @@ class APM {
159159
static Future<void> endUITrace() async {
160160
// End screen render collector for custom ui trace if enabled.
161161
if (InstabugScreenRenderManager.I.screenRenderEnabled) {
162-
return InstabugScreenRenderManager.I.endScreenRenderCollector();
162+
return InstabugScreenRenderManager.I
163+
.endScreenRenderCollector(UiTraceType.custom);
163164
}
164165

165166
return _host.endUITrace();

lib/src/utils/screen_rendering/instabug_screen_render_manager.dart

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ class InstabugScreenRenderManager {
125125
]) {
126126
try {
127127
// Return if frameTimingListener not attached
128-
if (!screenRenderEnabled || !_isTimingsListenerAttached) {
129-
return;
130-
}
128+
if (frameCollectorIsNotActive) return;
131129

132130
if (type == UiTraceType.custom) {
133131
_screenRenderForCustomUiTrace.traceId = traceId;
@@ -147,13 +145,12 @@ class InstabugScreenRenderManager {
147145
]) {
148146
try {
149147
// Return if frameTimingListener not attached
150-
if (!screenRenderEnabled || !_isTimingsListenerAttached) {
151-
return;
152-
}
148+
// log("frameCollectorIsNotActive $frameCollectorIsNotActive");
149+
if (frameCollectorIsNotActive) return;
153150

154151
//Save the memory cached data to be sent to native side
155152
if (_delayedFrames.isNotEmpty) {
156-
_saveCollectedData();
153+
saveCollectedData();
157154
_resetCachedFrameData();
158155
}
159156

@@ -178,24 +175,33 @@ class InstabugScreenRenderManager {
178175
@internal
179176
void stopScreenRenderCollector() {
180177
try {
178+
// Return if frameTimingListener not attached
179+
if (frameCollectorIsNotActive) return;
180+
181181
if (_delayedFrames.isNotEmpty) {
182-
_saveCollectedData();
182+
saveCollectedData();
183+
_resetCachedFrameData();
183184
}
184185

185186
// Sync Screen Render data for custom ui trace if exists
186187
if (_screenRenderForCustomUiTrace.isActive) {
187188
_reportScreenRenderForCustomUiTrace(_screenRenderForCustomUiTrace);
189+
_screenRenderForCustomUiTrace.clear();
188190
}
189191

190192
// Sync Screen Render data for auto ui trace if exists
191193
if (_screenRenderForAutoUiTrace.isActive) {
192194
_reportScreenRenderForAutoUiTrace(_screenRenderForAutoUiTrace);
195+
_screenRenderForAutoUiTrace.clear();
193196
}
194197
} catch (error, stackTrace) {
195198
_logExceptionErrorAndStackTrace(error, stackTrace);
196199
}
197200
}
198201

202+
bool get frameCollectorIsNotActive =>
203+
!screenRenderEnabled || !_isTimingsListenerAttached;
204+
199205
/// Dispose InstabugScreenRenderManager by removing timings callback and cashed data.
200206
void dispose() {
201207
_resetCachedFrameData();
@@ -326,6 +332,7 @@ class InstabugScreenRenderManager {
326332
InstabugScreenRenderData screenRenderData,
327333
) async {
328334
try {
335+
screenRenderData.saveEndTime();
329336
log(
330337
"reportScreenRenderForCustomUiTrace $screenRenderData",
331338
name: tag,
@@ -361,7 +368,8 @@ class InstabugScreenRenderManager {
361368
}
362369

363370
/// Add the memory cashed data to the objects that will be synced asynchronously to the native side.
364-
void _saveCollectedData() {
371+
@visibleForTesting
372+
void saveCollectedData() {
365373
if (_screenRenderForAutoUiTrace.isActive) {
366374
_updateAutoUiData();
367375
}

lib/src/utils/screen_rendering/instabug_widget_binding_observer.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class InstabugWidgetsBindingObserver extends WidgetsBindingObserver {
3636
.then((uiTraceId) {
3737
if (uiTraceId != null &&
3838
InstabugScreenRenderManager.I.screenRenderEnabled) {
39+
InstabugScreenRenderManager.I.endScreenRenderCollector();
3940
InstabugScreenRenderManager.I
4041
.startScreenRenderCollectorForTraceId(uiTraceId);
4142
}

test/apm_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ void main() {
310310
await APM.endUITrace();
311311

312312
verify(
313-
mScreenRenderManager.endScreenRenderCollector(),
313+
mScreenRenderManager.endScreenRenderCollector(UiTraceType.custom),
314314
).called(1);
315315
verifyNever(mHost.endUITrace());
316316
});

0 commit comments

Comments
 (0)