Présentation

Bonjour à tous,

Un petit billet rapide de présentation, afin que vous puissiez situer qui je suis, pourquoi je fais ce blog, et ce que vous pouvez vous attendre à voir dedans.

Ce post, comme tous ceux qui suivent jusque novembre 2011, ont d’abord été publiés sur la plateforme DotNet-France, avant d’être reportés sur cette plateforme indépendante. Merci à Julien Dollon de m’avoir hébergé (et supporté) pendant cette année et un peu plus !

Je m’appelle Jean-Philippe Gouigoux, je suis l’architecte logiciel d’un éditeur de logiciels de gestion petit par la taille, mais grand par l’innovation. Je suis MCTS SQLServer et MCPD Enterprise Architect.  Passionné d’informatique depuis mon premier Amstrad, j’ai écrit un driver d’imprimante Citizen 120D à une époque où la plupart de ceux qui me liront remplissait encore joyeusement ses couches.

Après un détour inexpliqué par des études d’ingénieur en systèmes mécaniques (UTC / Cranfield), mon subconscient m’a immédiatement remis dans les rails de l’informatique. J’ai fait du Java et du C++ pour une boîte de consultants à l’international pendant trois ans, et le charme de celle qui devint ma femme aidant, je me suis posé en Bretagne où je travaille depuis. J’ai eu le bonheur  de tourner pour ma société la page VB6 pour passer au VB.NET, puis au C#, .NET 2.0, et maintenant à .NET 3.5, Linq, etc. Mon boulot d’architecte m’amène à mettre en place l’infrastructure logicielle pour notre équipe de développement, ainsi qu’à analyser et coder certains aspects un peu exotiques de nos applications. La veille technologique fait également partie de mes attributions, même si je la pratique surtout depuis chez moi.

J’aime la philosophie de l’OpenSource, et la pratique dans mes projets personnels, mais je dois avouer que .NET me fait aimer Microsoft. Certains crieront au loup, mais c’est l’occasion de définir la philosophie de mon blog : je veux des technologies qui marchent. Pas de la recherche torturée de révolution informatique, mais du concret, du robuste. J’aime bien tester les nouvelles technologies, mais c’est avant tout pour améliorer la qualité de mon travail. D’où cet aspect qu’on peut qualifier d’opportuniste. Dans une entreprise, ce qu’il faut, c’est que ça marche. Si l’OpenSource fait l’affaire, tant mieux. Si Microsoft fait mieux avec .NET, c’est ça qu’on prendra.

Toujours dans cette idée d’industrialisation, vous ne trouverez pas sur ce blog de présentation sommaire d’une technologie toute nouvelle. Je ne serai pas à la pointe de la technologie (rappelez-vous : je suis vieux !). Mais ce que je souhaite réussir à faire, c’est vous donner des idées sur la capacité réelle d’une technologie à réussir le test industriel. La technologie à la mode, c’est sympa, mais est-ce qu’elle tient la charge ? Est-ce que ses concepteurs ont pensé aux montées en version, à l’inclusion dans une usine logicielle, à la facilité de formation d’une équipe ?

D’où les deux premiers articles : un retour d’expérience sur un serveur ASP.NET de services web en Mono (montée en charge, installation, configuration du pilote Oracle, problèmes rencontrés), et un papier un peu plus long sur la prévalence (comment gérer les problèmes de cycle de vie, la performance d’une application prévalente, le problème des requêtes, etc.)

Bonne lecture !

Posted in Uncategorized | Leave a comment

Déploiement industriel d’un serveur ASP.NET avec Mono et Apache

Introduction

La maturité technologique de Mono dans sa toute dernière version stable 2.4.3 est incontestable au vu de la roadmap parcourue et du pourcentage atteint sur les réécritures de classes du framework. Mais qu’en est-il de la configuration fine, des performances, de la montée en charge ? Bref, Mono est-il prêt au passage en production de niveau industriel ?

Depuis plusieurs années déjà, la pile ASP.NET est complète, et toutes les classes de base sont reprises. La prise en charge de WinForms a bien sûr posé plus de problèmes, ce qui est tout à fait compréhensible vu les différences fondamentales entre Win32 et X11. Les détracteurs de Mono supposent que ce dernier passera toute son existence à implémenter six mois après Microsoft les nouveautés de .NET. C’est oublier que Mono propose des librairies .NET spécifiques à Linux ou à certains métier.

Ceci étant dit, il est rare d’entendre parler d’un hébergeur proposant Mono, ou d’un éditeur de logiciel supportant ses solutions sous cet environnement. Pourquoi aussi peu de références ? Le système est pourtant suffisamment mature, et dispose de sérieux atouts, en particulier en termes de performance. C’est en tout cas le retour d’expérience que ma société a obtenu, lors de la mise en place de Mono dans le cadre d’un banc de test d’industrialisation de la solution.

Objectifs

