NAVAND-775: support waypoints per route#6555
Conversation
d47869e to
9dce7a8
Compare
|
Adding the DO NOT MERGE label as it requires bumping NN to 119.0.2. |
30dfbf8 to
3b903b3
Compare
Codecov Report
@@ Coverage Diff @@
## main #6555 +/- ##
============================================
+ Coverage 72.38% 72.40% +0.02%
- Complexity 5392 5399 +7
============================================
Files 764 764
Lines 29372 29377 +5
Branches 3477 3480 +3
============================================
+ Hits 21260 21270 +10
+ Misses 6719 6718 -1
+ Partials 1393 1389 -4
|
3b903b3 to
c65370e
Compare
| fun ev_route_with_waypoints_in_response_root_by_default() = sdkTest { | ||
| addResponseHandler(R.raw.ev_route_response_with_waypoints_in_root, evCoordinates) | ||
| val routes = requestRoutes(evCoordinates, electric = true, waypointsPerRoute = null) | ||
|
|
||
| checkWaypointsInRoot(expectedEvWaypointsNamesAndLocations, routes[0]) | ||
| } | ||
|
|
||
| @Test | ||
| fun ev_route_with_waypoints_in_response_root() = sdkTest { | ||
| addResponseHandler(R.raw.ev_route_response_with_waypoints_in_root, evCoordinates) | ||
| val routes = requestRoutes(evCoordinates, electric = true, waypointsPerRoute = false) | ||
|
|
||
| checkWaypointsInRoot(expectedEvWaypointsNamesAndLocations, routes[0]) | ||
| } | ||
|
|
||
| @Test | ||
| fun ev_route_with_waypoints_per_route() = sdkTest { | ||
| addResponseHandler(R.raw.ev_route_response_with_waypoints_per_route, evCoordinates) | ||
| val routes = requestRoutes(evCoordinates, electric = true, waypointsPerRoute = true) | ||
|
|
||
| checkWaypointsPerRoute(expectedEvWaypointsNamesAndLocations, routes[0]) | ||
| } | ||
|
|
||
| @Test | ||
| fun non_ev_route_with_waypoints_in_response_root_by_default() = sdkTest { | ||
| addResponseHandler(R.raw.route_response_with_waypoints_in_root, nonEvCoordinates) | ||
| val routes = requestRoutes(nonEvCoordinates, electric = false, waypointsPerRoute = null) | ||
|
|
||
| checkWaypointsInRoot(expectedFirstNonEvWaypointsNamesAndLocations, routes[0]) | ||
| checkWaypointsInRoot(expectedFirstNonEvWaypointsNamesAndLocations, routes[1]) | ||
| } | ||
|
|
||
| @Test | ||
| fun non_ev_route_with_waypoints_in_response_root() = sdkTest { | ||
| addResponseHandler(R.raw.route_response_with_waypoints_in_root, nonEvCoordinates) | ||
| val routes = requestRoutes(nonEvCoordinates, electric = false, waypointsPerRoute = false) | ||
|
|
||
| checkWaypointsInRoot(expectedFirstNonEvWaypointsNamesAndLocations, routes[0]) | ||
| checkWaypointsInRoot(expectedFirstNonEvWaypointsNamesAndLocations, routes[1]) | ||
| } | ||
|
|
||
| @Test | ||
| fun non_ev_route_with_waypoints_per_route() = sdkTest { | ||
| addResponseHandler(R.raw.route_response_with_waypoints_per_route, nonEvCoordinates) | ||
| val routes = requestRoutes(nonEvCoordinates, electric = false, waypointsPerRoute = true) | ||
|
|
||
| checkWaypointsPerRoute(expectedFirstNonEvWaypointsNamesAndLocations, routes[0]) | ||
| checkWaypointsPerRoute(expectedSecondNonEvWaypointsNamesAndLocations, routes[1]) | ||
| } |
There was a problem hiding this comment.
They seem like unit tests which are executed on device. I understand why it's done like this, we can't access NN in unit tests. I added NAVSDK-726 to address absence of NN in unit tests later.
There was a problem hiding this comment.
No, not because of that. It's not a unit test. It's an integration test: we request routes and parse the response.
We test the significant part of the SDK, not some specific class.
We already have such tests: see NavigationRouteTest.
And we don't have access to NN classes here either: everything that's checked here is represented by the SDK classes.
There was a problem hiding this comment.
It seems that NavigationRoute#internalWaypoints is calculated using NN, isn't it?
There was a problem hiding this comment.
Yes and they are checked here as well. What are you saying?
9131ebd to
feb2153
Compare
|
Supported in NN and checked. Removing DO NOT MERGE. |
feb2153 to
bf30de9
Compare
| override fun equals(other: Any?): Boolean { | ||
| if (this === other) return true | ||
| if (javaClass != other?.javaClass) return false | ||
|
|
||
| other as ApproximateCoordinates | ||
|
|
||
| if (abs(latitude - other.latitude) > tolerance) return false | ||
| if (abs(longitude - other.longitude) > tolerance) return false | ||
|
|
||
| return true | ||
| } | ||
|
|
||
| override fun hashCode(): Int { | ||
| var result = latitude.hashCode() | ||
| result = 31 * result + longitude.hashCode() | ||
| return result | ||
| } |
There was a problem hiding this comment.
as I understand two approximate coordinates could be equals, but their hash codes will be different. In this case data structures like hash tables won't be able to find equal objects. Not sure if it's important problem, but this could be unexpected
There was a problem hiding this comment.
Hmmm, I think you're right. It's not a problem now because it's used from assertEquals.
But I don't see the point of using ApproximateCoordinates as a key in hash table. Because for 2 different coordinates you'll get the same key... However, I think it would make sense to just return 0 here to avoid this unexpectedness.
bf30de9 to
31898b1
Compare
| } | ||
|
|
||
| private fun DirectionsResponse.Builder.updateWaypoints( | ||
| private fun buildNewWaypoints( |
There was a problem hiding this comment.
I should have asked this before, but I noticed this functionality only now.
What kind of update in waypoints do we expect? We don't expect to receive waypoints with different coordinates, do we?
There was a problem hiding this comment.
We don't. Only the metadata will be updated for now. Do you think we should update only unrecognizedProperties? Maybe it makes sense: we have a white-list of what we update.
There was a problem hiding this comment.
yep, reading the code, it's not obvious what's expected to change and what's not. It would be nice to explicitly update fields which we expect to be changed or explicitly save the ones we don't expect to be updated.
| route1 = NavigationRoute( | ||
| directionsResponse1, | ||
| 0, | ||
| routeOptions1, | ||
| mockk(relaxUnitFun = true) { | ||
| every { routeId } returns id1 | ||
| every { responseUuid } returns "uuid#0" | ||
| every { routerOrigin } returns RouterOrigin.ONBOARD | ||
| every { routeInfo } returns RouteInfo(listOf(mockk(relaxed = true))) | ||
| } | ||
| ) |
There was a problem hiding this comment.
maybe?
| route1 = NavigationRoute( | |
| directionsResponse1, | |
| 0, | |
| routeOptions1, | |
| mockk(relaxUnitFun = true) { | |
| every { routeId } returns id1 | |
| every { responseUuid } returns "uuid#0" | |
| every { routerOrigin } returns RouterOrigin.ONBOARD | |
| every { routeInfo } returns RouteInfo(listOf(mockk(relaxed = true))) | |
| } | |
| ) | |
| route1 = createNavigationRoutes( | |
| response = directionsResponse1, | |
| options = routeOptions1 | |
| ).first() |
There was a problem hiding this comment.
I need to mock id. I can use spy but IMO switching to createNavigationRoutes gives no profit here.
There was a problem hiding this comment.
createNavigationRoutes generates in the same way as NN does, so it's very similar, if not identical, to how the real system works. is that enough for unit tests?
There was a problem hiding this comment.
How can I mock the id if I use createNavigationRoutes?
I only see they way with creating a spy.
My question is: why is it better than the current approach?
| // #0 | ||
| arrayOf(null, null, null, null), | ||
| arrayOf(false, null, null, null), | ||
| arrayOf(true, null, null, null), |
There was a problem hiding this comment.
How can it happen that a route doesn't have waypoints? Is that done for backward compatibility?
There was a problem hiding this comment.
That's the point of unit tests. Technically they can be null and the method that is being tested accounts for this. And to invoke this method we don't need to know the whole business logic of the SDK (that it can't IRL have null waypoints).
There was a problem hiding this comment.
I see your point.
that it can't IRL have null waypoints
should the API on NavigationRoute be nullable then?
There was a problem hiding this comment.
Technically they can be null. Because both directionsRoute.waypoints() and directionsResponse.waypoints() are nullable. Our backend claims one of them will be returned (depending on the waypoints_per_route parameter).
But what if this behaviour changes? And do we really trust backend that much? You mentioned yourself that it's better to introduce custom request timeout than rely on NN.
I can return an empty list instead of course. WDYT?
| arrayOf(null, filledWaypoints2, null, filledWaypoints2), | ||
| arrayOf(false, filledWaypoints2, null, filledWaypoints2), | ||
| arrayOf(true, filledWaypoints2, null, null), | ||
| // #21 |
There was a problem hiding this comment.
just curious, why do you need this numbers?
There was a problem hiding this comment.
In the test description the index is used as a description. This way it's easy to understand which case fails. Another way is adding a specific description and I like it, but I think that here it's not really necessary because it's quite clear from the input parameters and it would only kinda duplicate them. For more complex tests description is good though.
| val route = NavigationRoute( | ||
| DirectionsResponse.builder() | ||
| .routes( | ||
| listOf( | ||
| DirectionsRoute.builder() | ||
| .distance(1.0) | ||
| .duration(2.9) | ||
| .waypoints(routeWaypoints) | ||
| .build() | ||
| ) | ||
| ) | ||
| .waypoints(responseWaypoints) | ||
| .code("Ok") | ||
| .build(), | ||
| 0, | ||
| RouteOptions.builder() | ||
| .profile(DirectionsCriteria.PROFILE_DRIVING_TRAFFIC) | ||
| .coordinatesList( | ||
| listOf( | ||
| Point.fromLngLat(1.1, 2.2), | ||
| Point.fromLngLat(3.3, 4.4) | ||
| ) | ||
| ) | ||
| .waypointsPerRoute(waypointsPerRoute) | ||
| .build(), | ||
| mockk(relaxed = true) | ||
| ) |
There was a problem hiding this comment.
would com.mapbox.navigation.testing.factories.NavigationRouteFactoryKt#createNavigationRoutes simplify this setup?
| mapboxNavigation.setNavigationRoutesAndWaitForUpdate(requestedRoutes) | ||
| mapboxNavigation.startTripSession() | ||
| stayOnInitialPosition() | ||
| val updatedRoutes = waitUntilRefresh().navigationRoutes |
There was a problem hiding this comment.
we set route created with charge level 18000 to navigation which already has charge level 17000. Is that expected?
There was a problem hiding this comment.
Sure. The routes were requested when the charge was 18000. Then the charge dropped to 17000 and we told MapboxNavigation about it. Then we set the requested routes. I don't see anything invalid in this case. The latest data (17000) should be used in the refresh request.
afec69a to
7b23f59
Compare
e3d6434 to
b231e9f
Compare
b231e9f to
6f92122
Compare
|
I added missing docs and updated the |
1c1b226 to
c39c7f7
Compare
|
I've rebased on top of the latest |
c39c7f7 to
b3f5681
Compare
No description provided.