forcedotcom / ApexUnit

ApexUnit is a powerful continuous integration tool for the Force.com platform
BSD 3-Clause "New" or "Revised" License
71 stars 51 forks source link

Processing of regex with namespace is not working right. #30

Open tfuda opened 8 years ago

tfuda commented 8 years ago

I'm seeing that the use of a regexes that looks like NAMESPACE.CLASSNAME is not working in a namespaced org. I have an org with the namespace TomPackage. I define a test class regex that looks like this "TomPackage.fflib_*Test". When I debug the code, I see this get converted into the following SOQL query:

SELECT Id , Name FROM ApexClass WHERE NamespacePrefix =null AND Name like 'TomPackage.fflib_%Test'

I debugged this and determined that ApexClassFetcherUtils.fetchApexClassesBasedOnRegex is not splitting the passed in regex on the . (period) character. It is simply passing a value of null as the namespace parameter to the QueryConstructor.generateQueryToFetchApexClassesBasedOnRegex method. Here is the offending code from ApexClassFetcherUtils:

    /*
     * Utility method This method returns a string array of apex test class id's
     * fetched based on regex pattern given as a command line input
     * 
     * @param connection - partnerConnection for the app to connect to the org
     * using web services
     * 
     * @param classesAsArray - Apex Class Ids passed by the calling method. The
     * returned string array of classes includes classes from this array as well
     * 
     * @param regexes - regex that is used to fetch classes from the org
     */
    private static String[] fetchApexClassesBasedOnRegex(PartnerConnection connection, String[] classesAsArray,
            String regex, Boolean includeTriggers) {
        if (regex != null && !regex.equals(" ")) {
            LOG.info("Using regex: \"" + regex + "\" to fetch apex classes");
            // construct the query
            String namespace = null;
            String soql = QueryConstructor.generateQueryToFetchApexClassesBasedOnRegex(namespace, regex);
            // fire the query using WSC and fetch the results
            String[] classesAsArrayUsingWSC = constructClassIdArrayUsingWSC(connection, soql);
            // if both manifest file and testClass regex expression is provided
            // as command line option, combine the results

            Set<String> uniqueSetOfClasses = new HashSet<String>();
            ArrayList<String> duplicateList = new ArrayList<String>();
            // eliminate duplicates from the given class Ids
            // (just in case duplicates still exist in the class array passed
            // from the calling method)
            if (classesAsArray != null && classesAsArray.length > 0) {
                for (int i = 0; i < classesAsArray.length; i++) {
                    if (!uniqueSetOfClasses.add(classesAsArray[i])) {
                        duplicateList.add(classesAsArray[i]);
                    }
                }
            }
            // eliminate duplicates from the classes fetched using the prefix
            if (classesAsArrayUsingWSC != null && classesAsArrayUsingWSC.length > 0) {
                for (int i = 0; i < classesAsArrayUsingWSC.length; i++) {
                    if (!uniqueSetOfClasses.add(classesAsArrayUsingWSC[i])) {
                        duplicateList.add(classesAsArrayUsingWSC[i]);
                    }
                }
            }

            //if include triggers, add triggers to duplicate list as Triggers cannot be tested on force.com
            if(includeTriggers){
                String soqlForTrigger = QueryConstructor.generateQueryToFetchApexTriggersBasedOnRegex(namespace, regex);
                String[] triggersAsArrayUsingWSC = constructClassIdArrayUsingWSC(connection, soqlForTrigger);

                if (triggersAsArrayUsingWSC != null && triggersAsArrayUsingWSC.length > 0) {
                    for (int i = 0; i < triggersAsArrayUsingWSC.length; i++) {
                        if (!uniqueSetOfClasses.add(triggersAsArrayUsingWSC[i])) {
                            duplicateList.add(triggersAsArrayUsingWSC[i]);
                        }
                    }
                }
            }

            // eliminate duplicates from the triggers fetched using the prefix

            String[] uniqueClassesAsArray = uniqueSetOfClasses.toArray(new String[uniqueSetOfClasses.size()]);

            // log the duplicate classes/triggers found by querying the org with
            // the given regex
            if (duplicateList != null && !duplicateList.isEmpty()) {
                String logDuplicates = "Found duplicates from the classes fetched from the regex: " + regex
                        + ". Skipping multiple execution/code coverage computation of these test class/source class(es) :";
                for (int i = 0; i < duplicateList.size(); i++) {
                    duplicateApexClassMap.put(duplicateList.get(i), apexClassMap.get(duplicateList.get(i)));
                    logDuplicates += " " + apexClassMap.get(duplicateList.get(i)) + ",";
                }
                LOG.info(logDuplicates);
            }

            return uniqueClassesAsArray;
        }
        return classesAsArray;
    }