L’objectif de ce banc de test était de vérifier la faisabilité du portage industriel du serveur de web services écrits en ASP.NET sur un environnement Linux. J’insiste sur ce caractère d’industrialisation : le fonctionnement d’une application ASP.NET simple sous Linux est déjà opérationnel depuis longtemps, et la migration ne nécessite qu’une recopie du contenu du répertoire virtuel. Soit. Mais sortons d’une application simple, et étudions désormais un ‘vrai’ serveur SOA. Comment fonctionne l’authentification ? Quid de l’impersonification du processus ? La connexion à la base de données nécessite-t-elle une configuration particulière ? Où se trouvent les fichiers de configuration en Mono ?

Nous avons du éplucher les forums et tâtonner de longues journées pour trouver les réglages expliqués ci-dessous : nous espérons que les réunir en un article aidera à promouvoir Mono à un rang ‘valide pour applications industrielles’. Nous verrons plus loin qu’il dispose de vrais avantages sur la pile .NET officielle fournie par Microsoft.

Préconfiguration

Les tests ont été menés sur une distribution OpenSuse 10.3, ainsi que sur une CentOS 5 (base RedHat). On détaillera dans le présent article l’installation pour l’OpenSuse. L’OS est installé avec les options ‘Développement C/C++’, ‘Développement .NET’, ‘Serveur Web et LAMP’. Apache est en version 2.0.13, Mono en 2.4.3. On ne l’installe pas à partir de l’OS car la version proposée est une vieille 1.2.6. Les versions 11 de SuSE propose la version 1.9, à ma connaissance.

1

Bien que l’installation du client Oracle soit censée être simplifiée par la présence d’un script autonome se chargeant de la mise en place complète de l’environnement, celui-ci nous a posé de nombreux problèmes, et nous avons finalement réalisé une bonne partie de la configuration à la main. Mais ce n’est pas le présent sujet d’étude.

Installation de l’application

Comme nous l’avions expliqué précédemment, le contenu du serveur virtuel a purement et simplement été recopié depuis une version Windows basée sur IIS. Le serveur virtuel avait été déployé en mode précompilé, avec un fichier .asmx et plusieurs fichiers dll correspondant aux classes utilisées et référencées par l’implémentation de ce service. Un fichier de configuration de l’application est également présent, pour porter les settings nécessaires à notre serveur. Le tout est recopié dans /srv/www/htdocs pour les tests.

Configuration de MOD_MONO

Le module MOD_MONO doit être installé dans Apache pour que Mono puisse traiter l’ASP.NET à l’intérieur de ce serveur HTTP. La mise en place du module est assez simple sous SuSE et on ne détaillera pas.

Il est par contre essentiel de configurer /etc/apache2/conf.d/mod_mono.conf en rajoutant en fin de fichier la ligne

MonoServerPath :/usr/bin/mod-mono-server2

Suite à de nombreuses tentatives infructueuses, nous avons réalisé que le plus simple pour que Mono retrouve la librairie correspondant au client Oracle était de mettre l’adresse en absolu dans le fichier /etc/mono/config, où la ligne

<dllmap dll="oci" target="libclntsh.so" os="!Windows"/>

est alors remplacée par

<dllmap dll="oci" target="/opt/oracle/product/lib/libclntsh.so" os="!Windows"/>

Il doit être possible d’affecter le LD_LIBRARY_PATH de l’utilisateur Apache pour que cette librairie soit trouvée sans avoir à préciser le chemin en dur, mais je n’ai pas trouvé dans quel fichier de configuration il faudrait mettre cette information. Si vous avez une solution, vous êtes évidemment les bienvenus.
Une fois les manipulations effectuées, ne pas oublier de relancer Apache, avec

service apache2 restart

Connexion à la base de données

Nous ne sommes pas au bout de nos peines. Pour la même raison que précédemment, nous n’avons pas affecté la variable ORACLE_HOME pour l’utilisateur Apache de notre serveur de services web.

Il doit y avoir moyen, mais suite à plusieurs heures de recherche infructueuse, nous nous sommes finalement rabattus vers une solution toute aussi fonctionnelle, bien que moins élégante, et qui consiste à utiliser du code .NET au démarrage de l’application pour affecter la variable d’environnement :

Environment.SetVariable("ORACLE_HOME", "/opt/product/oracle/R11g1/");

Une fois cette étape passée, nous avons pu pour la première fois nous connecter à notre serveur de web services et utiliser notre client de manière fonctionnelle. Nous avons été frappés dès le début par la performance de l’ensemble.

Aucune remarque sur la gestion de session, qui est indispensable pour garder les informations de connexion dans le cas d’une identification propriétaire. Aucune modification nécessaire dans le fichier de configuration ni sur l’installation pour que celle-ci fonctionne. Nous n’avons pas encore testé les modes STATESERVER et SQLSTATESERVER, permettant de déporter la gestion des sessions sur un service Windows ou dans une base de données, mais le mode par défaut INPROC est plus performant, et donne entière satisfaction.

Quelques premiers tests plus loin, et toujours pas de différence avec un serveur Windows. Il a fallu arriver dans des parties métier plus avancées pour rencontrer des erreurs. La première est apparue sur un appel de procédure stockée, plus précisément sur la méthode DeriveParameters() de la classe OracleCommandBuilder, utilisée pour récupérer les paramètres de la procédure stockée. A l’appel de cette méthode, nous avons obtenu l’erreur suivante :

