HostResolver logic is currently bypassed for testing (mocking or remapping) at 2 different levels of the stack using mostly inconsistent logic:
1) HostResolverProc (the interface for the proc or system resolver) can have an override injected and used for all host resolutions in HostResolver using the static HostResolverProc::SetDefault() method. This is only ever done via ScopedDefaultHostResolverProc which sets a new default upon creation and reverts on destruction. Comments on ScopedDefaultHostResolverProc state that it should only be used for catch-all safety nets, otherwise tests should use MockHostResolver, but that advice seems to have been ignored for years, as this is the primary mechanism of controlling host resolution in browser tests. When a default proc is set, HostResolverImpl will always use the system resolver rather than the built-in resolver, which means the test injection will almost always be used, but this adds extra test-only behavior in the actual HostResolver implementation and it gets bypassed when the built-in resolver is used directly rather than going through HostResolver.
The most common test override of HostResolverProc is RuleBasedHostResolverProc. It allows specifying rules with a hostname pattern and replacement string. Depending on the rule, the replacement string can either be the direct IPAddress result of all requests matching the rule, or it can act as a remap to a different hostname before delegating on to the actual system resolver. Due to a slightly confusing interface, most code adds calls the method to add remap rules, even when the clear intent is to set an IPAddress result, but fortunately RuleBasedHostResolverProc includes code to "fix" the rules to the IPAddress style when the given replacement hostname is an IPAddress.
RuleBasedHostResolverProc is widely used in browser tests, where BrowserTestBase uses TestHostResolver which uses ScopedDefaultHostResolverProc (counter to documented purposes) to create and inject a RuleBasedHostResolverProc that on no matching rule falls back to a HostResolverProc override that returns failure for any non-local resolutions and for local resolutions falls back to the actual system resolver. Setting rules is available through host_resolver()->AddRule() and similar.
HostResolverProc includes explicit support for chaining where each implementation can delegate to another when it doesn't support a given request. Other than the TestHostResolver case and a couple cases where multiple RuleBasedHostResolverProc are chained together, apparently to control rule ordering, the chaining functionality doesn't seem to ever be used, so this is mostly a wasted complication. When out of implementations in the chain, the actual system resolver is always used as a fallback, but since most usage of all this is for mocking, use of the actual system resolver is likely a mistake more often than desired.
2) The entire HostResolver interface can be overridden. Unlike HostResolverProc, there's no standard system for tests to inject their override for all the standard creations. The most common overrides are MockHostResolver and MappedHostResolver.
MockHostResolver is a test-only implementation that mirrors but simplifies the standard implementation in HostResolverImpl. It includes some logic for controlling timing of responses, but the meat of the implementation is a RuleBasedHostResolverProc. Like any other usage of RuleBasedHostResolverProc, this allows some very non-"mock" usage where requests could end up being resolved by the actual system resolver (with or without remapping), and such non-"mock" usage is likely accidental more often than it is desired.
MappedHostResolver is a simple implementation that applies hostname remapping rules before delegating to another HostResolver. It is used even in non-test browser code. IOThread::CreateGlobalHostResolver() (the standard HostResolver creation for the browser and network service) checks flags and if a special flag is set to specify remapping rules, the main HostResolver is created wrapped in a MappedHostResolver to apply those rules.
Lots of overcomplication, easy misuse, and overlapping solutions. My proposed cleanup:
A) Standardize on adding test code at the HostResolver level (MockHostResolver and MappedHostResolver), not HostResolverProc. Only tests of the host resolution code itself should be bypassing at deeper levels. This will greatly simplify and consistentize things. As the test overrides will completely replace HostResolver, we’ll be able to remove a lot of the test-only complications within HostResolver, simplifying and clarifying the actual non-test logic. Also, this will ensure the test logic is used for all host resolutions without relying on test-only logic to force HostResolverProc to be used in cases where it normally would not be.
B) Make MockHostResolver only for “mock” usage. Remove the ability to do mapping (because that should use MappedHostResolver) and remove all the fallbacks to the actual system resolver (to avoid mistakenly making real DNS calls in tests). If we really need to fallback to the actual resolver in some cases, maybe add the ability to explicitly set a rule where some hostname pattern will be delegated (without remapping) to another HostResolver implementation. Then it will be much more difficult to happen by mistake and the delegation will be at the HostResolver level rather than artificially jumping straight to the system resolver. Consider making most recent mock rules take precedence to counter the need for MockHostResolvers to fallback to other MockHostResolvers (as is sometimes done with RuleBasedHostResolverProc).
C) Add a way to inject HostResolver replacements for all standard usage within the browser, similar to the mechanism used today for replacing HostResolverProc. This will allow easy use within browser tests. Because of how we have at least 2 specialized implementations (MockHostResolver and MappedHostResolver) rather than 1 do-everything implementation, this will have to easily specify a full implementation object to use rather than just accessing one to set rules (but accessing a default one, probably MockHostResolver, is fine too).