Skip to content
Advertisement

Comparing the contents of two Treemaps

I have two TreeMaps that I want to compare. I currently have it written down like below but I feel like this could be written more efficiently. I tried looking in to comparators, but I don’t think that’s something I can use for my use-case.

The maps are Treemaps because the key must be case-insensitive.

public void theseRulesAreTheSame() {
    List<String> failures = new ArrayList<>();

    TreeMap<String, NSG> configNsgs = platformConfiguration.getAzure().nsgs();
    configNsgs.forEach((name, nsg) -> {
      assertThat(azureAdapter.doesNsgExistInAzure(name))
          .as("Unable to find network security group " + name + " in Azure.").isTrue();

      List<SecurityRulesItem> configSecurityRules = nsg.getSecurityRules();
      TreeMap<String, Object> azureSecurityRules = azureAdapter
          .getSecurityRulesForNsg(name);

      assertThat(configSecurityRules.size())
          .as("The nymber of security rules in Azure does not correspond to the number of security rules in the configuration!")
          .isEqualTo(azureSecurityRules.size());

      configSecurityRules.forEach(configSecurityRule -> {
        SecurityRuleInner azureSecurityRule = (SecurityRuleInner) azureSecurityRules
            .get(configSecurityRule.getRuleName());

        logger.info(
            "Checking security rule " + configSecurityRule.getRuleName()
                + " in network security group "
                + nsg.getName());

        if (null == azureSecurityRule) {
            logFailure(failures, null, configSecurityRule.getRuleName());
        } else {
          if (!azureSecurityRule.access().toString().equalsIgnoreCase(configSecurityRule.getAccess())) {
            logFailure(failures, configSecurityRule.getAccess(), azureSecurityRule.access());
          }
          if (!azureSecurityRule.destinationAddressPrefix().equalsIgnoreCase(configSecurityRule.getDestinationAddressPrefix())) {
            logFailure(failures, configSecurityRule.getDestinationAddressPrefix(), azureSecurityRule.destinationAddressPrefix());
          }
          if (!azureSecurityRule.destinationPortRange().equalsIgnoreCase(configSecurityRule.getDestinationPortRange())) {
            logFailure(failures, configSecurityRule.getDestinationPortRange(), azureSecurityRule.destinationPortRange());
          }
          if (!azureSecurityRule.sourceAddressPrefix().equalsIgnoreCase(configSecurityRule.getSourceAddressPrefix())) {
            logFailure(failures, configSecurityRule.getSourceAddressPrefix(), azureSecurityRule.sourceAddressPrefix());
          }
          if (!azureSecurityRule.sourcePortRange().equalsIgnoreCase(configSecurityRule.getSourcePortRange())) {
            logFailure(failures, configSecurityRule.getSourcePortRange(), azureSecurityRule.sourcePortRange());
          }
          if (!azureSecurityRule.protocol().toString().equalsIgnoreCase(configSecurityRule.getProtocol())) {
            logFailure(failures, configSecurityRule.getProtocol(), azureSecurityRule.protocol());
          }
          if (!azureSecurityRule.direction().toString().equalsIgnoreCase(configSecurityRule.getDirection())) {
            logFailure(failures, configSecurityRule.getDirection(), azureSecurityRule.direction());
          }
        }
      });
    });
    if (!failures.isEmpty()) {
      Assertions.fail(
          "Error(s) detected while comparing the network security groups between Azure and the config. Failures: "
              + failures);
    }
  }

Thanks in advance

Advertisement

Answer

If we have the two types AzureSecurityRule and ConfigSecurityRule we could make the comparison less verbose like this:

 BiConsumer<AzureSecurityRule, ConfigSecurityRule>  compareField(Function<AzureSecurityRule,String> f1, Function<ConfigSecurityRule> f2) {
return (az, cf) -> {
    if !f1.apply(az).equalsIgnoreCase(f2.apply(cf)) {
      logFailure(failure, f2.apply(cf), f1.apply(az));
    }
  }
}
...
List.of(
  compareField(az -> az.access().toString(), cf -> cf.getAccess()),
  compareField(az -> az.destinationAddressPrefix(), cf -> cf.getDestinationAddressPrefix()),
  ...
).forEach(cf -> cf.accept(azureSecurityRule, configSecurityRule));
Advertisement