“The requested feature is not implemented.”

Après vérification dans le code source de Mono, il apparaît effectivement que cette méthode n’est pas encore implémentée. J’avais envie de rajouter « avis aux amateurs », mais il faut savoir que le provider Oracle de Mono n’est pas très activement maintenu. Il semble que ce ne soit pas une priorité pour Novell. Suite à ce problème (ainsi que d’autres que je détaillerai éventuellement dans un prochain article), la personne anciennement en charge de cette fonctionnalité m’a conseillé de me tourner vers un provider commercial Oracle fonctionnant en mode Mono. Il y en a plusieurs, et nous avons adopté celui de Devart. Que les aficionados de l’OpenSource ne m’en veuillent pas trop : je promets de repasser au provider libre dès que celui-ci sera d’une qualité suffisante, mais ma priorité numéro 1 est que les logiciels que ma société édite fonctionnent bien.

Pour contourner cette restriction, nous avons décidé de créer nous-mêmes les paramètres attendus et avons rencontré un nouveau problème lors de la création des paramètres Oracles pour l’appel de la procédure stockée :

“Size is not set.”

Erreur d’autant plus étrange que nous avions bien précisé la taille du paramètre dans le constructeur avec la ligne suivante :

OracleParameter op = new OracleParameter("coucou", TypeParam, 32);

Il semble que la taille ne soit pas prise en compte. La solution a consisté à forcer les tailles de données dans chacun des paramètres :

OracleParameter op = new OracleParameter("coucou", TypeParam, 0);op.Size = 32;

Une autre erreur a été localisée sur le fournisseur Oracle pour ADO.NET fourni par Mono : la surcharge suivante de OracleParameter n’existe pas :

OracleParameter(string, type, string, bool, int, int, string, direction)

Ce n’est pas à proprement parler un problème si on développe directement en Mono, mais cette signature est utilisée dans le code généré sur un DataSet typé en provenance de Microsoft Visual Studio .NET 2005. La qualité de Mono est telle qu’on s’attend presque automatiquement à ce que toutes les surcharges soient disponibles.

C’est dans ce genre de contexte que l’utilisation de code Open Source amène un plus incomparable. Nous avons pu récupérer le code source de OracleParameter, rajouter le constructeur voulu et tout recompiler. La modification a bien sûr été proposée à l’équipe Mono par l’intermédiaire du site BugZilla dédié, pour intégration si elle satisfait aux critères de qualité.

Clickonce

Vu que le serveur applicatif fonctionne, pourquoi ne pas également installer sous Linux notre serveur de déploiement ? Là, les choses sont encore plus simples. Il faut spécifier les types mimes correspondant aux extensions liées à ClickOnce, dans le fichier /etc/apache2/mimes.types, en ajoutant les lignes suivantes :

AddType application/x-ms-application application
AddType application/x-ms-manifest manifest
AddType application/octet-stream deploy
AddType application/x-msdownload dll

Un premier test nous remonte toutefois l’erreur ci-dessous :

2

Le problème vient du fait que Mono prend bien en charge les fichiers de configuration, mais refuse de les laisser remonter par défaut sur un appel de client, pour des raisons de sécurité (présence possible de mots de passes, de chaînes de connexion, etc.). Il faut ouvrir cette possibilité pour le fichier .exe.config de notre client à déployer par ClickOnce, en ajoutant dans le fichier /etc/mono/2.0/web.config la ligne suivante :

<add verb="*" path="*.exe.config" type="System.Web.StaticFileHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

Le même problème existe sur les fichiers de type dll, et il faudra mettre en commentaires la ligne ci-dessous :

<!--<add verb="*" path="*.dll" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />-->

En effet, le Handler par défaut pour ce filtre de fichier est forbidden, à savoir que Mono interdit que ce fichier soit remonté lors d’une demande client. A nouveau, ceci est compréhensible du point de vue de la sécurité, mais dans le cas d’un déploiement ClickOnce, nous avons besoin de remonter nos dlls. Si cela pose vraiment problème, il est toujours possible de renommer les fichiers .dll en .dll.download. Le processus ClickOnce supporte d’ailleurs cette façon de télécharger les références.

Performances

Le premier avantage d’une utilisation de Mono / Apache par rapport à Microsoft .NET / IIS est sans conteste la performance brute mono-utilisateur. Attention, je ne parle pas de montée en charge, mais uniquement du temps de réaction du serveur sans aucune charge qu’un seul utilisateur. Dans un premier temps, nous n’avons pas passé trop de temps à déterminer si le serveur web ou la pile Mono était responsable de cette différence, vu que les deux sont plus ou moins liés.

