void validateResourcesGranularityAndProportion(Function.FunctionDetails functionDetails) {
        final long baseMillis = 1000;
        long multiples = 0L;
        if (functionInstanceResourceGranularities != null) {
            Double grnCpu = functionInstanceResourceGranularities.getCpu();
            Long grnRam = functionInstanceResourceGranularities.getRam();
            if (grnCpu != null) {
                // convert cpus to milli-cores to avoid loss of precision
                long grnCpuMillis = Math.round(baseMillis * grnCpu);
                if (grnCpuMillis > 0) {
                    long cpuMillis = Math.round(baseMillis * functionDetails.getResources().getCpu());
                    if (cpuMillis == 0 || cpuMillis % grnCpuMillis != 0) {
                        throw new IllegalArgumentException(
                                String.format("Per instance cpu requested, %s, for function should be positive and a "
                                                + "multiple of the granularity, %s",
                                        functionDetails.getResources().getCpu(), grnCpu));
                    }
                    if (functionInstanceResourceChangeInLockStep) {
                        multiples = cpuMillis / grnCpuMillis;
                    }
                }
            }
            if (grnRam != null && grnRam > 0) {
                if (functionDetails.getResources().getRam() == 0
                        || functionDetails.getResources().getRam() % grnRam != 0) {
                    throw new IllegalArgumentException(
                            String.format("Per instance ram requested, %s, "
                                            + "for function should be positive and a multiple of the granularity, %s",
                                    functionDetails.getResources().getRam(), grnRam));
                }
                if (functionInstanceResourceChangeInLockStep && multiples > 0) {
                    long ramMultiples = functionDetails.getResources().getRam() / grnRam;
                    if (multiples != ramMultiples) {
                        throw new IllegalArgumentException(
                                String.format("Per instance cpu requested, %s, ram requested, %s,"
                                                + " for function should be positive and the same multiple of the "
                                                + "granularity, cpu, %s, ram, %s",
                                        functionDetails.getResources().getCpu(),
                                        functionDetails.getResources().getRam(), grnCpu, grnRam));
                    }
                }
            }
        }
    }
@Test
    public void testValidateResourcesGranularityAndProportion() throws Exception {
        factory = createKubernetesRuntimeFactory(null, null, null, null, false);

        Resources granularities = Resources.builder()
                .cpu(0.1)
                .ram(1000L)
                .build();

        // when resources granularities are not set
        testResourceGranularities(null, null, null, false, false, null);
        testResourceGranularities(0.05, 100L, null, false, false, null);

        // only accept positive resource values when granularities are set
        testResourceGranularities(null, null, granularities, false, true,
                "Per instance cpu requested, 0.0, for function should be positive and a multiple of the granularity, 0.1");
        testResourceGranularities(0.1, null, granularities, false, true,
                "Per instance ram requested, 0, for function should be positive and a multiple of the granularity, 1000");
        testResourceGranularities(0.1, 0L, granularities, false, true,
                "Per instance ram requested, 0, for function should be positive and a multiple of the granularity, 1000");
        testResourceGranularities(null, 1000L, granularities, false, true,
                "Per instance cpu requested, 0.0, for function should be positive and a multiple of the granularity, 0.1");
        testResourceGranularities(0.0, 1000L, granularities, false, true,
                "Per instance cpu requested, 0.0, for function should be positive and a multiple of the granularity, 0.1");

        // requested resources must be multiples of granularities
        testResourceGranularities(0.05, 100L, granularities, false, true,
                "Per instance cpu requested, 0.05, for function should be positive and a multiple of the granularity, 0.1");
        testResourceGranularities(0.1, 100L, granularities, false, true,
                "Per instance ram requested, 100, for function should be positive and a multiple of the granularity, 1000");
        testResourceGranularities(1.01, 100L, granularities, false, true,
                "Per instance cpu requested, 1.01, for function should be positive and a multiple of the granularity, 0.1");
        testResourceGranularities(0.999, 100L, granularities, false, true,
                "Per instance cpu requested, 0.999, for function should be positive and a multiple of the granularity, 0.1");
        testResourceGranularities(1.001, 100L, granularities, false, true,
                "Per instance cpu requested, 1.001, for function should be positive and a multiple of the granularity, 0.1");
        testResourceGranularities(0.1, 1000L, granularities, false, false, null);
        testResourceGranularities(1.0, 1000L, granularities, false, false, null);
        testResourceGranularities(5.0, 1000L, granularities, false, false, null);

        // resource values of different dimensions should respect lock step configs
        testResourceGranularities(0.2, 1000L, granularities, false, false, null);
        testResourceGranularities(0.1, 2000L, granularities, false, false, null);
        testResourceGranularities(0.1, 2000L, granularities, true, true,
                "Per instance cpu requested, 0.1, ram requested, 2000, for function should be positive and the same multiple of the granularity, cpu, 0.1, ram, 1000");
        testResourceGranularities(0.2, 1000L, granularities, true, true,
                "Per instance cpu requested, 0.2, ram requested, 1000, for function should be positive and the same multiple of the granularity, cpu, 0.1, ram, 1000");
        testResourceGranularities(0.1, 1000L, granularities, true, false, null);
        testResourceGranularities(0.2, 2000L, granularities, true, false, null);
        testResourceGranularities(1.0, 10000L, granularities, true, false, null);
        testResourceGranularities(10.0, 100000L, granularities, true, false, null);
        testResourceGranularities(10.0, null, granularities, true, true,
                "Per instance ram requested, 0, for function should be positive and a multiple of the granularity, 1000");
    }