/// <summary> /// Gets all type declarations made in the current codebase that is being compiled. /// This is a tool method to be used in more complex achievements. /// This method is caching and can be called without discretion, and is thread safe. /// </summary> /// <param name="buildInformation">BuildInformation object used to locate the codebase</param> /// <returns>Cached collection of TypeDeclarationInfo</returns> public IEnumerable<TypeDeclarationInfo> GetCodebaseTypeDeclarations(BuildInformation buildInformation) { lock (codebaseTypeDefinitionPadLock) { if (codebaseTypeDefinitions == null) { codebaseTypeDefinitions = new List<TypeDeclarationInfo>(); foreach (var filename in buildInformation.CodeFiles) { var parser = GetParser(filename); var typeDeclarationInfoVisitor = new TypeDeclarationVisitor(); parser.CompilationUnit.AcceptVisitor(typeDeclarationInfoVisitor, null); codebaseTypeDefinitions.AddRange(typeDeclarationInfoVisitor.TypeDeclarations); } } return codebaseTypeDefinitions; } }
/// <summary> /// Initializes a new instance of the <see cref="DetectionSession"/> class. /// </summary> /// <param name="buildInformation">The build information.</param> public DetectionSession(BuildInformation buildInformation) { BuildInformation = buildInformation; }
public void TestAchievements() { ObjectFactory.Configure(a => a.For<IAchievementRepository>().Singleton().Use<AppDataXmlCompletedAchievementsRepository>()); var achievementRepository = ObjectFactory.GetInstance<IAchievementRepository>(); achievementRepository.LoadFromAssembly(typeof(NRefactoryAchievement).Assembly); var achievementTests = GetType().Assembly.GetTypes().Where(a => a.GetCustomAttributes(typeof (ExpectUnlockAttribute), true).Length > 0); foreach(var test in achievementTests) { var sourceFile = Path.GetFullPath("TestCases/" + test.Name + ".cs"); var buildInformation = new BuildInformation() { ActiveFile = sourceFile, ActiveProject = null, ActiveProjectOutputDirectory = Path.GetDirectoryName(sourceFile), ChangedFiles = new []{sourceFile} }; var expectedAchievements = test.GetCustomAttributes(typeof(ExpectUnlockAttribute), true).Select(a => ((ExpectUnlockAttribute)a).ExpectedAchievementType).ToList(); using (var detectionSession = new DetectionSession(buildInformation)) { var achievements = achievementRepository.GetAchievements().ToList(); var tasks = new Task[achievements.Count()]; var i = 0; var unlockedAchievements = new List<Type>(); foreach (var uncompletedAchievement in achievements) { var a = uncompletedAchievement; tasks[i++] = Task.Factory.StartNew(() => { var achievementType = a.AchievementType; var achievement = (AchievementBase)Activator.CreateInstance(achievementType); var achievementUnlocked = achievement.DetectAchievement(detectionSession); if (achievementUnlocked) { a.CodeLocation = achievement.AchievementCodeLocation; a.IsCompleted = true; unlockedAchievements.Add(achievementType); } }); } Task.WaitAll(tasks); //Test that expected achievements unlocked foreach(var expectedAchievement in expectedAchievements) { Assert.IsTrue(unlockedAchievements.Contains(expectedAchievement), Path.GetFileName(sourceFile) + " did not unlock expected achievement: " + expectedAchievement.FullName); } //Test that only expected achievements unlocked var unexpectedAchievements = unlockedAchievements.Except(expectedAchievements).Except(_globallyIgnoredAchievements).ToList(); Assert.IsTrue(unexpectedAchievements.Count() == 0, Path.GetFileName(sourceFile) + " unlocks unexpected achievements: " + string.Join(", ", unexpectedAchievements.Select(a => a.Name))); } } }
/// <summary> /// Dispatches handling of achievement detection in the file(s) specified /// in the passed BuildInformation object. /// /// This method is detection method agnostic. It simply forwards the /// BuildInformation object to all implementations of the Achievement class. /// </summary> /// <param name="buildInformation"> /// Objects specifying documents to parse for achievements. /// </param> public static bool Dispatch(BuildInformation buildInformation) { AchievementContext.OnAchievementDetectionStarting(null, new EventArgs()); var unlockedAchievements = new List <Achievement>(); var achievementDescriptorRepository = ObjectFactory.GetInstance <IAchievementRepository>(); using (var detectionSession = new DetectionSession(buildInformation)) { var unlockableAchievements = achievementDescriptorRepository.GetUnlockableAchievements(); var stopWatch = new Stopwatch(); stopWatch.Start(); var tasks = new Task[unlockableAchievements.Count()]; var i = 0; foreach (var uncompletedAchievement in unlockableAchievements) { var a = uncompletedAchievement; tasks[i++] = Task.Factory.StartNew(() => { var achievement = (AchievementBase)Activator.CreateInstance(a.AchievementType); var achievementUnlocked = achievement.DetectAchievement(detectionSession); if (achievementUnlocked) { a.CodeLocation = achievement.AchievementCodeLocation; a.IsCompleted = true; unlockedAchievements.Add(a); } }); } Task.WaitAll(tasks); stopWatch.Stop(); OnDetectionCompleted(null, new DetectionCompletedEventArgs() { AchievementsTested = unlockableAchievements.Count(), ElapsedMilliseconds = (int)stopWatch.ElapsedMilliseconds }); } if (unlockedAchievements.Count() > 0) { foreach (var completedAchievement in unlockedAchievements.Where(a => a != null)) { achievementDescriptorRepository.MarkAchievementAsCompleted(completedAchievement); } AchievementContext.OnAchievementsUnlocked(null, unlockedAchievements); return(true); } return(false); }
/// <summary> /// Called when a build is completed. /// </summary> /// <param name="fSucceeded"><c>true</c> if no update actions failed.</param> /// <param name="fModified"><c>true</c> if any update action succeeded.</param> /// <param name="fCancelCommand"><c>true</c> if update actions were canceled.</param> /// <returns> /// If the method succeeds, it returns <c>S_OK</c>. If it fails, it returns an error code. /// </returns> int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) { if (fSucceeded != 0) { try { ProjectTypeDef projectTypeDef = null; if (dte.ActiveDocument != null && dte.ActiveDocument.ProjectItem != null && dte.ActiveDocument.ProjectItem.ContainingProject != null) { var containingProject = dte.ActiveDocument.ProjectItem.ContainingProject; var projectFile = containingProject.FileName; if (!string.IsNullOrEmpty(projectFile) && !projectTypeDefCache.TryGetValue(projectFile, out projectTypeDef)) { XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003"; var csProj = XDocument.Load(containingProject.FileName); var strokesAchievementTypeNodes = csProj.Descendants(ns + "StrokesProjectType"); var strokesAchievementTypeNode = strokesAchievementTypeNodes.FirstOrDefault(); if (strokesAchievementTypeNode != null && strokesAchievementTypeNode.HasElements) { projectTypeDef = new ProjectTypeDef() { IsAchievementProject = strokesAchievementTypeNode.Elements().Any(a => a.Name == ns + "Achievements" && a.Value == "true"), IsChallengeProject = strokesAchievementTypeNode.Elements().Any(a => a.Name == ns + "Challenges" && a.Value == "true") }; projectTypeDefCache.Add(projectFile, projectTypeDef); } } } if (projectTypeDef == null) { projectTypeDef = new ProjectTypeDef(); // Assume default values (false, false); } // Return out if we're not compiling an achievement project. if (!projectTypeDef.IsAchievementProject && !projectTypeDef.IsChallengeProject) { return VSConstants.S_OK; } // Get all .cs files in solution projects, that has changed since lastAchievementCheck var changedFiles = FileTracker.GetFiles(dte.Solution); // Update lastAchievementCheck lastAchievementCheck = DateTime.Now; // Construct build information var buildInformation = new BuildInformation(); buildInformation.CodeFiles = FileTracker.GetFiles(dte.Solution).ToArray(); var activeDocument = dte.ActiveDocument; if (activeDocument != null) { var documentFile = activeDocument.FullName; if (documentFile.EndsWith(".cs")) { buildInformation.ActiveFile = documentFile; if (!changedFiles.Contains(documentFile)) { // Always check active document. changedFiles.Add(documentFile); } // Fill relevant values on buildInformation var projectItem = activeDocument.ProjectItem.ContainingProject; buildInformation.ActiveProject = projectItem.FileName; buildInformation.ActiveProjectOutputDirectory = FileTracker.GetProjectOutputDirectory(projectItem); } } buildInformation.ChangedFiles = changedFiles.ToArray(); // Validate build information if (buildInformation.ActiveProject == null && buildInformation.ChangedFiles.Length == 0) { // Build information contains nothing - so we won't detect achievements return VSConstants.S_OK; } // Lock builds while detection is occuring - this prevents parallel detection isAchievementDetectionRunning = true; DetectionDispatcher.Dispatch(buildInformation); } finally { // Unlock builds isAchievementDetectionRunning = false; } } return VSConstants.S_OK; }
/// <summary> /// Dispatches handling of achievement detection in the file(s) specified /// in the passed BuildInformation object. /// /// This method is detection method agnostic. It simply forwards the /// BuildInformation object to all implementations of the Achievement class. /// </summary> /// <param name="buildInformation"> /// Objects specifying documents to parse for achievements. /// </param> public static bool Dispatch(BuildInformation buildInformation) { AchievementContext.OnAchievementDetectionStarting(null, new EventArgs()); var unlockedAchievements = new List<Achievement>(); var achievementDescriptorRepository = ObjectFactory.GetInstance<IAchievementRepository>(); using (var detectionSession = new DetectionSession(buildInformation)) { var unlockableAchievements = achievementDescriptorRepository.GetUnlockableAchievements(); var stopWatch = new Stopwatch(); stopWatch.Start(); var tasks = new Task[unlockableAchievements.Count()]; var i = 0; foreach (var uncompletedAchievement in unlockableAchievements) { var a = uncompletedAchievement; tasks[i++] = Task.Factory.StartNew(() => { var achievement = (AchievementBase)Activator.CreateInstance(a.AchievementType); var achievementUnlocked = achievement.DetectAchievement(detectionSession); if (achievementUnlocked) { a.CodeLocation = achievement.AchievementCodeLocation; a.IsCompleted = true; unlockedAchievements.Add(a); } }); } Task.WaitAll(tasks); stopWatch.Stop(); OnDetectionCompleted(null, new DetectionCompletedEventArgs() { AchievementsTested = unlockableAchievements.Count(), ElapsedMilliseconds = (int)stopWatch.ElapsedMilliseconds }); } if (unlockedAchievements.Count() > 0) { foreach (var completedAchievement in unlockedAchievements.Where(a => a != null)) { achievementDescriptorRepository.MarkAchievementAsCompleted(completedAchievement); } AchievementContext.OnAchievementsUnlocked(null, unlockedAchievements); return true; } return false; }