La première installation de Linux sur une machine à faible capacité nous a surpris avant même de mettre en place les traces chronométrées : la différence de temps de chargement était visible sans même avoir à mesurer quoi que ce soit. Notre première réaction a été de vérifier les caractéristiques de la machine utilisée, car à force de virtualiser et de faire du déport d’affichage, on finit parfois par ne plus bien se rappeler des spécifications hardware… Elles étaient largement inférieures à notre machine de référence, pourtant plus lente à exécuter les mêmes services web. Nous avons alors éliminé les possibilités d’erreurs : pointage des deux serveurs sur la même base de données, mise en place sur la même machine, désactivation de tous les services d’un côté comme de l’autre, pour arriver à la conclusion suivante : OpenSuse 10.3 + Apache 2.0 + Mono 2.4.3 est environ 20% plus rapide que Windows 2003 Server + IIS 6.0 + Microsoft .NET 2.0, à machine équivalente, pour des actions unitaires.

Nous ne prétendons pas avoir mené un benchmark exhaustif et pouvoir analyser finement les différences, voire les prédire en fonction d’autres contextes. Mais bon, les chronomètres font foi, et quelles que soient les raisons ou précautions à prendre sur ces résultats, en mode mono-utilisateur, notre serveur est 20% plus rapide avec Mono.

Attention, à nouveau : je parle bien d’un mode mono-utilisateur, qui n’a absolument rien à voir avec la réalité d’un serveur de services web. Lors de la montée en charge, on a constaté que Apache / Mono ne répondait pas aussi bien que IIS / MS.NET, et même loin de là si on n’utilise pas les extensions Novell pour la performance de Mono. Je ferai peut-être un article plus détaillé là-dessus un de ces jours, mais à la louche, on tient deux fois moins d’utilisateurs dans le premier mode que dans le deuxième.

Petite remarque sur les extensions Mono : Novell vend une extension de SuSE Entreprise pour améliorer les performances de Mono. Je ne sais pas ce qui se passe en dessous, si il y a de l’optimisation des librairies système, de Mono, de MOD-Mono ou d’Apache, mais le fait d’installer ces extensions a donné un surplus de performance réellement impressionnant. Pour être honnête, la différence de montée en charge sans ces extensions était telle qu’on ne pouvait pas décemment remplacer des serveurs Windows / IIS par ceux en Linux / Apache / Mono. Avec, il manque encore de la performance, mais à la vitesse où Mono évolue, on peut se dire qu’il ne manque plus que quelques versions pour atteindre un niveau équivalent.

Avantages

Le fait de pouvoir intervenir dans le code est extrêmement positif. N’étant pas des participants assidus aux projets Open Source, l’argument du code modifiable selon nos besoins nous paraissait un peu éloigné de nos considérations.

Nous raisonnions en termes de complexité du projet, et pensions ne jamais être en mesure de proposer des modifications. Oui, quand on voit la taille impressionnante du projet Mono, on a beau être un programmeur aguerri, on ne sait pas trop par quel bout aborder tout ça…
En pratique, la complexité s’efface lorsqu’on se penche sur un code particulier (on ne peut que saluer la qualité des commentaires de code de Mono). Je n’ai pas eu de souci à intervenir dans la classe OracleParameter pour rajouter une signature de constructeur, et ce de manière très simple. Ca me fait d’ailleurs penser que je n’ai pas regardé si ma modification avait été acceptée par Mono…

Conclusion

Malgré quelques petits problèmes sur la configuration de l’accès à la base de données, il apparaît que Mono se prête bien à une utilisation dans un cadre industriel.
Le but des tests dont nous rapportons les résultats ci-dessus était de démontrer la faisabilité d’un déploiement du serveur de services web en mode Linux pour de nouveaux clients.

Non seulement cette faisabilité est aujourd’hui démontrée, avec une première installation prévue pour les prochains mois, mais elle a également amené de bonnes surprises en termes de temps de réponse, dans des charges faibles.

Dans un premier temps, il était prévu de ne migrer que les clients en faisant la demande. A la suite de ces résultats, nous envisageons de migrer nos propres serveurs (nous hébergeons les données de certains de nos clients) dans un futur proche.

Et pour plus tard

Cette expérience positive nous encourage à envisager également des tests de Mono pour la partie interface.

Clairement, il paraît difficile d’aborder le problème sur une application WinForms. Mais le passage à WPF, avec une grammaire XAML de description de l’interface séparée du moteur de rendu lui-même, va certainement simplifier fortement cette migration, vu que l’implémentation du moteur pourra être réalisée en code natif Linux.

A condition bien sûr que Mono continue d’avancer au même rythme. Le rapprochement entre Novell et Microsoft a donné de bons résultats pour Silverlight. En ce qui concerne WPF, entre le fait qu’il est en train de se faire phagocyter par Silverlight, et que même Miguel de Icaza ne croit pas en une implémentation prochaine de WPF en Mono, j’ai de furieux doutes. Le mode de développement préférentiel risque donc de devenir Silverlight, en utilisant le mode Out Of Browser pour simuler des applications Line Of Business. Tout ceci pose des questions, mais le caractère Open Source et la qualité des développements sont un gage de pérennité considérable. Et surtout, qui aurait dit il y a un an que Mono aurait déjà un compilateur C# 4.0, et fournirait le support d’une partie de WCF, de presque tout Silverlight 3, etc. ?

Annexes

SUSE Linux Enterprise Mono Extension : http://www.novell.com/products/mono/

Posted in .NET | Tagged , | Leave a comment

PartCover

Comme certainement beaucoup d’entre vous, j’ai été assez déçu de voir que NCover était désormais un logiciel commercial. Dans le cadre de mon travail, j’ai demandé à disposer de la version professionnelle, surtout pour pouvoir utiliser une GUI. En pratique, je ne trouve pas terribles les améliorations par rapport à la 1.5.8. Il s’agit de la dernière version Open Source, disponible, bien que pas tellement publiée, sur le site ncover.com, en cliquant sur le lien pour télécharger la version d’essai, puis en cliquant sur le bouton “Have a license ? Download here”.

Les messages d’erreur dans la version commerciale sont toujours aussi abscons que dans la version Open Source. En particulier, si vous obtenez une couverture de code absolument vide, c’est classiquement parce que les fichiers .pdb n’étaient pas avec vos fichiers .dll. Pas de message, rien, alors que c’est pourtant une erreur habituelle. De même pour la non-registration de la librairie de base. Pourquoi ne pas remonter un message d’erreur ? Et quand vous avez passé tous ces problèmes, il vous reste encore à ne pas oublier de rajouter /noshadow en option de NUnit, sinon il place en cache les .dll… mais pas les .pdb, et vous retombez dans le même souci que précédemment.

Bref, beaucoup de travail pour une version commerciale. D’où l’idée de voir si un projet Open Source avait pris le relai. C’est le cas, avec PartCover, disponible sur SourceForge. SharpDevelop utilise désormais ce logiciel plutôt que NCover, pour rester au plus près de l’esprit Open Source.

Posted in .NET, Tests | Leave a comment

Mise à jour des librairies pour FOP en .NET

Suite à une demande dans les commentaires d’un précédent article, voici les librairies compilées en .NET pour faire du XSL/FO par interop avec IKVM. J’en profite pour passer aux nouvelles versions de FOP (0.95) et d’IKVM (0.40.0.1).

Le tout a été buildé sur la base d’une VM 1.6.0_13-b03, et sans clé de nom fort.

Voici le lien. Si vous utilisez ces librairies, merci de m’envoyer un petit message sur votre retour d’expérience, satisfaisant ou pas.

Tant que j’y suis… Cette méthode est manuelle, et ne sélectionne pas les classes utilisées pour la compilation par IKVM. Si vous voulez seulement faire du XSLFO, vous pouvez utiliser également le projet NFOP. D’après ce que j’ai compris, ce n’est pas un portage de FOP en .NET, mais plutôt la même chose que ce que j’ai détaillé dans le post sur XSL/FO en .NET, à savoir une recompilation. Par contre, ils font bien sûr beaucoup mieux les choses, et en particulier, ils doivent filtrer les classes nécessaires lorsqu’ils compilent les JAR de dépendances, ce qui fait que le résultat est bien plus léger. Je vais tester si il n’y a pas autre chose, et je posterai mes remarques dans quelque temps.

Posted in .NET | Tagged , | Leave a comment

Programmation parallèle en .NET

Un petit test de la parallélisation en .NET, avec la CTP de Juin 2008 des Parallel Extensions.

A utiliser, c’est très simple : on utilise une nouvelle librairie System.Threading, qui rajoute des opérateurs parallélisés ainsi que des listes supportant la parallélisation.

Voici un petit bout de code pour tester sur un AMD 64X2 4400+, avec donc deux coeurs :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace TestParallelisation
{
 class Program
 {
  static void Main(string[] args)
  {
   List<int> Entiers = new List<int>();

   for (int ind = 100000; ind < 120000; ind++)
    Entiers.Add(ind); Stopwatch Chrono = Stopwatch.StartNew();
   List<int> Premiers = new List<int>();

   foreach (int Entier in Entiers)
    if (EstPremier(Entier))
     Premiers.Add(Entier);
   Console.WriteLine("{0} premiers trouvés en {1} ms", Premiers.Count, Chrono.Elapsed.Milliseconds);

  &nbsp;Chrono = Stopwatch.StartNew();
   List<int> PremiersSynchro = new List<int>();
   Parallel.ForEach<int>(Entiers, i => { if (EstPremier(i)) PremiersSynchro.Add(i); });
   Console.WriteLine("{0} premiers trouvés en {1} ms", PremiersSynchro.Count, Chrono.Elapsed.Milliseconds);
  }

  private static bool EstPremier(int Nombre)
  {
   for (int ind = 2; ind < Nombre - 1; ind++)
    if (Nombre % ind == 0)
     return false;
   return true;
  }
 }
}

On voit qu’on cherche la liste des nombres premiers entre 100 000 et 120 000, avec une première version standard, et qui s’exécutera donc sur un seul core, et une seconde permettant la parallélisation. Ici, rien de compliqué puisque chaque nombre est traité séparément, donc c’est très simple de répartir sur autant de processeurs que nécessaire.

Les résultats : 539 ms en mono-core, 388 ms en parallèle sur deux cores. Une amélioration de performance d’environ 30%, en informatique, c’est déjà pas mal… Et imaginez sur un octo-core. Les résultats varient un peu en fonction du temps, mais on a une bonne idée du ratio sur ce type d’algo.

Toutefois, un bug est présent dans ce code : on écrit les résultats dans une liste d’entiers, et le multithread peut faire que les résultats sont écrits en même temps, résultant en des écrasements, que j’ai pu constater sur un test avec un résultat de référence de 1709 entiers premiers, et 1708 seulement sur le deuxième.

Le premier réflexe est de créer plusieurs listes et de les grouper à la fin, mais comment faire quand on n’a pas la main sur le nombre de threads ? Après recherche sur internet, il se trouve que Microsoft a tout prévu. Au lieu d’utiliser des listes génériques, il faut utiliser System.Threading.BlockingCollection<T>, qui est faite spécialement pour ça…

Bref, un premier essai convaincant !

Posted in .NET, Parallélisation | Leave a comment

UI Automation : le framework

J’ai finalement fait beaucoup plus vite que je ne pensais pour créer une première version de ce que je voudrais voir devenir une grammaire XML spécialisée dans le test des interfaces graphiques, et un moteur d’exécution de ces tests basé sur UI Automation Framework.

Miboboio m’a donné la solution pour gérer du texte, j’ai trouvé comment régler le problème des fenêtres principales qui changent (typiquement quand on a une fenêtre de login qui s’efface pour laisser la place à une fenêtre principale), et le reste était relativement simple.

Bon, ce n’est vraiment qu’un prototype pour l’instant, mais j’ai quand même créé un projet sur CodePlex. Vous pouvez aller voir sur http://www.codeplex.com/uiautotestxml pour plus de détails, et tester la release 0.1. J’ai mis deux exemples de scenario XML de test. Un qui pilote la calculatrice :

<?xml version="1.0" encoding="utf-8" ?>
<scenario>
 <application path="calc.exe">
  <launchWaitTimeout>2000</launchWaitTimeout>
 </application>
 <actions>
  <setText target="403" value="123"/>
  <buttonClick target="92" waitForAction="100"/>
  <setText target="403" value="456"/>
  <buttonClick target="112" waitForAction="100"/>
  <checkText target="403" value="579, " errMsg="Mauvais calcul"/>
 </actions>
</scenario>

Un autre qui pilote une application de test que j’ai montée pour l’occasion, afin de tester la prise en compte du changement de fenêtre active :

<?xml version="1.0" encoding="utf-8" ?>
<scenario>
 <application path="UIAutoTest.TestTargetApp.exe">
  <launchWaitTimeout>2000</launchWaitTimeout>
 </application>
 <actions>
  <clearText target="txtPassword"/>
  <setText target="txtPassword" value="coucou"/>
    <linkClick target="linkLabel1" waitForAction="100"/>
  <formSwitch/>
  <checkLabel target="lblLogUser" value="User72" errMsg="Problem with the login"/>
 </actions>
</scenario>

A priori, la grammaire doit parler d’elle-même, et sinon rendez-vous sur le site pour plus de détails. Merci par avance de vos commentaires et remarques sur ce qu’il manque pour que ça puisse servir.

Posted in .NET | Tagged | Leave a comment

IKVM et FOP

SI vous avez lu mon premier post sur l’utilisation d’Apache FOP en .NET par interop IKVM, vous serez peut-être intéressés de savoir que FOP 0.95 est sorti en version finale, et que les tests d’interop sont concluants.

En tout cas, pas de problème de mise à niveau sur les quelques XSL-FO testé…

Posted in .NET | Tagged , | Leave a comment

UI Automation : on touche au but !

Miboboio a gagné le droit de me traiter d’andouille, et a eu la noblesse de ne pas le faire dans son commentaire de mon précédent post. Il a pourtant trouvé la solution à mon problème récurrent sur UIAutomation et je l’en remercie vivement !

Son approche est la suivante : puisque le TextPattern ne propose pas de SetValue, mais juste un GetValue, on va passer par le ValuePattern. J’avais essayé ce pattern ainsi que le InvokePattern, mais sans succès. Et c’est là que Miboboio a trouvé l’astuce : dans le cas où la zone de texte ne constitue pas un ValuePattern, il place le focus dessus (pas besoin de passer par un pattern pour ça, c’est appelable directement sur l’instance de AutomationElement), et ensuite fait un System.Windows.Forms.SendKeys.SendWait. Je précise bien le namespace sur le nom de classe, car il ne fait pas partie des espaces de nommages liés à l’UIAutomation.

Je me suis permis de raccourcir un peu le code de Miboboio, et de l’intégrer dans un code complet incluant le lancement du processus à contrôler, de façon que les personnes intéressées puissent simplement copier-coller dans VS.NET et lancer. Je vous laisse aller sur son commentaire pour voir l’original avec tous les commentaires. Et sinon, vous pouvez tester directement ceci :

