Commit d81426fb authored by Nathan Bean's avatar Nathan Bean
Browse files

Updated program structural verification to consider inheritance

parent a4c9c59a
......@@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeAnalyzers", "CodeAnalyz
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeAnalyzers.Test", "CodeAnalyzers\CodeAnalyzers.Test\CodeAnalyzers.Test.csproj", "{382F61C7-A97D-4546-AA4C-5A3171057347}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProgramVerifierTests", "ProgramVarifierTests\ProgramVerifierTests.csproj", "{5D88D78A-72C1-41BC-A165-D38DDAF29609}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -61,6 +63,14 @@ Global
{382F61C7-A97D-4546-AA4C-5A3171057347}.Release|Any CPU.Build.0 = Release|Any CPU
{382F61C7-A97D-4546-AA4C-5A3171057347}.Release|x64.ActiveCfg = Release|Any CPU
{382F61C7-A97D-4546-AA4C-5A3171057347}.Release|x64.Build.0 = Release|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Debug|x64.ActiveCfg = Debug|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Debug|x64.Build.0 = Debug|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Release|Any CPU.Build.0 = Release|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Release|x64.ActiveCfg = Release|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......
using System;
using KSU.CS.ProgramVerifier;
namespace ProgramVarifierTests
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Let's get ready to Verify!");
var specificationPath = @"C:\Users\nhbean\Teaching\CIS400\Specifications\ms3";
var solutionPath = @"C:\Users\nhbean\Teaching\CIS400\GyroScope";
var result = Verifier.Check(solutionPath, specificationPath).GetAwaiter().GetResult();
foreach(var issue in result.StructuralIssues)
{
Console.WriteLine(issue);
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Verifier\ProgramVerifier.csproj" />
</ItemGroup>
</Project>
......@@ -165,6 +165,29 @@ namespace KSU.CS.Pendant.Server.Controllers
return View(gitHubAccount);
}
/// <summary>
/// Removes this GitHub account from the user
/// </summary>
/// <returns>A HTTP redirect</returns>
[Route("GitHubAccount/Delete/{id}")]
[Authorize(Policy = "AdminOnly")]
public async Task<IActionResult> Delete(int id)
{
// Load the current user's data
var gitHubAccount = await _context.GitHubAccounts.FindAsync(id);
// Remove the GitHub account from the user (if it exists)
if (gitHubAccount != null)
{
_context.Remove(gitHubAccount);
await _context.SaveChangesAsync();
}
return RedirectToAction("Index");
}
/// <summary>
/// Regenerates the user's secret to use with the GitHub Webhooks
/// </summary>
......@@ -309,12 +332,6 @@ namespace KSU.CS.Pendant.Server.Controllers
// Compute the signature from the request body
var computedHash = HmacSHA256(gitHubAccount.Secret, body);
if(gitHubAccount.Username == "dominikfryc")
{
await System.IO.File.AppendAllTextAsync(@"C:\workDirectory\log.txt", $"\n\nFryc computed: {computedHash}");
await System.IO.File.AppendAllTextAsync(@"C:\workDirectory\log.txt", $"\n\nFryc computed: {requestHash}");
}
// Verify the signatures match
var match = computedHash == requestHash;
if (!match) return StatusCode(403, "Signature does not match - You may need to update your secret in your GitHub repo's webhook settings.");
......
......@@ -18,6 +18,16 @@ namespace UMLSpecification
/// </summary>
public string Kind => "class";
/// <summary>
/// If this class is declared abstract
/// </summary>
public bool Abstract { get; set; }
/// <summary>
/// The base class (if any) of this class
/// </summary>
public string BaseClass { get; set; }
public Visibility Visibility { get; set; }
public List<FieldDefinition> Fields { get; } = new List<FieldDefinition>();
......
......@@ -12,6 +12,12 @@ namespace UMLSpecification
public Visibility Visibility { get; set; }
public bool Abstract { get; set; }
public bool Virtual { get; set; }
public bool Override { get; set; }
public string ReturnType { get; set; }
public List<ParameterDefinition> Parameters { get; } = new List<ParameterDefinition>();
......
......@@ -14,6 +14,12 @@ namespace UMLSpecification
public Visibility Visibility { get; set; }
public bool Abstract { get; set; }
public bool Virtual { get; set; }
public bool Override { get; set; }
public bool Get { get; set; }
public bool Set { get; set; }
......
......@@ -47,7 +47,7 @@ namespace KSU.CS.ProgramVerifier
.OfType<EnumDeclarationSyntax>()
.Where(eds => eds.Identifier.ValueText.Equals(enumeration.Name))
.FirstOrDefault();
CheckEnumerationStructure(enumeration, enumSyntax, result.StructuralIssues);
CheckEnumerationStructure(model, enumeration, enumSyntax, result.StructuralIssues);
}
}
......@@ -67,7 +67,7 @@ namespace KSU.CS.ProgramVerifier
.OfType<InterfaceDeclarationSyntax>()
.Where(eds => eds.Identifier.ValueText.Equals(interfaceDef.Name))
.FirstOrDefault();
CheckInterfaceStructure(interfaceDef, interfaceSyntax, result.StructuralIssues);
CheckInterfaceStructure(model, interfaceDef, interfaceSyntax, result.StructuralIssues);
}
}
......@@ -87,7 +87,7 @@ namespace KSU.CS.ProgramVerifier
.OfType<ClassDeclarationSyntax>()
.Where(eds => eds.Identifier.ValueText.Equals(classDef.Name))
.FirstOrDefault();
CheckClassStructure(classDef, classSyntax, result.StructuralIssues);
CheckClassStructure(model, classDef, classSyntax, result.StructuralIssues);
}
}
}
......@@ -99,7 +99,7 @@ namespace KSU.CS.ProgramVerifier
/// <param name="enumerationDef">The enum specification</param>
/// <param name="enumSyntax">The actual enum syntax</param>
/// <param name="issues">A list of issues found in the project</param>
static void CheckEnumerationStructure(UMLSpecification.EnumDefinition enumerationDef, EnumDeclarationSyntax enumSyntax, List<string> issues)
static void CheckEnumerationStructure(SemanticModel model, UMLSpecification.EnumDefinition enumerationDef, EnumDeclarationSyntax enumSyntax, List<string> issues)
{
if (enumSyntax == null)
{
......@@ -127,7 +127,7 @@ namespace KSU.CS.ProgramVerifier
}
}
static void CheckInterfaceStructure(UMLSpecification.InterfaceDefinition interfaceDef, InterfaceDeclarationSyntax interfaceSyntax, List<string> issues)
static void CheckInterfaceStructure(SemanticModel model, UMLSpecification.InterfaceDefinition interfaceDef, InterfaceDeclarationSyntax interfaceSyntax, List<string> issues)
{
if (interfaceSyntax == null)
{
......@@ -144,7 +144,7 @@ namespace KSU.CS.ProgramVerifier
// Check interface events
// Check interface properties
PropertyCheck(interfaceDef, interfaceSyntax, issues);
PropertyCheck(model, interfaceDef, interfaceSyntax, issues);
// Check interface methods
MethodCheck(interfaceDef, interfaceSyntax, issues);
......@@ -154,7 +154,7 @@ namespace KSU.CS.ProgramVerifier
}
}
static void CheckClassStructure(UMLSpecification.ClassDefinition classDef, ClassDeclarationSyntax classSyntax, List<string> issues)
static void CheckClassStructure(SemanticModel model, UMLSpecification.ClassDefinition classDef, ClassDeclarationSyntax classSyntax, List<string> issues)
{
if (classSyntax == null)
{
......@@ -168,12 +168,30 @@ namespace KSU.CS.ProgramVerifier
issues.Add($"Expected class {classDef.Name} to be declared {classDef.Visibility.ToString().ToLower()}");
}
// Check abstract status
if(classDef.Abstract && !AbstractCheck(classSyntax))
{
issues.Add($"Expected class {classDef.Name} to be declared abstract");
}
// Check base class
if(classDef.BaseClass != null)
{
var symbol = model.GetDeclaredSymbol(classSyntax).BaseType;
if(symbol == null || symbol.ToMinimalDisplayString(model, classSyntax.SpanStart) != classDef.BaseClass)
{
issues.Add($"Expected class {classDef.Name} to inherit from class {classDef.BaseClass}");
}
}
// Check interfaces
// Check class events
// Check class fields
// Check class properties
PropertyCheck(classDef, classSyntax, issues);
PropertyCheck(model, classDef, classSyntax, issues);
// Check class methods
MethodCheck(classDef, classSyntax, issues);
......@@ -190,39 +208,61 @@ namespace KSU.CS.ProgramVerifier
/// <param name="definition">The definition containing properties</param>
/// <param name="syntax">The syntax structure to check</param>
/// <param name="issues">The issues discovered</param>
private static void PropertyCheck(UMLSpecification.IPropertyHost definition, BaseTypeDeclarationSyntax syntax, List<string> issues)
private static void PropertyCheck(SemanticModel model, UMLSpecification.IPropertyHost definition, BaseTypeDeclarationSyntax syntax, List<string> issues)
{
// Get the symbol corresponding to the syntax object
var symbol = model.GetDeclaredSymbol(syntax);
// Grab current symbol's properties' symbols
var propertySymbols = symbol.GetMembers().OfType<IPropertySymbol>().ToList();
// Add those of the base classes (if any)
var baseClassSymbol = symbol.BaseType;
while (baseClassSymbol != null)
{
propertySymbols.AddRange(baseClassSymbol.GetMembers().OfType<IPropertySymbol>());
baseClassSymbol = baseClassSymbol.BaseType;
}
// Check all properties
foreach (var property in definition.Properties)
{
var propertySyntax = syntax.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(pds => pds.Identifier.ValueText.Equals(property.Name))
.FirstOrDefault();
if (propertySyntax == null)
var propertySymbol = propertySymbols
.Where(p => p.Name == property.Name)
.FirstOrDefault();
if (propertySymbol is null)
{
issues.Add($"Expected {definition.Kind} {definition.Name} to have a property named {property.Name}.");
}
else
{
{
// check visibility
if (!(syntax is InterfaceDeclarationSyntax) && !VisibilityCheck(property.Visibility, propertySyntax))
if (!(syntax is InterfaceDeclarationSyntax) && !VisibilityCheck(property.Visibility, propertySymbol))
{
issues.Add($"Expected {definition.Kind} {definition.Name} property {property.Name} to be declared {property.Visibility}");
}
// Check abstract status
if (property.Abstract && !propertySymbol.IsAbstract)
{
issues.Add($"Expected {definition.Kind} {definition.Name} property to be declared {property.Visibility}");
issues.Add($"Expected {definition.Kind} {definition.Name} property {property.Name} to be declared abstract");
}
// check type
if (property.Type != propertySyntax.Type.ToString())
if (property.Type != propertySymbol.Type.ToMinimalDisplayString(model, syntax.SpanStart))
{
issues.Add($"Expected {definition.Kind} {definition.Name} property {property.Name} to have Type {property.Type}");
}
// check getter - we need to check for both lamda and traditional syntax
if (property.Get && propertySyntax.ExpressionBody == null && !propertySyntax.AccessorList.Accessors.Any(s => s.Kind() == SyntaxKind.GetAccessorDeclaration))
// check getter
if (property.Get && propertySymbol.GetMethod == null)
{
issues.Add($"Expected {definition.Kind} {definition.Name} property {property.Name} to have a get method.");
}
// check setter
if (property.Set && !propertySyntax.AccessorList.Accessors.Any(s => s.Kind() == SyntaxKind.SetAccessorDeclaration))
if (property.Set && propertySymbol.SetMethod == null)
{
issues.Add($"Expected {definition.Kind} {definition.Name} property {property.Name} to have a set method.");
}
......@@ -293,6 +333,60 @@ namespace KSU.CS.ProgramVerifier
return true;
}
/// <summary>
/// Helper method for checking the visibility of a declared symbol
/// </summary>
/// <param name="visibility">The expected visibility declared in the UML</param>
/// <param name="declarationSyntax">The declared type to check</param>
/// <returns></returns>
private static bool VisibilityCheck(UMLSpecification.Visibility visibility, ISymbol symbol)
{
if (visibility == UMLSpecification.Visibility.Public && !(symbol.DeclaredAccessibility == Accessibility.Public))
{
return false;
}
if (visibility == UMLSpecification.Visibility.Protected && !(symbol.DeclaredAccessibility == Accessibility.Protected))
{
return false;
}
if (visibility == UMLSpecification.Visibility.Private && !(symbol.DeclaredAccessibility == Accessibility.Private))
{
return false;
}
return true;
}
/// <summary>
/// Determines if the syntax element is declared abstract
/// </summary>
/// <param name="declarationSyntax">The declaration syntax to check</param>
/// <returns>True if it is abstract, false if not</returns>
private static bool AbstractCheck(MemberDeclarationSyntax declarationSyntax)
{
return declarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword));
}
private static bool CompareAliasedTypes(string aliasType, string actualType)
{
return aliasType switch
{
"IEnumerable<string>" => actualType == "IEnumerable<String>",
"bool" => actualType == "Boolean",
"string" => actualType == "String",
"sbyte" => actualType == "SByte",
"byte" => actualType == "Byte",
"short" => actualType == "Int16",
"ushort" => actualType == "UInt16",
"int" => actualType == "Int32",
"uint" => actualType == "UInt32",
"long" => actualType == "Int64",
"ulong" => actualType == "UInt64",
"float" => actualType == "Single",
"double" => actualType == "Double",
"decimal" => actualType == "Decimal",
_ => actualType == aliasType
};
}
}
}
......@@ -61,6 +61,10 @@ namespace KSU.CS.ProgramVerifier
// Run any student tests
//await TestRunner.Run(solution, result);
result.FunctionalIssues.Add("Pendant is unable to check for functional issues at this time.");
await System.IO.File.AppendAllTextAsync(@"c:\\workDirectory\log.txt", $"Finished validating ${studentPath}");
/*
try
{
await System.IO.File.AppendAllTextAsync(@"c:\\workDirectory\log.txt", $"Getting ready to run tests on ${studentPath}; {result.StructuralIssues.Count} structural issues found; Compilation was {result.DoesCompile}");
......@@ -72,6 +76,7 @@ namespace KSU.CS.ProgramVerifier
{
await System.IO.File.AppendAllTextAsync(@"c:\\workDirectory\log.txt", $"Encountered Error: ${e.Message} ${e.StackTrace}");
}
*/
}
return result;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment