Scott Hanselman

Darned Usable - Yet Another Reason that Notepad2 is the editor of choice and the magic of CR/LF vs. LF

January 5, '05 Comments [5] Posted in Musings
Sponsored By

Notepad2cool

Those in the know already love Notepad2 and have much respect for Flo's flow. But, just when I thought I couldn't dig it more, today I had to edit some CVS files that were on SourceForge. They are Unix files (meaning Line Feeds terminating each line [LF] rather than CarriageReturn/LineFeed [CR/LF]) and the FAQ said in no uncertain terms - be careful when editing these files on Windows. 

That's advice that's always good to follow. Note only should you know your CR/LF status, but also whether you have Tabs vs. Spaces, and if you are ANSI or UTF-8 or Unicode.

Anyway, Notepad2 not only shows the the LF status in the status bar and optionally at the end of each line, BUT it also lets you CHANGE THE FILES STATUS by simply double clicking on the status bar indicator.

Fabulous. I was able to edit the Unix files without fear and with the confidence that the files I saved were correct. Slick.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Mapping/Connecting a Drive Letter to a WebDAV or Front Page website

January 5, '05 Comments [4] Posted in Programming | Tools
Sponsored By

On the OT list recently the question was asked: "Does anyone know of an FPSE (Front Page Server Extensions/WebDAV) client that works on the command-line?"

The fellow basically wanted to XCOPY/COPY files from his machine to a remote site from the command line. I recommended the ridiculously powerful SynchronEX. I use it to automate the backup of my site from FTP (XFTP actually, maintaining file dates!) nightly, which is then backed up by my Iomega Rev Drive's Automatic Backup Pro Software. It's more than just a command-line tool, it's a complete scriptable beast with two-way sync and multiple remote file system support.

Later, John Schroedl responded with this built-in-to-Windows-XP tasty tip:

net use * http://www.webdav.org/ passwrd /user:username

He notes a caveat "Windows XP assumes that any WebDAV repository begins at the root of the server path. If a user mounts http://www.example.com/hr/ergonomics, then the OPTIONS request to http://www.example.com/ must show WebDAV support or the Redirector will fail."

For example:

C:\>net use * http://foo/Bar/SomeDirectory XXXXXXX /user:scott
Drive Z: is now connected to
http://foo/Bar/SomeDirectory.

The command completed successfully.

C:\>dir z:
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\

12/22/2004  05:01 PM    <DIR>          .
12/22/2004  05:01 PM    <DIR>          ..
12/09/2004  03:37 PM             2,335 File.egp
12/22/2004  05:01 PM    <DIR>          TestDir
               1 File(s)          2,335 bytes
               3 Dir(s)   2,620,660,736 bytes free

From here you can use the command shell commands (XCOPY/COPY) to work with DAV as any other drive. A great tip!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

More dasBlog 1.7 changes coming soon

January 1, '05 Comments [3] Posted in ASP.NET | DasBlog | HttpModule | Bugs | Tools
Sponsored By

Omar and I are still busting our butts on a "newtelligence dasBlog Community Edition 1.7" release in the next few weeks. There will likely be a number of changes, many of which Omar has on the dasBlog Wiki.

In the last week we've:

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Testing Code-Generated Code with NUnit, Temporary Files, Voodoo, Rubber Bands and Reflection

January 1, '05 Comments [1] Posted in ASP.NET | NUnit | XML
Sponsored By

Generatedunittestprocess2Here's a little technical content for y'all. I write this on my new iJoy as the wife watches Lifetime this New Year's Day.

We're into Unit Testing here and getting more so every day. We also do a lot of code generation as well, and struggled with how to test that the code generation works from end to end.

We annotate XSDs to describe our objects. We have an XSD "DOM" Adapter that makes XML Schema documents look more friendly to consumers. We then use CodeSmith to spin through the "DOM" and generate code. Then we need to compile the generated code and hope it's correct. It's kind of a drawn out process.

However, since these objects are so fundamental to everything else that we do, it's important to test their generation. These objects should compile, have the correct attributes, be create-able at runtime, and serialize correctly.