Process notepad = Process.Start("notepad.exe");
Thread.Sleep(2000);
AutomationElement notepadForm = AutomationElement.FromHandle(notepad.MainWindowHandle);
AutomationElement zoneTexte = notepadForm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
TextPattern txtPattern = zoneTexte.GetCurrentPattern(TextPattern.Pattern) as TextPattern;
object valuePattern = null;
if (!zoneTexte.TryGetCurrentPattern(ValuePattern.Pattern, out valuePattern))
{
 zoneTexte.SetFocus();
 Thread.Sleep(500);
 SendKeys.SendWait("coucou");
}
else
{
 zoneTexte.SetFocus();
 ((ValuePattern)valuePattern).SetValue("coucou");
}

Vous aurez juste besoin de rajouter les using ci-dessous :

using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Automation; // inclure l'assembly UIAutomation
using System.Windows.Forms; // inclure l'assembly System.Windows.Forms

Moralité de l’histoire : quand la doc Microsoft dit que ce n’est pas possible de le faire pour des raisons de sécurité… trouvez une autre faille… pardon, une autre façon de faire.

Ah, juste un petit détail : n’essayez pas de faire fonctionner ce code en debug, ou en tout cas, ne passez pas par VS.NET entre le SetFocus et le SendKeys : en effet, dans ce cas-là, le focus repasse à VS.NET, au lieu du contrôle à piloter, et le SendKeys sur VS.NET pendant qu’il est en train de débugger ne se passe pas bien du tout (blocage du processus).

C’est d’ailleurs ça, la clé de l’astuce de Miboboio : quand vous faites un SetFocus sur un AutomationElement, le focus est vraiment passé à l’application piloté. L’UIAutomation ne fonctionne pas comme ManagedSpy++ avec des hooks Windows activés depuis le processus pilote : il envoie vraiment les messages Windows correspondants. Je vous encourage à voir l’article de Frédéric Queudret qui comprend un schéma très détaillé de comment ça fonctionne. J’avais d’ailleurs oublié de donner le lien dans mon post précédent. Désolé. Le voici.

Allez, un dernier lien pour la route : le forum de Microsoft dédié à l’accessibilité. Il y a pas mal de questions sur des cas précis : comment activer une combo, etc. Je pense que je vais trouver les réponses au reste de mes questions là-dedans, et j’espère dans quelques mois avoir mis en place un petit framework pour utiliser tout ça. Idéalement, une grammaire XML pour écrire des tests automatisés sur une application, avec un système de test unitaire embarqué. Bien évidemment, je vous tiendrai au courant.

En attendant, merci encore à Miboboio !

Posted in .NET, Tests | Tagged | Leave a comment

UI Automation : cent fois sur le métier remettez votre ouvrage…

L’automatisation de test me titille à nouveau…

Dans un post précédent (Microsoft UI Automation : déçu, encore une fois…), j’avais expliqué ce qui, selon moi, faisait que le framework UI Automation ne permettait pas de faire de l’automatisation de test : le manque de possibilité d’écrire dans une zone de texte. En effet, je veux bien croire que certaines applications fonctionnent uniquement avec des boutons, mais moi je bosse sur des applications de gestion, qui sont utilisées par des secrétaires et des personnes qui instruisent des dossiers, et quand ils s’en tapent une centaine pas jour, ils ont usé le clavier…

Bref, je pensais avoir tiré une croix sur le sujet, et voilatipa qu’un nouvel article sort là-dessus, dans les flux RSS qui s’affiche au démarrage de Visual Studio. L’auteur n’est autre que Frédéric Queudret, qui était également l’auteur si je me rappelle bien d’un article dans Programmez! sur le même sujet. Je me dis qu’il y a peut-être du nouveau, et surtout une réponse à mon interrogation : dans mon post précédent, je finissais en disant que je serais heureux de me faire traiter d’andouille par quelqu’un qui peut me montrer que je suis passé à côté de la plaque, et je le pense vraiment.

Me voilà donc sur l’article cité, et l’exemple n’est pas avec le notepad comme dans les tutoriels que j’avais trouvé sur le sujet : chouette, ça veut dire que c’est du nouveau. Donc peut-être avec une réponse à ce fameux problème : comment entrer du texte ?

L’application traitée est la calculatrice. On va donc vérifier un calcul. Et donc avoir à entrer des données. Youpi, on chauffe !

Et bien non : c’est là tout l’avantage de cette application : il y a des boutons sur lesquels on tape pour rentrer du texte. Vous l’avez compris : le test automatisé pour vérifier que la calculatrice donne bien trois quand on tape 1 + 3 consiste à simuler le clic sur le bouton 1, puis sur le bouton +, etc.

Voyons les choses du bon côté : ça tend à prouver tout simplement ce que j’avais conclu l’autre fois, et je vois s’éloigner les chances de me faire traiter à juste titre d’andouille. Le mauvais côté des choses est que je suis toujours bredouille.

