public void ContractChanges()
 {
     ContractEnforcement.ValidateContractContainBreakingChanges(
         dllName: DllName,
         baselinePath: OfficialBaselinePath,
         breakingChangesPath: "DotNetSDKAPIChanges.json");
 }
 public void PreviewContractChanges()
 {
     ContractEnforcement.ValidatePreviewContractContainBreakingChanges(
         dllName: DllName,
         officialBaselinePath: OfficialBaselinePath,
         previewBaselinePath: "DotNetPreviewSDKAPI.json",
         previewBreakingChangesPath: "DotNetPreviewSDKAPIChanges.json");
 }
예제 #3
0
        private static TypeTree BuildTypeTree(TypeTree root, Type[] types)
        {
            IEnumerable <Type> subclassTypes = types.Where((type) => type.IsSubclassOf(root.Type)).OrderBy(o => o.FullName, invariantComparer);

            foreach (Type subclassType in subclassTypes)
            {
                root.Subclasses[ContractEnforcement.GenerateNameWithClassAttributes(subclassType)] = ContractEnforcement.BuildTypeTree(new TypeTree(subclassType), types);
            }

            IEnumerable <KeyValuePair <string, MemberInfo> > memberInfos =
                root.Type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
                .Select(memberInfo => new KeyValuePair <string, MemberInfo>($"{memberInfo.ToString()}{string.Join("-", ContractEnforcement.RemoveDebugSpecificAttributes(memberInfo.CustomAttributes))}", memberInfo))
                .OrderBy(o => o.Key, invariantComparer);

            foreach (KeyValuePair <string, MemberInfo> memberInfo in memberInfos)
            {
                List <string> attributes = ContractEnforcement.RemoveDebugSpecificAttributes(memberInfo.Value.CustomAttributes)
                                           .Select((customAttribute) => customAttribute.AttributeType.Name)
                                           .ToList();
                attributes.Sort(invariantComparer);

                string methodSignature = null;

                if (memberInfo.Value.MemberType == MemberTypes.Method)
                {
                    MethodInfo methodInfo = (MethodInfo)memberInfo.Value;
                    methodSignature = ContractEnforcement.GenerateNameWithMethodAttributes(methodInfo);
                }
                else if (memberInfo.Value.MemberType == MemberTypes.Property)
                {
                    PropertyInfo propertyInfo = (PropertyInfo)memberInfo.Value;
                    methodSignature = ContractEnforcement.GenerateNameWithPropertyAttributes(propertyInfo);
                }
                else if (memberInfo.Value.MemberType == MemberTypes.Field)
                {
                    FieldInfo fieldInfo = (FieldInfo)memberInfo.Value;
                    methodSignature = ContractEnforcement.GenerateNameWithFieldAttributes(fieldInfo);
                }
                else if (memberInfo.Value.MemberType == MemberTypes.Constructor || memberInfo.Value.MemberType == MemberTypes.Event)
                {
                    methodSignature = memberInfo.ToString();
                }

                root.Members[
                    memberInfo.Key
                ] = new MemberMetadata(
                    memberInfo.Value.MemberType,
                    attributes,
                    methodSignature);
            }

            foreach (Type nestedType in root.Type.GetNestedTypes().OrderBy(o => o.FullName))
            {
                root.NestedTypes[ContractEnforcement.GenerateNameWithClassAttributes(nestedType)] = ContractEnforcement.BuildTypeTree(new TypeTree(nestedType), types);
            }

            return(root);
        }
예제 #4
0
        public static string GetCurrentContract(string dllName)
        {
            TypeTree locally  = new TypeTree(typeof(object));
            Assembly assembly = ContractEnforcement.GetAssemblyLocally(dllName);

            Type[] exportedTypes = assembly.GetExportedTypes();
            ContractEnforcement.BuildTypeTree(locally, exportedTypes);

            string localJson = JsonConvert.SerializeObject(locally, Formatting.Indented);

            return(localJson);
        }
예제 #5
0
        public static void ValidateContractContainBreakingChanges(
            string dllName,
            string baselinePath,
            string breakingChangesPath)
        {
            string localJson = GetCurrentContract(dllName);

            File.WriteAllText($"Contracts/{breakingChangesPath}", localJson);

            string baselineJson = GetBaselineContract(baselinePath);

            ContractEnforcement.ValidateJsonAreSame(localJson, baselineJson);
        }
예제 #6
0
        private static string GenerateNameWithPropertyAttributes(PropertyInfo propertyInfo)
        {
            string name = $"{propertyInfo};{nameof(propertyInfo.CanRead)}:{(propertyInfo.CanRead ? bool.TrueString : bool.FalseString)};" +
                          $"{nameof(propertyInfo.CanWrite)}:{(propertyInfo.CanWrite ? bool.TrueString : bool.FalseString)};";

            MethodInfo getMethodInfo = propertyInfo.GetGetMethod();

            if (getMethodInfo != null)
            {
                name += ContractEnforcement.GenerateNameWithMethodAttributes(getMethodInfo);
            }

            MethodInfo setMethodInfo = propertyInfo.GetSetMethod();

            if (setMethodInfo != null)
            {
                name += ContractEnforcement.GenerateNameWithMethodAttributes(setMethodInfo);
            }

            return(name);
        }
예제 #7
0
        public static void ValidatePreviewContractContainBreakingChanges(
            string dllName,
            string officialBaselinePath,
            string previewBaselinePath,
            string previewBreakingChangesPath)
        {
            string currentPreviewJson = ContractEnforcement.GetCurrentContract(
                dllName);

            JObject currentJObject          = JObject.Parse(currentPreviewJson);
            JObject officialBaselineJObject = JObject.Parse(File.ReadAllText("Contracts/" + officialBaselinePath));

            string currentJsonNoOfficialContract = ContractEnforcement.RemoveDuplicateContractElements(
                localContract: currentJObject,
                officialContract: officialBaselineJObject);

            Assert.IsNotNull(currentJsonNoOfficialContract);

            string baselinePreviewJson = ContractEnforcement.GetBaselineContract(previewBaselinePath);

            File.WriteAllText($"Contracts/{previewBreakingChangesPath}", currentJsonNoOfficialContract);

            ContractEnforcement.ValidateJsonAreSame(baselinePreviewJson, currentJsonNoOfficialContract);
        }
예제 #8
0
        private static TypeTree BuildTypeTree(TypeTree root, Type[] types)
        {
            IEnumerable <Type> subclassTypes = types.Where((type) => type.IsSubclassOf(root.Type)).OrderBy(o => o.FullName, invariantComparer);

            foreach (Type subclassType in subclassTypes)
            {
                root.Subclasses[ContractEnforcement.GenerateNameWithClassAttributes(subclassType)] = ContractEnforcement.BuildTypeTree(new TypeTree(subclassType), types);
            }

            IEnumerable <KeyValuePair <string, MemberInfo> > memberInfos =
                root.Type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
                .Select(memberInfo => new KeyValuePair <string, MemberInfo>($"{memberInfo}{string.Join("-", ContractEnforcement.RemoveDebugSpecificAttributes(memberInfo.CustomAttributes))}", memberInfo))
                .OrderBy(o => o.Key, invariantComparer);

            foreach (KeyValuePair <string, MemberInfo> memberInfo in memberInfos)
            {
                List <string> attributes = ContractEnforcement.RemoveDebugSpecificAttributes(memberInfo.Value.CustomAttributes)
                                           .Select((customAttribute) => customAttribute.AttributeType.Name)
                                           .ToList();
                attributes.Sort(invariantComparer);

                string methodSignature = null;

                if (memberInfo.Value.MemberType == MemberTypes.Method)
                {
                    MethodInfo methodInfo = (MethodInfo)memberInfo.Value;
                    methodSignature = ContractEnforcement.GenerateNameWithMethodAttributes(methodInfo);
                }
                else if (memberInfo.Value.MemberType == MemberTypes.Property)
                {
                    PropertyInfo propertyInfo = (PropertyInfo)memberInfo.Value;
                    methodSignature = ContractEnforcement.GenerateNameWithPropertyAttributes(propertyInfo);
                }
                else if (memberInfo.Value.MemberType == MemberTypes.Field)
                {
                    FieldInfo fieldInfo = (FieldInfo)memberInfo.Value;
                    methodSignature = ContractEnforcement.GenerateNameWithFieldAttributes(fieldInfo);
                }
                else if (memberInfo.Value.MemberType == MemberTypes.Constructor || memberInfo.Value.MemberType == MemberTypes.Event)
                {
                    methodSignature = memberInfo.ToString();
                }

                // Certain custom attributes add the following to the string value "d__9" which sometimes changes
                // based on the .NET SDK version it is being built on. This removes the value to avoid showing
                // breaking change when there is none.
                string key = Regex.Replace(memberInfo.Key, @"d__\d+", string.Empty);
                root.Members[
                    key
                ] = new MemberMetadata(
                    memberInfo.Value.MemberType,
                    attributes,
                    methodSignature);
            }

            foreach (Type nestedType in root.Type.GetNestedTypes().OrderBy(o => o.FullName))
            {
                root.NestedTypes[ContractEnforcement.GenerateNameWithClassAttributes(nestedType)] = ContractEnforcement.BuildTypeTree(new TypeTree(nestedType), types);
            }

            return(root);
        }