Here's how we run CodeSmith in the NUnit tests to generate into temporary .g.cs files. The ".g.cs" extension is our own invention, allowing us to keep track of what's generated and what's written by hand.

We store the XML Schemas that will be generated as embedded resources in the test assemblies. That makes the Unit tests self-contained. This embedding technique was described in an earlier post. We unfold the schemas into the temporary directory and create a Corillian CodeSmith CodeRunner. The CodeRunner is a wrapper that we've written around CodeSmith to allow us to more easily run CodeSmith templates and avoid shelling out to EXEs. All this is done in [TextFixtureSetup] and deleted in [TestFixtureTearDown].

[Test()]
public void WriteTestMessagesFile()
{
CodeSmithRunner.Runner runner =
new Corillian.CodeGeneration.CodeSmithRunner.Runner();
runner.LoadTemplate(templatePath);
GenerateSource(runner,outFileMessagesNamePath,schemaMessagesPath);
}

private void GenerateSource(CodeSmithRunner.Runner runner,
string outputPath, string schemaPathIn)
{
using(StreamWriter output = File.CreateText(outputPath))
{
//deal with the schema file
Corillian.CodeGeneration.XmlSchemaExplorer.Schema schema =
Corillian.CodeGeneration.XmlSchemaExplorer.Schema.Read(schemaPathIn);
if(schema == null)
throw new ApplicationException("Schema couldn't be loaded");
Corillian.CodeGeneration.XmlSchemaExplorer.SchemaType[] types = schema.Parse();
if(types == null || types.Length == 0)
throw new ApplicationException("No types loaded from schema");
Corillian.CodeGeneration.XmlSchemaExplorer.TypeCollection typeCol =
new Corillian.CodeGeneration.XmlSchemaExplorer.TypeCollection(types,schemaPathIn);
runner.SetProperty("SchemaTypeCollection",typeCol);
runner.SetProperty("TargetNamespace","Test.Types");
runner.GenerateOutput(output);
}
}

The test above loads the CodeSmith .CST template from templatePath and outputs the resulting C# into outFileMessagesNamePath from the XSD in schemaMessagesPath.

Now that we've got generated C# in a temporary file, and assuming (since no exceptions stopped the tests) it worked, we'll want to compile the generated code into an assembly.

private Assembly Compile(string scope, string[] outputPaths)
{
Assembly retVal = compiledAsssemblies[outputPaths[0]+scope] as Assembly;
if (retVal == null)
{
Microsoft.CSharp.CSharpCodeProvider prov =
new Microsoft.CSharp.CSharpCodeProvider();
ICodeCompiler comp = prov.CreateCompiler();
string[] asms = new string[]{"System.Xml.dll",
"System.Web.dll",
"Corillian.Voyager.Common.dll",
"Corillian.Voyager.ExecutionServices.Client.dll",
"Corillian.CodeGeneration.Templates.Test.dll"};
string assemblyFileName =
Path.GetFileNameWithoutExtension(outputPaths[0]) + "." + scope + ".dll";
CompilerParameters options = new CompilerParameters(asms,assemblyFileName,true);
options.GenerateInMemory = false;
CompilerResults res = comp.CompileAssemblyFromFileBatch(options,outputPaths);
if(res.Errors.HasErrors)
{
Assert.Fail(res.Errors[0].ToString());
}

retVal = res.CompiledAssembly;
compiledAsssemblies.Add(outputPaths[0]+scope,retVal);
}
Assert.IsNotNull(retVal);
return retVal;
}

This Compile method takes an array of files to compile and returns the compiled System.Reflection.Assembly. We use the CSharpCodeProvider to compile the code. This is much cleaner and makes better use of what's available to us than doing something so coarse as shelling out to run csc.exe and loading the assembly. It also allows us to better detect compile errors. Notice the Assert.File if the CompilerResults has errors. We also keep the assembly stored away in a hashtable just in case another test calls Compile with the same main generated file.

Now that we can generate code and compile generated code into an Assembly, we need to actually create/instantiate the newly generated object and confirm that it has the characteristics we expect.

[Test()]
public void CheckDerivationAcrossNamespaces()
{
Assembly assembly = Compile("UI",
new string[]{outFileMessagesNamePath,outFileUIOnlyPath,assemblyInfo});
Type testResponse = assembly.GetType("Test.Types.SomeTestResponseMessage",true,true);
object theTestMessage =
testResponse.InvokeMember("cctor",BindingFlags.CreateInstance,null,null,null);
Assert.IsNotNull(theTestMessage);
PropertyInfo[] props = testResponse.GetProperties();
bool SomeDerivedUser = false;
bool FooTypedUser = false;
foreach(PropertyInfo prop in props)
{
if (prop.PropertyType.Name == "SomeDerivedUser" && prop.Name == "SomeDerivedUser")
{
Assert.AreEqual("SomeBaseUser",prop.PropertyType.BaseType.Name);
object[] attrs = prop.GetCustomAttributes(typeof(TagRemapAttribute),false);
Assert.IsTrue(attrs.Length == 2);
SomeDerivedUser = true;
}
if (prop.PropertyType.Name == "SomeDerivedComplexUser" && prop.Name == "FooTypedUser")
{
Assert.AreEqual("SomeBaseUser",prop.PropertyType.BaseType.Name);
object[] attrs = prop.GetCustomAttributes(typeof(TagRemapAttribute),false);
Assert.IsTrue(attrs.Length == 2);
FooTypedUser = true;
}
}
Assert.IsTrue(SomeDerivedUser ,"Didn't find a ID object of type
SomeDerivedUser in the UI's SomeTestResponseMessage!");
Assert.IsTrue(FooTypedUser,"Didn't find a ID object of type
SomeDerivedComplexUser in the UI's SomeTestResponseMessage!");

}

Here we compile an assembly from generated code and call assembly.GetType() to get the particular Type we're interested in. Now it gets interesting. We call the constructor of our type with Type.InvokeMember("cctor") and get the instance object back.

CodegenerationtestoutputIt's important to note that we have NOW compile-time references to any of these objects. They never existed at the time we wrote the tests, so there's no casting or type coercion that can be done. Everything is dynamic, but the tests are still confirming that the expect steps occured successfully. The tests are aiming to test the lifecycle of our objects, even though the objects will not be instantiated using Reflection. It's a parallel, but valid, reality.

In this year, we need to determine that our SomeTestResponseMessage contains two properties of specific types, with specific names, with specific custom attributes and we Assert each step of the way. If somehow make a subtle change that affects this use case, we'll hear about it via Test Failures.

The test results for this suite are generated by NUnit into XML then styled into HTML and combined with the full Test Suite and emailed to the team. Here's the output for just this TextFixture shown at right.

The only thing in these examples that is specific to what Corillian is our use of CodeSmith. Certainly tests could be written for other Code Generators that produce domain objects. I was particularly happy that CodeSmith provided a programmatic interface to it's stuff that allowed Patrick to create the CodeSmithRunner. That combined with the Microsoft CSharpCodeProvider makes these tests very robust (read: not fragile) and self contained. I've said it before, but I'd much rather use the correct public types than shell out whenever possible. You might remember I applied similar prejudices when calling Cassini's object model rather than running the command line.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Cannon Beach, Oregon - Panoramic Digital Photography

January 1, '05 Comments [3] Posted in Musings
Sponsored By

Mo and I went to Cannon Beach over the Christmas Holiday. I took a few pics with my Canon Exilim EX-Z3 of the Beach near Mo's Chowder. We did take some pictures of Mo at Mo's. :)

Cannonbeach2004largepanorama

This photo was stitched together with 10 pictures using the truly unparalleled Panorama Factory. I think I need to buy this software, it's amazing.

Update: Floyd/Ralf suggested I try ArcSoft's Panorama Maker as well. Wow, I'd never heard of this company, but they have a crapload of software. I did the panorama again, and it came out slightly brighter and clearer. I prefer the spartan interface of Panorama Factory, but now I'll need to do some additional tests. It appears that the advanced features and  spherical correction abilties of Panorama Factory are superior, but Panorama Maker looks to be easier for Joe-User.

Cannonbeach2004largepanorama2

Panorama Maker's simple Flash export feature is pretty amazing also...

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.