Il paraît que la CTP de Rosario prend en charge les tests automatisés. J’espère deux choses :

  1. que Microsoft aura étendu son UI Framework, ou au pire créé des hooks sur les contrôles pour pouvoir entrer du texte
  2. que les commerciaux de Microsoft seront un jour au courant de ce qui se passe dans leur application avant de l’apprendre par des blogs de personnes extérieures à leur société : j’ai demandé plusieurs fois au commercial France sur Visual Studio si il y allait avoir quelque chose là-dessus, et il n’était au courant de rien. Même chose aux TechDays.

En attendant, je vais envoyer de ce pas un petit mail à Frédéric Queudret en lui proposant de me traiter d’andouille, à condition qu’il me donne la solution à ce problème de 10 ans ! Si des gens lisent ce post, je m’engage à vous tenir au courant de sa réponse, et de ma quête sans fin sur ce sujet…

[Edit]

Comme je reporte ce post depuis mon ancienne plateforme de blog, je reprends les commentaires à la main.

Miboboio :

private void WriteToTextbox(AutomationElement Parent, string AutomationId,string Text)
{

if (Parent != null)
{
//Find textbox
PropertyCondition idCond = new PropertyCondition(AutomationElement.AutomationIdProperty, AutomationId);
AutomationElement CurrentTextbox = Parent.FindFirst(TreeScope.Descendants, idCond);

if (CurrentTextbox != null)
{

try
{

TextPattern txtPattern = CurrentTextbox.GetCurrentPattern(TextPattern.Pattern) as TextPattern;

// Once you have an instance of an AutomationElement,
// check if it supports the ValuePattern pattern.
object valuePattern = null;

// Control does not support the ValuePattern pattern
// so use keyboard input to insert content.
//
// NOTE: Elements that support TextPattern
// do not support ValuePattern and TextPattern
// does not support setting the text of
// multi-line edit or document controls.
// For this reason, text input must be simulated
// using one of the following methods.
//
if (!CurrentTextbox.TryGetCurrentPattern(
ValuePattern.Pattern, out valuePattern))
{

// Set focus for input functionality and begin.
CurrentTextbox.SetFocus();

// Pause before sending keyboard input.
Thread.Sleep(100);

// Delete existing content in the control and insert new content.
SendKeys.SendWait(« ^{HOME} »); // Move to start of control
SendKeys.SendWait(« ^+{END} »); // Select everything
SendKeys.SendWait(« {DEL} »); // Delete selection
SendKeys.SendWait(Text);
}
// Control supports the ValuePattern pattern so we can
// use the SetValue method to insert content.
else
{

// Set focus for input functionality and begin.
CurrentTextbox.SetFocus();

((ValuePattern)valuePattern).SetValue(Text);
}

}
catch (Exception ex)
{

Assert.Fail(ex.Message);
}
}
}
else
{
Assert.Fail(« parent is null »);
}
}

Posted in .NET, Tests | Tagged | Leave a comment

Retour d’expérience sur ClickOnce

Si jamais quelqu’un de Microsoft me lit, je serais curieux de savoir si ClickOnce est considéré comme déjà enterré par Silverlight et consorts, ou si c’est toujours présenté comme une technologie à utiliser. Outre le fait que ClickOnce ne soit pas supporté officiellement (en tout cas, dixit le support téléphonique), plusieurs bugs vraiment gênants existent sur cette technologie, et ne sont visiblement pas en voie de résolution.

Il y en a au moins un qui a été corrigé, mais c’était tellement criant qu’il pouvait difficilement en être autrement : il s’agit du non-support des proxies authentifiés pour le téléchargement. Ca fonctionne grâce à un fix qu’on peut se procurer en le demandant à la hotline Microsoft (http://support.microsoft.com/default.aspx/kb/917952). Je n’ai pas bien compris pourquoi il n’avait pas été mis à disposition publique, mais bon… le problème est réglé.

Par contre, je suis récemment tombé sur un autre souci, à savoir que l’authentification du client .NET se chargeant du téléchargement des modules (donc, l’API ClickOnce de System.Deployment) n’était visiblement pas capable de gérer un saut d’adresse.

Après quelques recherches sur le net, il semblerait que le client de téléchargement se base toujours sur l’adresse effective du fichier .application, et non sur l’adresse à laquelle a été adressée la requête HTTP pour récupérer celui-ci. Ce qui pose problème en cas de pare-feu, DMZ, translation, etc. Un incident a été enregistré, mais il n’y a ni réponse ni activité depuis Juin.

Je commence à me demander si ClickOnce n’est pas en train d’être mis de côté par Microsoft, vu que les problèmes de déploiement d’un client lourd ne se posent plus suite à la mise en place de Silverlight. Pourtant, les applications WPF sont mises en avant, et on trouve de nombreux liens sur le déploiement d’applications basées sur cette techno relativement récente.

En conclusion, si vous devez utiliser ClickOnce de manière industrielle, faites des tests dans la configuration exacte de production (réseau, proxy, authentification, SSL, etc.).

Posted in Retours | Tagged | Leave a comment